nikcli 0.0.6

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 (602) hide show
  1. package/.turbo/turbo-typecheck.log +1 -0
  2. package/AGENTS.md +27 -0
  3. package/Dockerfile +18 -0
  4. package/README.md +15 -0
  5. package/bin/nikcli +84 -0
  6. package/config.json +13 -0
  7. package/docs/tailscale-mobile/01-tailscale-setup.md +94 -0
  8. package/docs/tailscale-mobile/02-host-setup.md +115 -0
  9. package/docs/tailscale-mobile/03-phone-and-serve.md +134 -0
  10. package/docs/tailscale-mobile/README.md +59 -0
  11. package/examples/README.md +54 -0
  12. package/package.json +147 -0
  13. package/parsers-config.ts +253 -0
  14. package/script/build.ts +179 -0
  15. package/script/postinstall.mjs +125 -0
  16. package/script/publish-registries.ts +187 -0
  17. package/script/publish.ts +100 -0
  18. package/script/schema.ts +47 -0
  19. package/script/seed-e2e.ts +50 -0
  20. package/sequential-prancing-forest.md +373 -0
  21. package/src/acp/README.md +164 -0
  22. package/src/acp/agent.ts +1303 -0
  23. package/src/acp/session.ts +105 -0
  24. package/src/acp/types.ts +22 -0
  25. package/src/agent/agent.ts +528 -0
  26. package/src/agent/generate.txt +32 -0
  27. package/src/agent/prompt/compaction.txt +14 -0
  28. package/src/agent/prompt/explore.txt +18 -0
  29. package/src/agent/prompt/summary.txt +11 -0
  30. package/src/agent/prompt/title.txt +44 -0
  31. package/src/auth/index.ts +73 -0
  32. package/src/bun/index.ts +119 -0
  33. package/src/bun/registry.ts +54 -0
  34. package/src/bus/bus-event.ts +43 -0
  35. package/src/bus/global.ts +10 -0
  36. package/src/bus/index.ts +105 -0
  37. package/src/chatbot/handlers.ts +150 -0
  38. package/src/chatbot/index.ts +132 -0
  39. package/src/cli/bootstrap.ts +17 -0
  40. package/src/cli/cmd/acp.ts +69 -0
  41. package/src/cli/cmd/ads.ts +377 -0
  42. package/src/cli/cmd/agent.ts +259 -0
  43. package/src/cli/cmd/auth.ts +400 -0
  44. package/src/cli/cmd/chatbot.ts +420 -0
  45. package/src/cli/cmd/cmd.ts +7 -0
  46. package/src/cli/cmd/companion.ts +81 -0
  47. package/src/cli/cmd/connectors.ts +593 -0
  48. package/src/cli/cmd/debug/agent.ts +166 -0
  49. package/src/cli/cmd/debug/config.ts +16 -0
  50. package/src/cli/cmd/debug/file.ts +97 -0
  51. package/src/cli/cmd/debug/index.ts +48 -0
  52. package/src/cli/cmd/debug/lsp.ts +52 -0
  53. package/src/cli/cmd/debug/ripgrep.ts +87 -0
  54. package/src/cli/cmd/debug/scrap.ts +16 -0
  55. package/src/cli/cmd/debug/skill.ts +16 -0
  56. package/src/cli/cmd/debug/snapshot.ts +52 -0
  57. package/src/cli/cmd/export.ts +88 -0
  58. package/src/cli/cmd/generate.ts +38 -0
  59. package/src/cli/cmd/github.ts +412 -0
  60. package/src/cli/cmd/image-model.ts +128 -0
  61. package/src/cli/cmd/import.ts +201 -0
  62. package/src/cli/cmd/lovable.ts +128 -0
  63. package/src/cli/cmd/mcp.ts +738 -0
  64. package/src/cli/cmd/mobile.ts +223 -0
  65. package/src/cli/cmd/models.ts +77 -0
  66. package/src/cli/cmd/plug.ts +231 -0
  67. package/src/cli/cmd/pr.ts +104 -0
  68. package/src/cli/cmd/rag-model.ts +167 -0
  69. package/src/cli/cmd/remote.ts +416 -0
  70. package/src/cli/cmd/run.ts +589 -0
  71. package/src/cli/cmd/serve.ts +51 -0
  72. package/src/cli/cmd/session.ts +133 -0
  73. package/src/cli/cmd/speak-model.ts +204 -0
  74. package/src/cli/cmd/stats.ts +402 -0
  75. package/src/cli/cmd/tui/app.tsx +841 -0
  76. package/src/cli/cmd/tui/attach.ts +31 -0
  77. package/src/cli/cmd/tui/component/border.tsx +75 -0
  78. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  79. package/src/cli/cmd/tui/component/dialog-command.tsx +172 -0
  80. package/src/cli/cmd/tui/component/dialog-config.tsx +291 -0
  81. package/src/cli/cmd/tui/component/dialog-connectors.tsx +440 -0
  82. package/src/cli/cmd/tui/component/dialog-image-model.tsx +97 -0
  83. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  84. package/src/cli/cmd/tui/component/dialog-model.tsx +234 -0
  85. package/src/cli/cmd/tui/component/dialog-provider.tsx +260 -0
  86. package/src/cli/cmd/tui/component/dialog-rag-model.tsx +217 -0
  87. package/src/cli/cmd/tui/component/dialog-remote.tsx +489 -0
  88. package/src/cli/cmd/tui/component/dialog-session-list.tsx +170 -0
  89. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  90. package/src/cli/cmd/tui/component/dialog-settings/index.tsx +59 -0
  91. package/src/cli/cmd/tui/component/dialog-settings/prompt.tsx +40 -0
  92. package/src/cli/cmd/tui/component/dialog-settings/sidebar.tsx +39 -0
  93. package/src/cli/cmd/tui/component/dialog-settings/spinner.tsx +62 -0
  94. package/src/cli/cmd/tui/component/dialog-settings/ui.tsx +58 -0
  95. package/src/cli/cmd/tui/component/dialog-skills.tsx +117 -0
  96. package/src/cli/cmd/tui/component/dialog-speak-model.tsx +304 -0
  97. package/src/cli/cmd/tui/component/dialog-stash.tsx +87 -0
  98. package/src/cli/cmd/tui/component/dialog-status.tsx +165 -0
  99. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  100. package/src/cli/cmd/tui/component/dialog-theme-create.tsx +717 -0
  101. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +52 -0
  102. package/src/cli/cmd/tui/component/dialog-workspace-list.tsx +350 -0
  103. package/src/cli/cmd/tui/component/error-component.tsx +91 -0
  104. package/src/cli/cmd/tui/component/logo.tsx +103 -0
  105. package/src/cli/cmd/tui/component/plugin-route-missing.tsx +14 -0
  106. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +669 -0
  107. package/src/cli/cmd/tui/component/prompt/frecency.tsx +89 -0
  108. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  109. package/src/cli/cmd/tui/component/prompt/index.tsx +2165 -0
  110. package/src/cli/cmd/tui/component/prompt/stash.tsx +63 -0
  111. package/src/cli/cmd/tui/component/spinner.tsx +24 -0
  112. package/src/cli/cmd/tui/component/startup-loading.tsx +63 -0
  113. package/src/cli/cmd/tui/component/table/markdown-table.tsx +267 -0
  114. package/src/cli/cmd/tui/component/table-db/db/connections.ts +75 -0
  115. package/src/cli/cmd/tui/component/table-db/db/db-connection.ts +223 -0
  116. package/src/cli/cmd/tui/component/table-db/db/db-preview.ts +202 -0
  117. package/src/cli/cmd/tui/component/table-db/db/factory.ts +77 -0
  118. package/src/cli/cmd/tui/component/table-db/db/index.ts +9 -0
  119. package/src/cli/cmd/tui/component/table-db/db/mysql-connection.ts +330 -0
  120. package/src/cli/cmd/tui/component/table-db/db/postgres-connection.ts +338 -0
  121. package/src/cli/cmd/tui/component/table-db/db/sqlite-connection.ts +302 -0
  122. package/src/cli/cmd/tui/component/table-db/db/types.ts +108 -0
  123. package/src/cli/cmd/tui/component/table-db/table/dbedit-hooks.ts +74 -0
  124. package/src/cli/cmd/tui/component/table-db/table/index.ts +15 -0
  125. package/src/cli/cmd/tui/component/table-db/table/table-events.ts +54 -0
  126. package/src/cli/cmd/tui/component/table-db/table/table-formatters.ts +191 -0
  127. package/src/cli/cmd/tui/component/table-db/table/table-hooks.ts +105 -0
  128. package/src/cli/cmd/tui/component/table-db/table/table-keyboard-handler.ts +255 -0
  129. package/src/cli/cmd/tui/component/table-db/table/table-layout-engine.ts +208 -0
  130. package/src/cli/cmd/tui/component/table-db/table/table-renderable.ts +486 -0
  131. package/src/cli/cmd/tui/component/table-db/table/table-selection-manager.ts +136 -0
  132. package/src/cli/cmd/tui/component/table-db/table/table-state.ts +198 -0
  133. package/src/cli/cmd/tui/component/table-db/table/types.ts +69 -0
  134. package/src/cli/cmd/tui/component/table-db/ui/db-visualizer.tsx +71 -0
  135. package/src/cli/cmd/tui/component/table-db/ui/index.ts +2 -0
  136. package/src/cli/cmd/tui/component/table-db/ui/table-renderer.ts +607 -0
  137. package/src/cli/cmd/tui/component/textarea-keybindings.ts +73 -0
  138. package/src/cli/cmd/tui/component/tips.tsx +195 -0
  139. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  140. package/src/cli/cmd/tui/context/args.tsx +14 -0
  141. package/src/cli/cmd/tui/context/directory.ts +13 -0
  142. package/src/cli/cmd/tui/context/exit.tsx +24 -0
  143. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  144. package/src/cli/cmd/tui/context/keybind.tsx +102 -0
  145. package/src/cli/cmd/tui/context/kv.tsx +52 -0
  146. package/src/cli/cmd/tui/context/local.tsx +458 -0
  147. package/src/cli/cmd/tui/context/plugin-keybinds.ts +41 -0
  148. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  149. package/src/cli/cmd/tui/context/route.tsx +54 -0
  150. package/src/cli/cmd/tui/context/sdk.tsx +128 -0
  151. package/src/cli/cmd/tui/context/server.tsx +8 -0
  152. package/src/cli/cmd/tui/context/sync.tsx +510 -0
  153. package/src/cli/cmd/tui/context/theme/abyss.json +233 -0
  154. package/src/cli/cmd/tui/context/theme/apple.json +235 -0
  155. package/src/cli/cmd/tui/context/theme/arctic.json +232 -0
  156. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  157. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  158. package/src/cli/cmd/tui/context/theme/ayuai.json +229 -0
  159. package/src/cli/cmd/tui/context/theme/blood.json +229 -0
  160. package/src/cli/cmd/tui/context/theme/carbonfox.json +248 -0
  161. package/src/cli/cmd/tui/context/theme/catmoe.json +235 -0
  162. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  163. package/src/cli/cmd/tui/context/theme/catppuccin-latte.json +233 -0
  164. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  165. package/src/cli/cmd/tui/context/theme/catppuccin.json +259 -0
  166. package/src/cli/cmd/tui/context/theme/charcoal.json +230 -0
  167. package/src/cli/cmd/tui/context/theme/chromatic.json +235 -0
  168. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  169. package/src/cli/cmd/tui/context/theme/cosmic.json +234 -0
  170. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  171. package/src/cli/cmd/tui/context/theme/cyber.json +235 -0
  172. package/src/cli/cmd/tui/context/theme/dawnfox.json +229 -0
  173. package/src/cli/cmd/tui/context/theme/dimension.json +235 -0
  174. package/src/cli/cmd/tui/context/theme/dracula-official.json +222 -0
  175. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  176. package/src/cli/cmd/tui/context/theme/dream.json +235 -0
  177. package/src/cli/cmd/tui/context/theme/duo.json +235 -0
  178. package/src/cli/cmd/tui/context/theme/dusk.json +235 -0
  179. package/src/cli/cmd/tui/context/theme/ebony.json +232 -0
  180. package/src/cli/cmd/tui/context/theme/equilibrium.json +232 -0
  181. package/src/cli/cmd/tui/context/theme/ethereal.json +235 -0
  182. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  183. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  184. package/src/cli/cmd/tui/context/theme/fusion.json +235 -0
  185. package/src/cli/cmd/tui/context/theme/ghost.json +235 -0
  186. package/src/cli/cmd/tui/context/theme/github-dark.json +229 -0
  187. package/src/cli/cmd/tui/context/theme/github-dimmed.json +231 -0
  188. package/src/cli/cmd/tui/context/theme/github-light.json +229 -0
  189. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  190. package/src/cli/cmd/tui/context/theme/glass.json +235 -0
  191. package/src/cli/cmd/tui/context/theme/gold.json +235 -0
  192. package/src/cli/cmd/tui/context/theme/gone.json +234 -0
  193. package/src/cli/cmd/tui/context/theme/greyscale.json +229 -0
  194. package/src/cli/cmd/tui/context/theme/gruvbox.json +242 -0
  195. package/src/cli/cmd/tui/context/theme/hacker.json +229 -0
  196. package/src/cli/cmd/tui/context/theme/holo.json +235 -0
  197. package/src/cli/cmd/tui/context/theme/ink.json +235 -0
  198. package/src/cli/cmd/tui/context/theme/jet.json +233 -0
  199. package/src/cli/cmd/tui/context/theme/kanagawa.json +227 -0
  200. package/src/cli/cmd/tui/context/theme/lavender.json +236 -0
  201. package/src/cli/cmd/tui/context/theme/lightph.json +235 -0
  202. package/src/cli/cmd/tui/context/theme/lucent-orng.json +237 -0
  203. package/src/cli/cmd/tui/context/theme/material-ocean.json +230 -0
  204. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  205. package/src/cli/cmd/tui/context/theme/matrix.json +227 -0
  206. package/src/cli/cmd/tui/context/theme/mercury.json +245 -0
  207. package/src/cli/cmd/tui/context/theme/midnight.json +235 -0
  208. package/src/cli/cmd/tui/context/theme/modern.json +235 -0
  209. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  210. package/src/cli/cmd/tui/context/theme/muted.json +229 -0
  211. package/src/cli/cmd/tui/context/theme/neon.json +229 -0
  212. package/src/cli/cmd/tui/context/theme/neonfusion.json +235 -0
  213. package/src/cli/cmd/tui/context/theme/neutral.json +235 -0
  214. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  215. package/src/cli/cmd/tui/context/theme/nikcli.json +245 -0
  216. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  217. package/src/cli/cmd/tui/context/theme/nordic.json +235 -0
  218. package/src/cli/cmd/tui/context/theme/nova.json +235 -0
  219. package/src/cli/cmd/tui/context/theme/obsidian.json +234 -0
  220. package/src/cli/cmd/tui/context/theme/one-dark.json +231 -0
  221. package/src/cli/cmd/tui/context/theme/one-pro.json +229 -0
  222. package/src/cli/cmd/tui/context/theme/onyx.json +233 -0
  223. package/src/cli/cmd/tui/context/theme/orng.json +249 -0
  224. package/src/cli/cmd/tui/context/theme/osaka-jade.json +240 -0
  225. package/src/cli/cmd/tui/context/theme/oxocarbon.json +229 -0
  226. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  227. package/src/cli/cmd/tui/context/theme/poimandres.json +230 -0
  228. package/src/cli/cmd/tui/context/theme/prism.json +235 -0
  229. package/src/cli/cmd/tui/context/theme/radiant.json +235 -0
  230. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  231. package/src/cli/cmd/tui/context/theme/shadow.json +235 -0
  232. package/src/cli/cmd/tui/context/theme/silicon.json +235 -0
  233. package/src/cli/cmd/tui/context/theme/slate.json +233 -0
  234. package/src/cli/cmd/tui/context/theme/soft.json +235 -0
  235. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  236. package/src/cli/cmd/tui/context/theme/spectrum.json +235 -0
  237. package/src/cli/cmd/tui/context/theme/starlight.json +233 -0
  238. package/src/cli/cmd/tui/context/theme/sunrise.json +235 -0
  239. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  240. package/src/cli/cmd/tui/context/theme/tech.json +235 -0
  241. package/src/cli/cmd/tui/context/theme/tokyonight-storm.json +245 -0
  242. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  243. package/src/cli/cmd/tui/context/theme/vapor.json +235 -0
  244. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  245. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  246. package/src/cli/cmd/tui/context/theme/vivid.json +232 -0
  247. package/src/cli/cmd/tui/context/theme/void.json +235 -0
  248. package/src/cli/cmd/tui/context/theme/vscode.json +235 -0
  249. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  250. package/src/cli/cmd/tui/context/theme/zinc.json +236 -0
  251. package/src/cli/cmd/tui/context/theme.tsx +1303 -0
  252. package/src/cli/cmd/tui/event.ts +48 -0
  253. package/src/cli/cmd/tui/feature-plugins/home/tips-view.tsx +152 -0
  254. package/src/cli/cmd/tui/feature-plugins/home/tips.tsx +50 -0
  255. package/src/cli/cmd/tui/feature-plugins/sidebar/context.tsx +63 -0
  256. package/src/cli/cmd/tui/feature-plugins/sidebar/files.tsx +62 -0
  257. package/src/cli/cmd/tui/feature-plugins/sidebar/footer.tsx +93 -0
  258. package/src/cli/cmd/tui/feature-plugins/sidebar/lsp.tsx +66 -0
  259. package/src/cli/cmd/tui/feature-plugins/sidebar/mcp.tsx +96 -0
  260. package/src/cli/cmd/tui/feature-plugins/sidebar/todo.tsx +48 -0
  261. package/src/cli/cmd/tui/feature-plugins/system/plugins.tsx +288 -0
  262. package/src/cli/cmd/tui/plugin/api.tsx +407 -0
  263. package/src/cli/cmd/tui/plugin/index.ts +3 -0
  264. package/src/cli/cmd/tui/plugin/internal.ts +25 -0
  265. package/src/cli/cmd/tui/plugin/runtime.ts +1048 -0
  266. package/src/cli/cmd/tui/plugin/slots.tsx +61 -0
  267. package/src/cli/cmd/tui/routes/home.tsx +153 -0
  268. package/src/cli/cmd/tui/routes/session/dbedit.tsx +474 -0
  269. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +65 -0
  270. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +110 -0
  271. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +105 -0
  272. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  273. package/src/cli/cmd/tui/routes/session/footer.tsx +75 -0
  274. package/src/cli/cmd/tui/routes/session/header.tsx +177 -0
  275. package/src/cli/cmd/tui/routes/session/index.tsx +2280 -0
  276. package/src/cli/cmd/tui/routes/session/permission.tsx +540 -0
  277. package/src/cli/cmd/tui/routes/session/question.tsx +435 -0
  278. package/src/cli/cmd/tui/routes/session/sidebar.tsx +313 -0
  279. package/src/cli/cmd/tui/thread.ts +174 -0
  280. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  281. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  282. package/src/cli/cmd/tui/ui/dialog-export-options.tsx +204 -0
  283. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  284. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +102 -0
  285. package/src/cli/cmd/tui/ui/dialog-select.tsx +389 -0
  286. package/src/cli/cmd/tui/ui/dialog.tsx +180 -0
  287. package/src/cli/cmd/tui/ui/link.tsx +34 -0
  288. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  289. package/src/cli/cmd/tui/ui/toast.tsx +138 -0
  290. package/src/cli/cmd/tui/util/clipboard.ts +154 -0
  291. package/src/cli/cmd/tui/util/editor.ts +32 -0
  292. package/src/cli/cmd/tui/util/signal.ts +7 -0
  293. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  294. package/src/cli/cmd/tui/util/transcript.ts +98 -0
  295. package/src/cli/cmd/tui/win32.ts +110 -0
  296. package/src/cli/cmd/tui/worker.ts +156 -0
  297. package/src/cli/cmd/uninstall.ts +357 -0
  298. package/src/cli/cmd/upgrade.ts +72 -0
  299. package/src/cli/cmd/web.ts +87 -0
  300. package/src/cli/cmd/workspace-serve.ts +16 -0
  301. package/src/cli/error.ts +57 -0
  302. package/src/cli/network.ts +55 -0
  303. package/src/cli/remote/index.ts +36 -0
  304. package/src/cli/remote/notifications.ts +104 -0
  305. package/src/cli/remote/qr-renderer.ts +86 -0
  306. package/src/cli/remote/remote-service.ts +757 -0
  307. package/src/cli/remote/session-manager.ts +284 -0
  308. package/src/cli/remote/subagent-hooks.ts +151 -0
  309. package/src/cli/remote/types.ts +121 -0
  310. package/src/cli/ui.ts +96 -0
  311. package/src/cli/upgrade.ts +25 -0
  312. package/src/command/index.ts +174 -0
  313. package/src/command/template/initialize.txt +10 -0
  314. package/src/command/template/review.txt +99 -0
  315. package/src/config/config.ts +1760 -0
  316. package/src/config/markdown.ts +88 -0
  317. package/src/config/migrate-tui-config.ts +155 -0
  318. package/src/config/paths.ts +174 -0
  319. package/src/config/tui-schema.ts +36 -0
  320. package/src/config/tui.ts +209 -0
  321. package/src/connectors/api/base.ts +75 -0
  322. package/src/connectors/api/figma.ts +103 -0
  323. package/src/connectors/api/github.ts +247 -0
  324. package/src/connectors/api/lovable.ts +126 -0
  325. package/src/connectors/api/slack.ts +137 -0
  326. package/src/connectors/auth.ts +68 -0
  327. package/src/connectors/cache.ts +119 -0
  328. package/src/connectors/credentials.ts +81 -0
  329. package/src/connectors/index.ts +202 -0
  330. package/src/connectors/registry.ts +358 -0
  331. package/src/docs/context.ts +120 -0
  332. package/src/docs/library.ts +189 -0
  333. package/src/env/index.ts +26 -0
  334. package/src/file/ignore.ts +83 -0
  335. package/src/file/index.ts +411 -0
  336. package/src/file/ripgrep.ts +402 -0
  337. package/src/file/time.ts +65 -0
  338. package/src/file/watcher.ts +127 -0
  339. package/src/flag/flag.ts +128 -0
  340. package/src/format/formatter.ts +356 -0
  341. package/src/format/index.ts +137 -0
  342. package/src/global/index.ts +57 -0
  343. package/src/id/id.ts +83 -0
  344. package/src/ide/index.ts +76 -0
  345. package/src/index.ts +184 -0
  346. package/src/installation/index.ts +246 -0
  347. package/src/lsp/client.ts +250 -0
  348. package/src/lsp/index.ts +483 -0
  349. package/src/lsp/language.ts +119 -0
  350. package/src/lsp/server.ts +2046 -0
  351. package/src/mcp/auth.ts +121 -0
  352. package/src/mcp/index.ts +860 -0
  353. package/src/mcp/oauth-callback.ts +198 -0
  354. package/src/mcp/oauth-provider.ts +148 -0
  355. package/src/mobile/auth.ts +97 -0
  356. package/src/mobile/github-repo.ts +185 -0
  357. package/src/patch/index.ts +631 -0
  358. package/src/permission/arity.ts +150 -0
  359. package/src/permission/dbedit.ts +236 -0
  360. package/src/permission/index.ts +210 -0
  361. package/src/permission/next.ts +287 -0
  362. package/src/plugin/codex.ts +493 -0
  363. package/src/plugin/copilot.ts +261 -0
  364. package/src/plugin/index.ts +714 -0
  365. package/src/plugin/install.ts +379 -0
  366. package/src/plugin/meta.ts +165 -0
  367. package/src/plugin/shared.ts +188 -0
  368. package/src/project/bootstrap.ts +35 -0
  369. package/src/project/instance.ts +84 -0
  370. package/src/project/project.ts +373 -0
  371. package/src/project/state.ts +66 -0
  372. package/src/project/vcs.ts +76 -0
  373. package/src/prompt/stash-store.ts +93 -0
  374. package/src/provider/auth.ts +147 -0
  375. package/src/provider/models-macro.ts +22 -0
  376. package/src/provider/models.ts +216 -0
  377. package/src/provider/provider.ts +1483 -0
  378. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  379. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  380. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  381. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  382. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  383. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  384. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  385. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  386. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1732 -0
  387. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  388. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  389. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  390. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  391. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  392. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  393. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  394. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  395. package/src/provider/transform.ts +828 -0
  396. package/src/pty/index.ts +241 -0
  397. package/src/question/index.ts +171 -0
  398. package/src/rag/chunk.ts +43 -0
  399. package/src/rag/embed.ts +179 -0
  400. package/src/rag/index.ts +376 -0
  401. package/src/rag/storage.ts +76 -0
  402. package/src/scheduler/index.ts +61 -0
  403. package/src/server/error.ts +36 -0
  404. package/src/server/event.ts +7 -0
  405. package/src/server/mdns.ts +59 -0
  406. package/src/server/routes/chatbot.ts +205 -0
  407. package/src/server/routes/companion.ts +729 -0
  408. package/src/server/routes/config.ts +92 -0
  409. package/src/server/routes/connectors.ts +121 -0
  410. package/src/server/routes/dbedit.ts +76 -0
  411. package/src/server/routes/experimental.ts +210 -0
  412. package/src/server/routes/file.ts +197 -0
  413. package/src/server/routes/global.ts +135 -0
  414. package/src/server/routes/mcp.ts +225 -0
  415. package/src/server/routes/mobile.ts +2044 -0
  416. package/src/server/routes/permission.ts +68 -0
  417. package/src/server/routes/project.ts +82 -0
  418. package/src/server/routes/provider.ts +235 -0
  419. package/src/server/routes/pty.ts +169 -0
  420. package/src/server/routes/question.ts +98 -0
  421. package/src/server/routes/session.ts +968 -0
  422. package/src/server/routes/tui.ts +379 -0
  423. package/src/server/routes/workspace.ts +104 -0
  424. package/src/server/server.ts +761 -0
  425. package/src/server/ssh.ts +207 -0
  426. package/src/session/auth.ts +402 -0
  427. package/src/session/compaction.ts +253 -0
  428. package/src/session/generate.ts +38 -0
  429. package/src/session/index.ts +598 -0
  430. package/src/session/llm.ts +273 -0
  431. package/src/session/message-v2.ts +836 -0
  432. package/src/session/message.ts +189 -0
  433. package/src/session/processor.ts +408 -0
  434. package/src/session/prompt/anthropic-20250930.txt +165 -0
  435. package/src/session/prompt/anthropic.txt +105 -0
  436. package/src/session/prompt/anthropic_spoof.txt +1 -0
  437. package/src/session/prompt/beast.txt +147 -0
  438. package/src/session/prompt/build-switch.txt +5 -0
  439. package/src/session/prompt/codex_header.txt +79 -0
  440. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  441. package/src/session/prompt/gemini.txt +155 -0
  442. package/src/session/prompt/max-steps.txt +16 -0
  443. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  444. package/src/session/prompt/plan.txt +25 -0
  445. package/src/session/prompt/qwen.txt +108 -0
  446. package/src/session/prompt.ts +1942 -0
  447. package/src/session/retry.ts +90 -0
  448. package/src/session/revert.ts +120 -0
  449. package/src/session/stats.ts +404 -0
  450. package/src/session/status.ts +84 -0
  451. package/src/session/summary.ts +184 -0
  452. package/src/session/system.ts +195 -0
  453. package/src/session/toast.tsx +105 -0
  454. package/src/session/todo.ts +258 -0
  455. package/src/session/uninstall.ts +357 -0
  456. package/src/share/share-next.ts +421 -0
  457. package/src/share/share.ts +92 -0
  458. package/src/shell/shell.ts +65 -0
  459. package/src/skill/index.ts +1 -0
  460. package/src/skill/skill.ts +232 -0
  461. package/src/snapshot/index.ts +297 -0
  462. package/src/storage/storage.ts +227 -0
  463. package/src/tool/apply_patch.ts +288 -0
  464. package/src/tool/apply_patch.txt +33 -0
  465. package/src/tool/bash.ts +252 -0
  466. package/src/tool/bash.txt +115 -0
  467. package/src/tool/batch.ts +175 -0
  468. package/src/tool/batch.txt +24 -0
  469. package/src/tool/codesearch.ts +132 -0
  470. package/src/tool/codesearch.txt +12 -0
  471. package/src/tool/context_collect.ts +152 -0
  472. package/src/tool/context_collect.txt +9 -0
  473. package/src/tool/context_diagnostics.ts +81 -0
  474. package/src/tool/context_diagnostics.txt +5 -0
  475. package/src/tool/context_related.ts +117 -0
  476. package/src/tool/context_related.txt +5 -0
  477. package/src/tool/context_search.ts +108 -0
  478. package/src/tool/context_search.txt +8 -0
  479. package/src/tool/db-diff.ts +434 -0
  480. package/src/tool/db-table.txt +15 -0
  481. package/src/tool/docs_add.ts +50 -0
  482. package/src/tool/docs_add.txt +5 -0
  483. package/src/tool/docs_context.ts +56 -0
  484. package/src/tool/docs_context.txt +4 -0
  485. package/src/tool/docs_gap_report.ts +79 -0
  486. package/src/tool/docs_gap_report.txt +7 -0
  487. package/src/tool/docs_load.ts +41 -0
  488. package/src/tool/docs_load.txt +4 -0
  489. package/src/tool/docs_request.ts +129 -0
  490. package/src/tool/docs_request.txt +7 -0
  491. package/src/tool/docs_search.ts +51 -0
  492. package/src/tool/docs_search.txt +6 -0
  493. package/src/tool/docs_unload.ts +38 -0
  494. package/src/tool/docs_unload.txt +5 -0
  495. package/src/tool/edit.ts +614 -0
  496. package/src/tool/edit.txt +10 -0
  497. package/src/tool/external-directory.ts +32 -0
  498. package/src/tool/generate_image.ts +174 -0
  499. package/src/tool/generate_image.txt +12 -0
  500. package/src/tool/glob.ts +79 -0
  501. package/src/tool/glob.txt +6 -0
  502. package/src/tool/grep.ts +153 -0
  503. package/src/tool/grep.txt +8 -0
  504. package/src/tool/invalid.ts +17 -0
  505. package/src/tool/ls.ts +116 -0
  506. package/src/tool/ls.txt +1 -0
  507. package/src/tool/lsp.ts +96 -0
  508. package/src/tool/lsp.txt +19 -0
  509. package/src/tool/memory_search.ts +141 -0
  510. package/src/tool/memory_search.txt +8 -0
  511. package/src/tool/multiedit.ts +46 -0
  512. package/src/tool/multiedit.txt +41 -0
  513. package/src/tool/plan-enter.txt +14 -0
  514. package/src/tool/plan-exit.txt +13 -0
  515. package/src/tool/plan.ts +130 -0
  516. package/src/tool/question.ts +33 -0
  517. package/src/tool/question.txt +10 -0
  518. package/src/tool/rag_index.ts +77 -0
  519. package/src/tool/rag_index.txt +10 -0
  520. package/src/tool/rag_reset.ts +26 -0
  521. package/src/tool/rag_reset.txt +4 -0
  522. package/src/tool/rag_search.ts +62 -0
  523. package/src/tool/rag_search.txt +6 -0
  524. package/src/tool/rag_status.ts +45 -0
  525. package/src/tool/rag_status.txt +4 -0
  526. package/src/tool/read.ts +203 -0
  527. package/src/tool/read.txt +12 -0
  528. package/src/tool/registry.ts +214 -0
  529. package/src/tool/skill.ts +169 -0
  530. package/src/tool/skill.txt +3 -0
  531. package/src/tool/smart_docs.ts +74 -0
  532. package/src/tool/smart_docs.txt +7 -0
  533. package/src/tool/speak/elevenlabs.ts +201 -0
  534. package/src/tool/speak/openrouter.ts +240 -0
  535. package/src/tool/speak/provider.ts +83 -0
  536. package/src/tool/speak.ts +440 -0
  537. package/src/tool/task.ts +194 -0
  538. package/src/tool/task.txt +60 -0
  539. package/src/tool/todo.ts +53 -0
  540. package/src/tool/todoread.txt +14 -0
  541. package/src/tool/todowrite.txt +167 -0
  542. package/src/tool/tool.ts +87 -0
  543. package/src/tool/tree.ts +218 -0
  544. package/src/tool/tree.txt +8 -0
  545. package/src/tool/truncation.ts +106 -0
  546. package/src/tool/use-connector.ts +47 -0
  547. package/src/tool/voice.ts +188 -0
  548. package/src/tool/webfetch.ts +205 -0
  549. package/src/tool/webfetch.txt +13 -0
  550. package/src/tool/websearch.ts +150 -0
  551. package/src/tool/websearch.txt +14 -0
  552. package/src/tool/write.ts +80 -0
  553. package/src/tool/write.txt +8 -0
  554. package/src/util/archive.ts +16 -0
  555. package/src/util/color.ts +19 -0
  556. package/src/util/context.ts +25 -0
  557. package/src/util/defer.ts +12 -0
  558. package/src/util/error.ts +77 -0
  559. package/src/util/eventloop.ts +20 -0
  560. package/src/util/filesystem.ts +125 -0
  561. package/src/util/flock.ts +329 -0
  562. package/src/util/fn.ts +11 -0
  563. package/src/util/format.ts +20 -0
  564. package/src/util/hash.ts +7 -0
  565. package/src/util/iife.ts +3 -0
  566. package/src/util/keybind.ts +103 -0
  567. package/src/util/lazy.ts +18 -0
  568. package/src/util/locale.ts +81 -0
  569. package/src/util/lock.ts +98 -0
  570. package/src/util/log.ts +180 -0
  571. package/src/util/network.ts +9 -0
  572. package/src/util/process.ts +15 -0
  573. package/src/util/queue.ts +32 -0
  574. package/src/util/record.ts +3 -0
  575. package/src/util/rpc.ts +66 -0
  576. package/src/util/scrap.ts +10 -0
  577. package/src/util/signal.ts +12 -0
  578. package/src/util/timeout.ts +14 -0
  579. package/src/util/token.ts +7 -0
  580. package/src/util/wildcard.ts +56 -0
  581. package/src/workspace/adaptors/index.ts +271 -0
  582. package/src/workspace/adaptors/types.ts +14 -0
  583. package/src/workspace/adaptors/worktree.ts +31 -0
  584. package/src/workspace/config.ts +19 -0
  585. package/src/workspace/index.ts +223 -0
  586. package/src/workspace/session-proxy-middleware.ts +97 -0
  587. package/src/workspace/sse.ts +66 -0
  588. package/src/workspace/workspace-context.ts +23 -0
  589. package/src/workspace/workspace-server/routes.ts +33 -0
  590. package/src/workspace/workspace-server/server.ts +47 -0
  591. package/src/worktree/index.ts +487 -0
  592. package/sst-env.d.ts +10 -0
  593. package/test/benchmark.test.ts +121 -0
  594. package/test/build-optimizations.test.ts +124 -0
  595. package/test/id-benchmark.test.ts +132 -0
  596. package/test/optimizations.test.ts +302 -0
  597. package/test/preload.ts +1 -0
  598. package/test/solidjs-benchmark.test.ts +262 -0
  599. package/test/solidjs-optimizations.test.ts +259 -0
  600. package/test/tui-benchmark.test.ts +230 -0
  601. package/test/wildcard-benchmark.test.ts +180 -0
  602. package/tsconfig.json +26 -0
@@ -0,0 +1,860 @@
1
+ import { dynamicTool, type Tool, jsonSchema, type JSONSchema7 } from "ai"
2
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js"
3
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js"
4
+ import { SSEClientTransport } from "@modelcontextprotocol/sdk/client/sse.js"
5
+ import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js"
6
+ import { UnauthorizedError } from "@modelcontextprotocol/sdk/client/auth.js"
7
+ import {
8
+ CallToolResultSchema,
9
+ type Tool as MCPToolDef,
10
+ ToolListChangedNotificationSchema,
11
+ } from "@modelcontextprotocol/sdk/types.js"
12
+ import { Config } from "../config/config"
13
+ import { Log } from "../util/log"
14
+ import { NamedError } from "@nikcli-ai/util/error"
15
+ import z from "zod/v4"
16
+ import { Instance } from "../project/instance"
17
+ import { Installation } from "../installation"
18
+ import { withTimeout } from "@/util/timeout"
19
+ import { McpOAuthProvider } from "./oauth-provider"
20
+ import { McpOAuthCallback } from "./oauth-callback"
21
+ import { McpAuth } from "./auth"
22
+ import { BusEvent } from "../bus/bus-event"
23
+ import { Bus } from "@/bus"
24
+ import { TuiEvent } from "@/cli/cmd/tui/event"
25
+ import open from "open"
26
+
27
+ export namespace MCP {
28
+ const log = Log.create({ service: "mcp" })
29
+ const DEFAULT_TIMEOUT = 30_000
30
+
31
+ export const Resource = z
32
+ .object({
33
+ name: z.string(),
34
+ uri: z.string(),
35
+ description: z.string().optional(),
36
+ mimeType: z.string().optional(),
37
+ client: z.string(),
38
+ })
39
+ .meta({ ref: "McpResource" })
40
+ export type Resource = z.infer<typeof Resource>
41
+
42
+ export const ToolsChanged = BusEvent.define(
43
+ "mcp.tools.changed",
44
+ z.object({
45
+ server: z.string(),
46
+ }),
47
+ )
48
+
49
+ export const BrowserOpenFailed = BusEvent.define(
50
+ "mcp.browser.open.failed",
51
+ z.object({
52
+ mcpName: z.string(),
53
+ url: z.string(),
54
+ }),
55
+ )
56
+
57
+ export const Failed = NamedError.create(
58
+ "MCPFailed",
59
+ z.object({
60
+ name: z.string(),
61
+ }),
62
+ )
63
+
64
+ type MCPClient = Client
65
+
66
+ export const Status = z
67
+ .discriminatedUnion("status", [
68
+ z
69
+ .object({
70
+ status: z.literal("connected"),
71
+ })
72
+ .meta({
73
+ ref: "MCPStatusConnected",
74
+ }),
75
+ z
76
+ .object({
77
+ status: z.literal("disabled"),
78
+ })
79
+ .meta({
80
+ ref: "MCPStatusDisabled",
81
+ }),
82
+ z
83
+ .object({
84
+ status: z.literal("failed"),
85
+ error: z.string(),
86
+ })
87
+ .meta({
88
+ ref: "MCPStatusFailed",
89
+ }),
90
+ z
91
+ .object({
92
+ status: z.literal("needs_auth"),
93
+ })
94
+ .meta({
95
+ ref: "MCPStatusNeedsAuth",
96
+ }),
97
+ z
98
+ .object({
99
+ status: z.literal("needs_client_registration"),
100
+ error: z.string(),
101
+ })
102
+ .meta({
103
+ ref: "MCPStatusNeedsClientRegistration",
104
+ }),
105
+ ])
106
+ .meta({
107
+ ref: "MCPStatus",
108
+ })
109
+ export type Status = z.infer<typeof Status>
110
+
111
+ function registerNotificationHandlers(client: MCPClient, serverName: string) {
112
+ client.setNotificationHandler(ToolListChangedNotificationSchema, async () => {
113
+ log.info("tools list changed notification received", { server: serverName })
114
+ Bus.publish(ToolsChanged, { server: serverName })
115
+ })
116
+ }
117
+
118
+ async function convertMcpTool(mcpTool: MCPToolDef, client: MCPClient, timeout?: number): Promise<Tool> {
119
+ const inputSchema = mcpTool.inputSchema
120
+
121
+ const schema: JSONSchema7 = {
122
+ ...(inputSchema as JSONSchema7),
123
+ type: "object",
124
+ properties: (inputSchema.properties ?? {}) as JSONSchema7["properties"],
125
+ additionalProperties: false,
126
+ }
127
+
128
+ return dynamicTool({
129
+ description: mcpTool.description ?? "",
130
+ inputSchema: jsonSchema(schema),
131
+ execute: async (args: unknown) => {
132
+ return client.callTool(
133
+ {
134
+ name: mcpTool.name,
135
+ arguments: args as Record<string, unknown>,
136
+ },
137
+ CallToolResultSchema,
138
+ {
139
+ resetTimeoutOnProgress: true,
140
+ timeout,
141
+ },
142
+ )
143
+ },
144
+ })
145
+ }
146
+
147
+ type TransportWithAuth = StreamableHTTPClientTransport | SSEClientTransport
148
+ const pendingOAuthTransports = new Map<string, TransportWithAuth>()
149
+
150
+ type PromptInfo = Awaited<ReturnType<MCPClient["listPrompts"]>>["prompts"][number]
151
+ type ResourceInfo = Awaited<ReturnType<MCPClient["listResources"]>>["resources"][number]
152
+ type McpEntry = NonNullable<Config.Info["mcp"]>[string]
153
+ function isMcpConfigured(entry: McpEntry): entry is Config.Mcp {
154
+ return typeof entry === "object" && entry !== null && "type" in entry
155
+ }
156
+
157
+ const state = Instance.state(
158
+ async () => {
159
+ const cfg = await Config.get()
160
+ const config = cfg.mcp ?? {}
161
+ const clients: Record<string, MCPClient> = {}
162
+ const status: Record<string, Status> = {}
163
+
164
+ await Promise.all(
165
+ Object.entries(config).map(async ([key, mcp]) => {
166
+ if (!isMcpConfigured(mcp)) {
167
+ log.error("Ignoring MCP config entry without type", { key })
168
+ return
169
+ }
170
+
171
+ if (mcp.enabled === false) {
172
+ status[key] = { status: "disabled" }
173
+ return
174
+ }
175
+
176
+ const result = await create(key, mcp).catch(() => undefined)
177
+ if (!result) return
178
+
179
+ status[key] = result.status
180
+
181
+ if (result.mcpClient) {
182
+ clients[key] = result.mcpClient
183
+ }
184
+ }),
185
+ )
186
+ return {
187
+ status,
188
+ clients,
189
+ }
190
+ },
191
+ async (state) => {
192
+ await Promise.all(
193
+ Object.values(state.clients).map((client) =>
194
+ client.close().catch((error) => {
195
+ log.error("Failed to close MCP client", {
196
+ error,
197
+ })
198
+ }),
199
+ ),
200
+ )
201
+ pendingOAuthTransports.clear()
202
+ },
203
+ )
204
+
205
+ async function fetchPromptsForClient(clientName: string, client: Client) {
206
+ const prompts = await client.listPrompts().catch((e) => {
207
+ log.error("failed to get prompts", { clientName, error: e.message })
208
+ return undefined
209
+ })
210
+
211
+ if (!prompts) {
212
+ return
213
+ }
214
+
215
+ const commands: Record<string, PromptInfo & { client: string }> = {}
216
+
217
+ for (const prompt of prompts.prompts) {
218
+ const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
219
+ const sanitizedPromptName = prompt.name.replace(/[^a-zA-Z0-9_-]/g, "_")
220
+ const key = sanitizedClientName + ":" + sanitizedPromptName
221
+
222
+ commands[key] = { ...prompt, client: clientName }
223
+ }
224
+ return commands
225
+ }
226
+
227
+ async function fetchResourcesForClient(clientName: string, client: Client) {
228
+ const resources = await client.listResources().catch((e) => {
229
+ log.error("failed to get prompts", { clientName, error: e.message })
230
+ return undefined
231
+ })
232
+
233
+ if (!resources) {
234
+ return
235
+ }
236
+
237
+ const commands: Record<string, ResourceInfo & { client: string }> = {}
238
+
239
+ for (const resource of resources.resources) {
240
+ const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
241
+ const sanitizedResourceName = resource.name.replace(/[^a-zA-Z0-9_-]/g, "_")
242
+ const key = sanitizedClientName + ":" + sanitizedResourceName
243
+
244
+ commands[key] = { ...resource, client: clientName }
245
+ }
246
+ return commands
247
+ }
248
+
249
+ export async function add(name: string, mcp: Config.Mcp) {
250
+ const s = await state()
251
+ const result = await create(name, mcp)
252
+ if (!result) {
253
+ const status = {
254
+ status: "failed" as const,
255
+ error: "unknown error",
256
+ }
257
+ s.status[name] = status
258
+ return {
259
+ status,
260
+ }
261
+ }
262
+ if (!result.mcpClient) {
263
+ s.status[name] = result.status
264
+ return {
265
+ status: s.status,
266
+ }
267
+ }
268
+ const existingClient = s.clients[name]
269
+ if (existingClient) {
270
+ await existingClient.close().catch((error) => {
271
+ log.error("Failed to close existing MCP client", { name, error })
272
+ })
273
+ }
274
+ s.clients[name] = result.mcpClient
275
+ s.status[name] = result.status
276
+
277
+ return {
278
+ status: s.status,
279
+ }
280
+ }
281
+
282
+ async function create(key: string, mcp: Config.Mcp) {
283
+ if (mcp.enabled === false) {
284
+ log.info("mcp server disabled", { key })
285
+ return {
286
+ mcpClient: undefined,
287
+ status: { status: "disabled" as const },
288
+ }
289
+ }
290
+
291
+ log.info("found", { key, type: mcp.type })
292
+ let mcpClient: MCPClient | undefined
293
+ let status: Status | undefined = undefined
294
+
295
+ if (mcp.type === "remote") {
296
+ const oauthDisabled = mcp.oauth === false
297
+ const oauthConfig = typeof mcp.oauth === "object" ? mcp.oauth : undefined
298
+ let authProvider: McpOAuthProvider | undefined
299
+
300
+ if (!oauthDisabled) {
301
+ authProvider = new McpOAuthProvider(
302
+ key,
303
+ mcp.url,
304
+ {
305
+ clientId: oauthConfig?.clientId,
306
+ clientSecret: oauthConfig?.clientSecret,
307
+ scope: oauthConfig?.scope,
308
+ },
309
+ {
310
+ onRedirect: async (url) => {
311
+ log.info("oauth redirect requested", { key, url: url.toString() })
312
+ },
313
+ },
314
+ )
315
+ }
316
+
317
+ const transports: Array<{ name: string; transport: TransportWithAuth }> = [
318
+ {
319
+ name: "StreamableHTTP",
320
+ transport: new StreamableHTTPClientTransport(new URL(mcp.url), {
321
+ authProvider,
322
+ requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
323
+ }),
324
+ },
325
+ {
326
+ name: "SSE",
327
+ transport: new SSEClientTransport(new URL(mcp.url), {
328
+ authProvider,
329
+ requestInit: mcp.headers ? { headers: mcp.headers } : undefined,
330
+ }),
331
+ },
332
+ ]
333
+
334
+ let lastError: Error | undefined
335
+ const connectTimeout = mcp.timeout ?? DEFAULT_TIMEOUT
336
+ for (const { name, transport } of transports) {
337
+ try {
338
+ const client = new Client({
339
+ name: "nikcli",
340
+ version: Installation.VERSION,
341
+ })
342
+ await withTimeout(client.connect(transport), connectTimeout)
343
+ registerNotificationHandlers(client, key)
344
+ mcpClient = client
345
+ log.info("connected", { key, transport: name })
346
+ status = { status: "connected" }
347
+ break
348
+ } catch (error) {
349
+ lastError = error instanceof Error ? error : new Error(String(error))
350
+
351
+ if (error instanceof UnauthorizedError) {
352
+ log.info("mcp server requires authentication", { key, transport: name })
353
+
354
+ if (lastError.message.includes("registration") || lastError.message.includes("client_id")) {
355
+ status = {
356
+ status: "needs_client_registration" as const,
357
+ error: "Server does not support dynamic client registration. Please provide clientId in config.",
358
+ }
359
+ Bus.publish(TuiEvent.ToastShow, {
360
+ title: "MCP Authentication Required",
361
+ message: `Server "${key}" requires a pre-registered client ID. Add clientId to your config.`,
362
+ variant: "warning",
363
+ duration: 8000,
364
+ }).catch((e) => log.debug("failed to show toast", { error: e }))
365
+ } else {
366
+ pendingOAuthTransports.set(key, transport)
367
+ status = { status: "needs_auth" as const }
368
+ Bus.publish(TuiEvent.ToastShow, {
369
+ title: "MCP Authentication Required",
370
+ message: `Server "${key}" requires authentication. Run: nikcli mcp auth ${key}`,
371
+ variant: "warning",
372
+ duration: 8000,
373
+ }).catch((e) => log.debug("failed to show toast", { error: e }))
374
+ }
375
+ break
376
+ }
377
+
378
+ log.debug("transport connection failed", {
379
+ key,
380
+ transport: name,
381
+ url: mcp.url,
382
+ error: lastError.message,
383
+ })
384
+ status = {
385
+ status: "failed" as const,
386
+ error: lastError.message,
387
+ }
388
+ }
389
+ }
390
+ }
391
+
392
+ if (mcp.type === "local") {
393
+ const [cmd, ...args] = mcp.command
394
+ const cwd = Instance.directory
395
+ const transport = new StdioClientTransport({
396
+ stderr: "ignore",
397
+ command: cmd,
398
+ args,
399
+ cwd,
400
+ env: {
401
+ ...process.env,
402
+ ...(cmd === "nikcli" ? { BUN_BE_BUN: "1" } : {}),
403
+ ...mcp.environment,
404
+ },
405
+ })
406
+
407
+ const connectTimeout = mcp.timeout ?? DEFAULT_TIMEOUT
408
+ try {
409
+ const client = new Client({
410
+ name: "nikcli",
411
+ version: Installation.VERSION,
412
+ })
413
+ await withTimeout(client.connect(transport), connectTimeout)
414
+ registerNotificationHandlers(client, key)
415
+ mcpClient = client
416
+ status = {
417
+ status: "connected",
418
+ }
419
+ } catch (error) {
420
+ log.error("local mcp startup failed", {
421
+ key,
422
+ command: mcp.command,
423
+ cwd,
424
+ error: error instanceof Error ? error.message : String(error),
425
+ })
426
+ status = {
427
+ status: "failed" as const,
428
+ error: error instanceof Error ? error.message : String(error),
429
+ }
430
+ }
431
+ }
432
+
433
+ if (!status) {
434
+ status = {
435
+ status: "failed" as const,
436
+ error: "Unknown error",
437
+ }
438
+ }
439
+
440
+ if (!mcpClient) {
441
+ return {
442
+ mcpClient: undefined,
443
+ status,
444
+ }
445
+ }
446
+
447
+ const result = await withTimeout(mcpClient.listTools(), mcp.timeout ?? DEFAULT_TIMEOUT).catch((err) => {
448
+ log.error("failed to get tools from client", { key, error: err })
449
+ return undefined
450
+ })
451
+ if (!result) {
452
+ await mcpClient.close().catch((error) => {
453
+ log.error("Failed to close MCP client", {
454
+ error,
455
+ })
456
+ })
457
+ status = {
458
+ status: "failed",
459
+ error: "Failed to get tools",
460
+ }
461
+ return {
462
+ mcpClient: undefined,
463
+ status: {
464
+ status: "failed" as const,
465
+ error: "Failed to get tools",
466
+ },
467
+ }
468
+ }
469
+
470
+ log.info("create() successfully created client", { key, toolCount: result.tools.length })
471
+ return {
472
+ mcpClient,
473
+ status,
474
+ }
475
+ }
476
+
477
+ export async function status() {
478
+ const s = await state()
479
+ const cfg = await Config.get()
480
+ const config = cfg.mcp ?? {}
481
+ const result: Record<string, Status> = {}
482
+
483
+ for (const [key, mcp] of Object.entries(config)) {
484
+ if (!isMcpConfigured(mcp)) continue
485
+ result[key] = s.status[key] ?? { status: "disabled" }
486
+ }
487
+
488
+ return result
489
+ }
490
+
491
+ export async function clients() {
492
+ return state().then((state) => state.clients)
493
+ }
494
+
495
+ export async function connect(name: string) {
496
+ const cfg = await Config.get()
497
+ const config = cfg.mcp ?? {}
498
+ const mcp = config[name]
499
+ if (!mcp) {
500
+ log.error("MCP config not found", { name })
501
+ return
502
+ }
503
+
504
+ if (!isMcpConfigured(mcp)) {
505
+ log.error("Ignoring MCP connect request for config without type", { name })
506
+ return
507
+ }
508
+
509
+ const result = await create(name, { ...mcp, enabled: true })
510
+
511
+ if (!result) {
512
+ const s = await state()
513
+ s.status[name] = {
514
+ status: "failed",
515
+ error: "Unknown error during connection",
516
+ }
517
+ return
518
+ }
519
+
520
+ const s = await state()
521
+ s.status[name] = result.status
522
+ if (result.mcpClient) {
523
+ const existingClient = s.clients[name]
524
+ if (existingClient) {
525
+ await existingClient.close().catch((error) => {
526
+ log.error("Failed to close existing MCP client", { name, error })
527
+ })
528
+ }
529
+ s.clients[name] = result.mcpClient
530
+ }
531
+ }
532
+
533
+ export async function disconnect(name: string) {
534
+ const s = await state()
535
+ const client = s.clients[name]
536
+ if (client) {
537
+ await client.close().catch((error) => {
538
+ log.error("Failed to close MCP client", { name, error })
539
+ })
540
+ delete s.clients[name]
541
+ }
542
+ s.status[name] = { status: "disabled" }
543
+ }
544
+
545
+ export async function tools() {
546
+ const result: Record<string, Tool> = {}
547
+ const s = await state()
548
+ const cfg = await Config.get()
549
+ const config = cfg.mcp ?? {}
550
+ const clientsSnapshot = await clients()
551
+ const defaultTimeout = cfg.experimental?.mcp_timeout
552
+
553
+ for (const [clientName, client] of Object.entries(clientsSnapshot)) {
554
+ if (s.status[clientName]?.status !== "connected") {
555
+ continue
556
+ }
557
+
558
+ const toolsResult = await client.listTools().catch((e) => {
559
+ log.error("failed to get tools", { clientName, error: e.message })
560
+ const failedStatus = {
561
+ status: "failed" as const,
562
+ error: e instanceof Error ? e.message : String(e),
563
+ }
564
+ s.status[clientName] = failedStatus
565
+ delete s.clients[clientName]
566
+ return undefined
567
+ })
568
+ if (!toolsResult) {
569
+ continue
570
+ }
571
+ const mcpConfig = config[clientName]
572
+ const entry = isMcpConfigured(mcpConfig) ? mcpConfig : undefined
573
+ const timeout = entry?.timeout ?? defaultTimeout
574
+ for (const mcpTool of toolsResult.tools) {
575
+ const sanitizedClientName = clientName.replace(/[^a-zA-Z0-9_-]/g, "_")
576
+ const sanitizedToolName = mcpTool.name.replace(/[^a-zA-Z0-9_-]/g, "_")
577
+ result[sanitizedClientName + "_" + sanitizedToolName] = await convertMcpTool(mcpTool, client, timeout)
578
+ }
579
+ }
580
+ return result
581
+ }
582
+
583
+ export async function prompts() {
584
+ const s = await state()
585
+ const clientsSnapshot = await clients()
586
+
587
+ const prompts = Object.fromEntries<PromptInfo & { client: string }>(
588
+ (
589
+ await Promise.all(
590
+ Object.entries(clientsSnapshot).map(async ([clientName, client]) => {
591
+ if (s.status[clientName]?.status !== "connected") {
592
+ return []
593
+ }
594
+
595
+ return Object.entries((await fetchPromptsForClient(clientName, client)) ?? {})
596
+ }),
597
+ )
598
+ ).flat(),
599
+ )
600
+
601
+ return prompts
602
+ }
603
+
604
+ export async function resources() {
605
+ const s = await state()
606
+ const clientsSnapshot = await clients()
607
+
608
+ const result = Object.fromEntries<ResourceInfo & { client: string }>(
609
+ (
610
+ await Promise.all(
611
+ Object.entries(clientsSnapshot).map(async ([clientName, client]) => {
612
+ if (s.status[clientName]?.status !== "connected") {
613
+ return []
614
+ }
615
+
616
+ return Object.entries((await fetchResourcesForClient(clientName, client)) ?? {})
617
+ }),
618
+ )
619
+ ).flat(),
620
+ )
621
+
622
+ return result
623
+ }
624
+
625
+ export async function getPrompt(clientName: string, name: string, args?: Record<string, string>) {
626
+ const clientsSnapshot = await clients()
627
+ const client = clientsSnapshot[clientName]
628
+
629
+ if (!client) {
630
+ log.warn("client not found for prompt", {
631
+ clientName,
632
+ })
633
+ return undefined
634
+ }
635
+
636
+ const result = await client
637
+ .getPrompt({
638
+ name: name,
639
+ arguments: args,
640
+ })
641
+ .catch((e) => {
642
+ log.error("failed to get prompt from MCP server", {
643
+ clientName,
644
+ promptName: name,
645
+ error: e.message,
646
+ })
647
+ return undefined
648
+ })
649
+
650
+ return result
651
+ }
652
+
653
+ export async function readResource(clientName: string, resourceUri: string) {
654
+ const clientsSnapshot = await clients()
655
+ const client = clientsSnapshot[clientName]
656
+
657
+ if (!client) {
658
+ log.warn("client not found for prompt", {
659
+ clientName: clientName,
660
+ })
661
+ return undefined
662
+ }
663
+
664
+ const result = await client
665
+ .readResource({
666
+ uri: resourceUri,
667
+ })
668
+ .catch((e) => {
669
+ log.error("failed to get prompt from MCP server", {
670
+ clientName: clientName,
671
+ resourceUri: resourceUri,
672
+ error: e.message,
673
+ })
674
+ })
675
+
676
+ return result
677
+ }
678
+
679
+ export async function
680
+ startAuth(mcpName: string): Promise<{ authorizationUrl: string }> {
681
+ const cfg = await Config.get()
682
+ const mcpConfig = cfg.mcp?.[mcpName]
683
+
684
+ if (!mcpConfig) {
685
+ throw new Error(`MCP server not found: ${mcpName}`)
686
+ }
687
+
688
+ if (!isMcpConfigured(mcpConfig)) {
689
+ throw new Error(`MCP server ${mcpName} is disabled or missing configuration`)
690
+ }
691
+
692
+ if (mcpConfig.type !== "remote") {
693
+ throw new Error(`MCP server ${mcpName} is not a remote server`)
694
+ }
695
+
696
+ if (mcpConfig.oauth === false) {
697
+ throw new Error(`MCP server ${mcpName} has OAuth explicitly disabled`)
698
+ }
699
+
700
+ await McpOAuthCallback.ensureRunning()
701
+
702
+ const oauthState = Array.from(crypto.getRandomValues(new Uint8Array(32)))
703
+ .map((b) => b.toString(16).padStart(2, "0"))
704
+ .join("")
705
+ await McpAuth.updateOAuthState(mcpName, oauthState)
706
+
707
+ const oauthConfig = typeof mcpConfig.oauth === "object" ? mcpConfig.oauth : undefined
708
+ let capturedUrl: URL | undefined
709
+ const authProvider = new McpOAuthProvider(
710
+ mcpName,
711
+ mcpConfig.url,
712
+ {
713
+ clientId: oauthConfig?.clientId,
714
+ clientSecret: oauthConfig?.clientSecret,
715
+ scope: oauthConfig?.scope,
716
+ },
717
+ {
718
+ onRedirect: async (url) => {
719
+ capturedUrl = url
720
+ },
721
+ },
722
+ )
723
+
724
+ const transport = new StreamableHTTPClientTransport(new URL(mcpConfig.url), {
725
+ authProvider,
726
+ })
727
+
728
+ try {
729
+ const client = new Client({
730
+ name: "nikcli",
731
+ version: Installation.VERSION,
732
+ })
733
+ await client.connect(transport)
734
+ return { authorizationUrl: "" }
735
+ } catch (error) {
736
+ if (error instanceof UnauthorizedError && capturedUrl) {
737
+ pendingOAuthTransports.set(mcpName, transport)
738
+ return { authorizationUrl: capturedUrl.toString() }
739
+ }
740
+ throw error
741
+ }
742
+ }
743
+
744
+ export async function authenticate(mcpName: string): Promise<Status> {
745
+ const { authorizationUrl } = await startAuth(mcpName)
746
+
747
+ if (!authorizationUrl) {
748
+ const s = await state()
749
+ return s.status[mcpName] ?? { status: "connected" }
750
+ }
751
+
752
+ const oauthState = await McpAuth.getOAuthState(mcpName)
753
+ if (!oauthState) {
754
+ throw new Error("OAuth state not found - this should not happen")
755
+ }
756
+
757
+ log.info("opening browser for oauth", { mcpName, url: authorizationUrl, state: oauthState })
758
+
759
+ const callbackPromise = McpOAuthCallback.waitForCallback(oauthState)
760
+
761
+ try {
762
+ const subprocess = await open(authorizationUrl)
763
+ await new Promise<void>((resolve, reject) => {
764
+ const timeout = setTimeout(() => resolve(), 500)
765
+ subprocess.on("error", (error) => {
766
+ clearTimeout(timeout)
767
+ reject(error)
768
+ })
769
+ subprocess.on("exit", (code) => {
770
+ if (code !== null && code !== 0) {
771
+ clearTimeout(timeout)
772
+ reject(new Error(`Browser open failed with exit code ${code}`))
773
+ }
774
+ })
775
+ })
776
+ } catch (error) {
777
+ log.warn("failed to open browser, user must open URL manually", { mcpName, error })
778
+ Bus.publish(BrowserOpenFailed, { mcpName, url: authorizationUrl })
779
+ }
780
+
781
+ const code = await callbackPromise
782
+
783
+ const storedState = await McpAuth.getOAuthState(mcpName)
784
+ if (storedState !== oauthState) {
785
+ await McpAuth.clearOAuthState(mcpName)
786
+ throw new Error("OAuth state mismatch - potential CSRF attack")
787
+ }
788
+
789
+ await McpAuth.clearOAuthState(mcpName)
790
+
791
+ return finishAuth(mcpName, code)
792
+ }
793
+
794
+ export async function finishAuth(mcpName: string, authorizationCode: string): Promise<Status> {
795
+ const transport = pendingOAuthTransports.get(mcpName)
796
+
797
+ if (!transport) {
798
+ throw new Error(`No pending OAuth flow for MCP server: ${mcpName}`)
799
+ }
800
+
801
+ try {
802
+ await transport.finishAuth(authorizationCode)
803
+
804
+ await McpAuth.clearCodeVerifier(mcpName)
805
+
806
+ const cfg = await Config.get()
807
+ const mcpConfig = cfg.mcp?.[mcpName]
808
+
809
+ if (!mcpConfig) {
810
+ throw new Error(`MCP server not found: ${mcpName}`)
811
+ }
812
+
813
+ if (!isMcpConfigured(mcpConfig)) {
814
+ throw new Error(`MCP server ${mcpName} is disabled or missing configuration`)
815
+ }
816
+
817
+ pendingOAuthTransports.delete(mcpName)
818
+ const result = await add(mcpName, mcpConfig)
819
+
820
+ const statusRecord = result.status as Record<string, Status>
821
+ return statusRecord[mcpName] ?? { status: "failed", error: "Unknown error after auth" }
822
+ } catch (error) {
823
+ log.error("failed to finish oauth", { mcpName, error })
824
+ return {
825
+ status: "failed",
826
+ error: error instanceof Error ? error.message : String(error),
827
+ }
828
+ }
829
+ }
830
+
831
+ export async function removeAuth(mcpName: string): Promise<void> {
832
+ await McpAuth.remove(mcpName)
833
+ McpOAuthCallback.cancelPending(mcpName)
834
+ pendingOAuthTransports.delete(mcpName)
835
+ await McpAuth.clearOAuthState(mcpName)
836
+ log.info("removed oauth credentials", { mcpName })
837
+ }
838
+
839
+ export async function supportsOAuth(mcpName: string): Promise<boolean> {
840
+ const cfg = await Config.get()
841
+ const mcpConfig = cfg.mcp?.[mcpName]
842
+ if (!mcpConfig) return false
843
+ if (!isMcpConfigured(mcpConfig)) return false
844
+ return mcpConfig.type === "remote" && mcpConfig.oauth !== false
845
+ }
846
+
847
+ export async function hasStoredTokens(mcpName: string): Promise<boolean> {
848
+ const entry = await McpAuth.get(mcpName)
849
+ return !!entry?.tokens
850
+ }
851
+
852
+ export type AuthStatus = "authenticated" | "expired" | "not_authenticated"
853
+
854
+ export async function getAuthStatus(mcpName: string): Promise<AuthStatus> {
855
+ const hasTokens = await hasStoredTokens(mcpName)
856
+ if (!hasTokens) return "not_authenticated"
857
+ const expired = await McpAuth.isTokenExpired(mcpName)
858
+ return expired ? "expired" : "authenticated"
859
+ }
860
+ }