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,1048 @@
1
+ import "@opentui/solid/runtime-plugin-support"
2
+ import {
3
+ type TuiDispose,
4
+ type TuiPlugin,
5
+ type TuiPluginApi,
6
+ type TuiPluginInstallResult,
7
+ type TuiPluginModule,
8
+ type TuiPluginMeta,
9
+ type TuiPluginStatus,
10
+ type TuiTheme,
11
+ } from "@nikcli-ai/plugin/tui"
12
+ import path from "path"
13
+ import { fileURLToPath } from "url"
14
+
15
+ import { Config } from "@/config/config"
16
+ import { TuiConfig } from "@/config/tui"
17
+ import { Log } from "@/util/log"
18
+ import { errorData, errorMessage } from "@/util/error"
19
+ import { isRecord } from "@/util/record"
20
+ import { Instance } from "@/project/instance"
21
+ import {
22
+ checkPluginCompatibility,
23
+ getPluginIdFromPackage,
24
+ isDeprecatedPlugin,
25
+ parsePluginSpecifier,
26
+ pluginSource,
27
+ readPluginId,
28
+ readV1Plugin,
29
+ resolvePluginEntrypoint,
30
+ resolvePluginId,
31
+ resolvePluginTarget,
32
+ type PluginSource,
33
+ } from "@/plugin/shared"
34
+ import { PluginMeta } from "@/plugin/meta"
35
+ import { installPlugin as installModulePlugin, patchPluginConfig, readPluginManifest } from "@/plugin/install"
36
+ import { addTheme, hasTheme } from "../context/theme"
37
+ import { Global } from "@/global"
38
+ import { Filesystem } from "@/util/filesystem"
39
+ import { Process } from "@/util/process"
40
+ import { Flag } from "@/flag/flag"
41
+ import { Installation } from "@/installation"
42
+ import { INTERNAL_TUI_PLUGINS, type InternalTuiPlugin } from "./internal"
43
+ import { setupSlots, Slot as View } from "./slots"
44
+ import type { HostPluginApi, HostSlots } from "./slots"
45
+
46
+ type PluginLoad = {
47
+ item?: Config.PluginSpec
48
+ spec: string
49
+ target: string
50
+ retry: boolean
51
+ source: PluginSource | "internal"
52
+ id: string
53
+ module: TuiPluginModule
54
+ install_theme: TuiTheme["install"]
55
+ }
56
+
57
+ type Api = HostPluginApi
58
+
59
+ type PluginScope = {
60
+ lifecycle: TuiPluginApi["lifecycle"]
61
+ track: (fn: (() => void) | undefined) => () => void
62
+ dispose: () => Promise<void>
63
+ }
64
+
65
+ type PluginEntry = {
66
+ id: string
67
+ load: PluginLoad
68
+ meta: TuiPluginMeta
69
+ plugin: TuiPlugin
70
+ options: Config.PluginOptions | undefined
71
+ enabled: boolean
72
+ scope?: PluginScope
73
+ }
74
+
75
+ type ServerPluginEntry = {
76
+ id: string
77
+ spec: string
78
+ source: PluginSource | "internal"
79
+ }
80
+
81
+ type RuntimeState = {
82
+ directory: string
83
+ api: Api
84
+ slots: HostSlots
85
+ plugins: PluginEntry[]
86
+ plugins_by_id: Map<string, PluginEntry>
87
+ serverPlugins: ServerPluginEntry[]
88
+ pending: Map<
89
+ string,
90
+ {
91
+ item: Config.PluginSpec
92
+ meta: TuiConfig.PluginMeta
93
+ }
94
+ >
95
+ }
96
+
97
+ const log = Log.create({ service: "tui.plugin" })
98
+ const DISPOSE_TIMEOUT_MS = 5000
99
+ const KV_KEY = "plugin_enabled"
100
+
101
+ function fail(message: string, data: Record<string, unknown>) {
102
+ if (!("error" in data)) {
103
+ log.error(message, data)
104
+ console.error(`[tui.plugin] ${message}`, data)
105
+ return
106
+ }
107
+
108
+ const text = `${message}: ${errorMessage(data.error)}`
109
+ const next = { ...data, error: errorData(data.error) }
110
+ log.error(text, next)
111
+ console.error(`[tui.plugin] ${text}`, next)
112
+ }
113
+
114
+ type CleanupResult = { type: "ok" } | { type: "error"; error: unknown } | { type: "timeout" }
115
+
116
+ function runCleanup(fn: () => unknown, ms: number): Promise<CleanupResult> {
117
+ return new Promise((resolve) => {
118
+ const timer = setTimeout(() => {
119
+ resolve({ type: "timeout" })
120
+ }, ms)
121
+
122
+ Promise.resolve()
123
+ .then(fn)
124
+ .then(
125
+ () => {
126
+ resolve({ type: "ok" })
127
+ },
128
+ (error) => {
129
+ resolve({ type: "error", error })
130
+ },
131
+ )
132
+ .finally(() => {
133
+ clearTimeout(timer)
134
+ })
135
+ })
136
+ }
137
+
138
+ function isTheme(value: unknown) {
139
+ if (!isRecord(value)) return false
140
+ if (!("theme" in value)) return false
141
+ if (!isRecord(value.theme)) return false
142
+ return true
143
+ }
144
+
145
+ function resolveRoot(root: string) {
146
+ if (root.startsWith("file://")) {
147
+ const file = fileURLToPath(root)
148
+ if (root.endsWith("/")) return file
149
+ return path.dirname(file)
150
+ }
151
+ if (path.isAbsolute(root)) return root
152
+ return path.resolve(process.cwd(), root)
153
+ }
154
+
155
+ function createThemeInstaller(meta: TuiConfig.PluginMeta, root: string, spec: string): TuiTheme["install"] {
156
+ return async (file) => {
157
+ const raw = file.startsWith("file://") ? fileURLToPath(file) : file
158
+ const src = path.isAbsolute(raw) ? raw : path.resolve(root, raw)
159
+ const theme = path.basename(src, path.extname(src))
160
+ if (hasTheme(theme)) return
161
+
162
+ const text = await Filesystem.readText(src).catch((error) => {
163
+ log.warn("failed to read tui plugin theme", { path: spec, theme: src, error })
164
+ return
165
+ })
166
+ if (text === undefined) return
167
+
168
+ const fail = Symbol()
169
+ const data = await Promise.resolve(text)
170
+ .then((x) => JSON.parse(x))
171
+ .catch((error) => {
172
+ log.warn("failed to parse tui plugin theme", { path: spec, theme: src, error })
173
+ return fail
174
+ })
175
+ if (data === fail) return
176
+
177
+ if (!isTheme(data)) {
178
+ log.warn("invalid tui plugin theme", { path: spec, theme: src })
179
+ return
180
+ }
181
+
182
+ const source_dir = path.dirname(meta.source)
183
+ const local_dir =
184
+ path.basename(source_dir) === ".nikcli"
185
+ ? path.join(source_dir, "themes")
186
+ : path.join(source_dir, ".nikcli", "themes")
187
+ const dest_dir = meta.scope === "local" ? local_dir : path.join(Global.Path.config, "themes")
188
+ const dest = path.join(dest_dir, `${theme}.json`)
189
+ if (!(await Filesystem.exists(dest))) {
190
+ await Filesystem.write(dest, text).catch((error) => {
191
+ log.warn("failed to persist tui plugin theme", { path: spec, theme: src, dest, error })
192
+ })
193
+ }
194
+
195
+ addTheme(theme, data)
196
+ }
197
+ }
198
+
199
+ async function loadExternalPlugin(
200
+ item: Config.PluginSpec,
201
+ meta: TuiConfig.PluginMeta | undefined,
202
+ retry = false,
203
+ ): Promise<PluginLoad | undefined> {
204
+ const spec = Config.pluginSpecifier(item)
205
+ if (isDeprecatedPlugin(spec)) return
206
+ log.info("loading tui plugin", { path: spec, retry })
207
+ const resolved = await resolvePluginTarget(spec).catch((error) => {
208
+ fail("failed to resolve tui plugin", { path: spec, retry, error })
209
+ return
210
+ })
211
+ if (!resolved) return
212
+
213
+ const source = pluginSource(spec)
214
+ if (source === "npm") {
215
+ const ok = await checkPluginCompatibility(resolved, Installation.VERSION)
216
+ .then(() => true)
217
+ .catch((error) => {
218
+ fail("tui plugin incompatible", { path: spec, retry, error })
219
+ return false
220
+ })
221
+ if (!ok) return
222
+ }
223
+
224
+ const target = resolved
225
+ if (!meta) {
226
+ fail("missing tui plugin metadata", {
227
+ path: spec,
228
+ retry,
229
+ })
230
+ return
231
+ }
232
+
233
+ const root = resolveRoot(source === "file" ? spec : target)
234
+ const install_theme = createThemeInstaller(meta, root, spec)
235
+ const entry = await resolvePluginEntrypoint(spec, target, "tui").catch((error) => {
236
+ fail("failed to resolve tui plugin entry", { path: spec, target, retry, error })
237
+ return
238
+ })
239
+ if (!entry) return
240
+
241
+ const mod = await import(entry)
242
+ .then((raw) => {
243
+ return readV1Plugin(raw as Record<string, unknown>, spec, "tui", "detect") as TuiPluginModule | undefined
244
+ })
245
+ .catch((error) => {
246
+ fail("failed to load tui plugin", { path: spec, target: entry, retry, error })
247
+ return
248
+ })
249
+ if (!mod) return
250
+
251
+ const id = await resolvePluginId(source, spec, target, readPluginId(mod.id, spec)).catch((error) => {
252
+ fail("failed to load tui plugin", { path: spec, target, retry, error })
253
+ return
254
+ })
255
+ if (!id) return
256
+
257
+ return {
258
+ item,
259
+ spec,
260
+ target,
261
+ retry,
262
+ source,
263
+ id,
264
+ module: mod,
265
+ install_theme,
266
+ }
267
+ }
268
+
269
+ function createMeta(
270
+ source: PluginLoad["source"],
271
+ spec: string,
272
+ target: string,
273
+ meta: { state: PluginMeta.State; entry: PluginMeta.Entry } | undefined,
274
+ id?: string,
275
+ ): TuiPluginMeta {
276
+ if (meta) {
277
+ return {
278
+ state: meta.state,
279
+ ...meta.entry,
280
+ }
281
+ }
282
+
283
+ const now = Date.now()
284
+ return {
285
+ state: source === "internal" ? "same" : "first",
286
+ id: id ?? spec,
287
+ source,
288
+ spec,
289
+ target,
290
+ first_time: now,
291
+ last_time: now,
292
+ time_changed: now,
293
+ load_count: 1,
294
+ fingerprint: target,
295
+ }
296
+ }
297
+
298
+ function loadInternalPlugin(item: InternalTuiPlugin): PluginLoad {
299
+ const spec = item.id
300
+ const target = spec
301
+
302
+ return {
303
+ spec,
304
+ target,
305
+ retry: false,
306
+ source: "internal",
307
+ id: item.id,
308
+ module: item,
309
+ install_theme: createThemeInstaller(
310
+ {
311
+ scope: "global",
312
+ source: target,
313
+ },
314
+ process.cwd(),
315
+ spec,
316
+ ),
317
+ }
318
+ }
319
+
320
+ function createPluginScope(load: PluginLoad, id: string) {
321
+ const ctrl = new AbortController()
322
+ let list: { key: symbol; fn: TuiDispose }[] = []
323
+ let done = false
324
+
325
+ const onDispose = (fn: TuiDispose) => {
326
+ if (done) return () => {}
327
+ const key = Symbol()
328
+ list.push({ key, fn })
329
+ let drop = false
330
+ return () => {
331
+ if (drop) return
332
+ drop = true
333
+ list = list.filter((x) => x.key !== key)
334
+ }
335
+ }
336
+
337
+ const track = (fn: (() => void) | undefined) => {
338
+ if (!fn) return () => {}
339
+ const off = onDispose(fn)
340
+ let drop = false
341
+ return () => {
342
+ if (drop) return
343
+ drop = true
344
+ off()
345
+ fn()
346
+ }
347
+ }
348
+
349
+ const lifecycle: TuiPluginApi["lifecycle"] = {
350
+ signal: ctrl.signal,
351
+ onDispose,
352
+ }
353
+
354
+ const dispose = async () => {
355
+ if (done) return
356
+ done = true
357
+ ctrl.abort()
358
+ const queue = [...list].reverse()
359
+ list = []
360
+ const until = Date.now() + DISPOSE_TIMEOUT_MS
361
+ for (const item of queue) {
362
+ const left = until - Date.now()
363
+ if (left <= 0) {
364
+ fail("timed out cleaning up tui plugin", {
365
+ path: load.spec,
366
+ id,
367
+ timeout: DISPOSE_TIMEOUT_MS,
368
+ })
369
+ break
370
+ }
371
+
372
+ const out = await runCleanup(item.fn, left)
373
+ if (out.type === "ok") continue
374
+ if (out.type === "timeout") {
375
+ fail("timed out cleaning up tui plugin", {
376
+ path: load.spec,
377
+ id,
378
+ timeout: DISPOSE_TIMEOUT_MS,
379
+ })
380
+ break
381
+ }
382
+
383
+ if (out.type === "error") {
384
+ fail("failed to clean up tui plugin", {
385
+ path: load.spec,
386
+ id,
387
+ error: out.error,
388
+ })
389
+ }
390
+ }
391
+ }
392
+
393
+ return {
394
+ lifecycle,
395
+ track,
396
+ dispose,
397
+ }
398
+ }
399
+
400
+ function readPluginEnabledMap(value: unknown) {
401
+ if (!isRecord(value)) return {}
402
+ return Object.fromEntries(
403
+ Object.entries(value).filter((item): item is [string, boolean] => typeof item[1] === "boolean"),
404
+ )
405
+ }
406
+
407
+ function pluginEnabledState(state: RuntimeState, config: TuiConfig.Info) {
408
+ return {
409
+ ...readPluginEnabledMap(config.plugin_enabled),
410
+ ...readPluginEnabledMap(state.api.kv.get(KV_KEY, {})),
411
+ }
412
+ }
413
+
414
+ function writePluginEnabledState(api: Api, id: string, enabled: boolean) {
415
+ api.kv.set(KV_KEY, {
416
+ ...readPluginEnabledMap(api.kv.get(KV_KEY, {})),
417
+ [id]: enabled,
418
+ })
419
+ }
420
+
421
+ function listPluginStatus(state: RuntimeState): TuiPluginStatus[] {
422
+ const seen = new Set<string>()
423
+ const tuiPlugins: TuiPluginStatus[] = []
424
+
425
+ for (const plugin of state.plugins) {
426
+ if (seen.has(plugin.id)) continue
427
+ seen.add(plugin.id)
428
+ tuiPlugins.push({
429
+ id: plugin.id,
430
+ source: plugin.meta.source,
431
+ spec: plugin.meta.spec,
432
+ target: plugin.meta.target,
433
+ enabled: plugin.enabled,
434
+ active: plugin.scope !== undefined,
435
+ })
436
+ }
437
+
438
+ for (const sp of state.serverPlugins) {
439
+ if (seen.has(sp.id)) continue
440
+ seen.add(sp.id)
441
+ tuiPlugins.push({
442
+ id: sp.id,
443
+ source: sp.source,
444
+ spec: sp.spec,
445
+ target: "server",
446
+ enabled: true,
447
+ active: false,
448
+ })
449
+ }
450
+
451
+ return tuiPlugins
452
+ }
453
+
454
+ async function deactivatePluginEntry(state: RuntimeState, plugin: PluginEntry, persist: boolean) {
455
+ plugin.enabled = false
456
+ if (persist) writePluginEnabledState(state.api, plugin.id, false)
457
+ if (!plugin.scope) return true
458
+ const scope = plugin.scope
459
+ plugin.scope = undefined
460
+ await scope.dispose()
461
+ return true
462
+ }
463
+
464
+ async function activatePluginEntry(state: RuntimeState, plugin: PluginEntry, persist: boolean) {
465
+ plugin.enabled = true
466
+ if (persist) writePluginEnabledState(state.api, plugin.id, true)
467
+ if (plugin.scope) return true
468
+
469
+ const scope = createPluginScope(plugin.load, plugin.id)
470
+ const api = pluginApi(state, plugin.load, scope, plugin.id)
471
+ const ok = await Promise.resolve()
472
+ .then(async () => {
473
+ await plugin.plugin(api, plugin.options, plugin.meta)
474
+ return true
475
+ })
476
+ .catch((error) => {
477
+ fail("failed to initialize tui plugin", {
478
+ path: plugin.load.spec,
479
+ id: plugin.id,
480
+ error,
481
+ })
482
+ return false
483
+ })
484
+
485
+ if (!ok) {
486
+ await scope.dispose()
487
+ return false
488
+ }
489
+
490
+ if (!plugin.enabled) {
491
+ await scope.dispose()
492
+ return true
493
+ }
494
+
495
+ plugin.scope = scope
496
+ return true
497
+ }
498
+
499
+ async function activatePluginById(state: RuntimeState | undefined, id: string, persist: boolean) {
500
+ if (!state) return false
501
+ const plugin = state.plugins_by_id.get(id)
502
+ if (!plugin) return false
503
+ return activatePluginEntry(state, plugin, persist)
504
+ }
505
+
506
+ async function deactivatePluginById(state: RuntimeState | undefined, id: string, persist: boolean) {
507
+ if (!state) return false
508
+ const plugin = state.plugins_by_id.get(id)
509
+ if (!plugin) return false
510
+ return deactivatePluginEntry(state, plugin, persist)
511
+ }
512
+
513
+ function pluginApi(runtime: RuntimeState, load: PluginLoad, scope: PluginScope, base: string): TuiPluginApi {
514
+ const api = runtime.api
515
+ const host = runtime.slots
516
+ const command: TuiPluginApi["command"] = {
517
+ register(cb) {
518
+ return scope.track(api.command.register(cb))
519
+ },
520
+ trigger(value) {
521
+ api.command.trigger(value)
522
+ },
523
+ }
524
+
525
+ const route: TuiPluginApi["route"] = {
526
+ register(list) {
527
+ return scope.track(api.route.register(list))
528
+ },
529
+ navigate(name, params) {
530
+ api.route.navigate(name, params)
531
+ },
532
+ get current() {
533
+ return api.route.current
534
+ },
535
+ }
536
+
537
+ const theme: TuiPluginApi["theme"] = Object.assign(Object.create(api.theme), {
538
+ install: load.install_theme,
539
+ })
540
+
541
+ const event: TuiPluginApi["event"] = {
542
+ on(type, handler) {
543
+ return scope.track(api.event.on(type, handler))
544
+ },
545
+ }
546
+
547
+ let count = 0
548
+
549
+ const slots: TuiPluginApi["slots"] = {
550
+ register(plugin) {
551
+ const id = count ? `${base}:${count}` : base
552
+ count += 1
553
+ scope.track(host.register({ ...plugin, id }))
554
+ return id
555
+ },
556
+ }
557
+
558
+ return {
559
+ app: api.app,
560
+ command,
561
+ route,
562
+ ui: api.ui,
563
+ keybind: api.keybind,
564
+ tuiConfig: api.tuiConfig,
565
+ kv: api.kv,
566
+ state: api.state,
567
+ theme,
568
+ get client() {
569
+ return api.client
570
+ },
571
+ scopedClient: api.scopedClient,
572
+ workspace: api.workspace,
573
+ event,
574
+ renderer: api.renderer,
575
+ slots,
576
+ plugins: {
577
+ list() {
578
+ return listPluginStatus(runtime)
579
+ },
580
+ activate(id) {
581
+ return activatePluginById(runtime, id, true)
582
+ },
583
+ deactivate(id) {
584
+ return deactivatePluginById(runtime, id, true)
585
+ },
586
+ add(spec) {
587
+ return addPluginBySpec(runtime, spec)
588
+ },
589
+ install(spec, options) {
590
+ return installPluginBySpec(runtime, spec, options?.global)
591
+ },
592
+ },
593
+ lifecycle: scope.lifecycle,
594
+ }
595
+ }
596
+
597
+ function collectPluginEntries(load: PluginLoad, meta: TuiPluginMeta): PluginEntry[] {
598
+ if (!load.module.tui) return []
599
+ const options = load.item ? Config.pluginOptions(load.item) : undefined
600
+ return [
601
+ {
602
+ id: load.id,
603
+ load,
604
+ meta,
605
+ plugin: load.module.tui,
606
+ options,
607
+ enabled: true,
608
+ },
609
+ ]
610
+ }
611
+
612
+ function addPluginEntry(state: RuntimeState, plugin: PluginEntry) {
613
+ if (state.plugins_by_id.has(plugin.id)) {
614
+ fail("duplicate tui plugin id", {
615
+ id: plugin.id,
616
+ path: plugin.load.spec,
617
+ })
618
+ return false
619
+ }
620
+
621
+ state.plugins_by_id.set(plugin.id, plugin)
622
+ state.plugins.push(plugin)
623
+ return true
624
+ }
625
+
626
+ function applyInitialPluginEnabledState(state: RuntimeState, config: TuiConfig.Info) {
627
+ const map = pluginEnabledState(state, config)
628
+ for (const plugin of state.plugins) {
629
+ const enabled = map[plugin.id]
630
+ if (enabled === undefined) continue
631
+ plugin.enabled = enabled
632
+ }
633
+ }
634
+
635
+ async function resolveExternalPlugins(
636
+ list: Config.PluginSpec[],
637
+ wait: () => Promise<void>,
638
+ meta: (item: Config.PluginSpec) => TuiConfig.PluginMeta | undefined,
639
+ ) {
640
+ const loaded = await Promise.all(list.map((item) => loadExternalPlugin(item, meta(item))))
641
+ const ready: PluginLoad[] = []
642
+ let deps: Promise<void> | undefined
643
+
644
+ for (let i = 0; i < list.length; i++) {
645
+ let entry = loaded[i]
646
+ if (!entry) {
647
+ const item = list[i]
648
+ if (!item) continue
649
+ const spec = Config.pluginSpecifier(item)
650
+ if (pluginSource(spec) !== "file") continue
651
+ deps ??= wait().catch((error) => {
652
+ log.warn("failed waiting for tui plugin dependencies", { error })
653
+ })
654
+ await deps
655
+ entry = await loadExternalPlugin(item, meta(item), true)
656
+ }
657
+ if (!entry) continue
658
+ ready.push(entry)
659
+ }
660
+
661
+ return ready
662
+ }
663
+
664
+ async function loadAllPluginInfo(list: Config.PluginSpec[]) {
665
+ const results: ServerPluginEntry[] = []
666
+ const seen = new Set<string>()
667
+
668
+ for (const item of list) {
669
+ const spec = Config.pluginSpecifier(item)
670
+ if (seen.has(spec)) continue
671
+ seen.add(spec)
672
+
673
+ if (isDeprecatedPlugin(spec)) continue
674
+
675
+ try {
676
+ const source = pluginSource(spec)
677
+ const id = await getPluginIdFromPackage(spec, spec)
678
+ if (id) {
679
+ results.push({ id, spec, source })
680
+ } else {
681
+ // Fallback: use the package name as id
682
+ const parsed = parsePluginSpecifier(spec)
683
+ results.push({ id: parsed.pkg, spec, source })
684
+ }
685
+ } catch {
686
+ // Fallback: use the package name as id
687
+ const parsed = parsePluginSpecifier(spec)
688
+ results.push({ id: parsed.pkg, spec, source: pluginSource(spec) })
689
+ }
690
+ }
691
+
692
+ return results
693
+ }
694
+
695
+ async function addExternalPluginEntries(state: RuntimeState, ready: PluginLoad[]) {
696
+ if (!ready.length) return { plugins: [] as PluginEntry[], ok: true }
697
+
698
+ const meta = await PluginMeta.touchMany(
699
+ ready.map((item) => ({
700
+ spec: item.spec,
701
+ target: item.target,
702
+ id: item.id,
703
+ })),
704
+ ).catch((error) => {
705
+ log.warn("failed to track tui plugins", { error })
706
+ return undefined
707
+ })
708
+
709
+ const plugins: PluginEntry[] = []
710
+ let ok = true
711
+ for (let i = 0; i < ready.length; i++) {
712
+ const entry = ready[i]
713
+ if (!entry) continue
714
+ const hit = meta?.[i]
715
+ if (hit && hit.state !== "same") {
716
+ log.info("tui plugin metadata updated", {
717
+ path: entry.spec,
718
+ retry: entry.retry,
719
+ state: hit.state,
720
+ source: hit.entry.source,
721
+ version: hit.entry.version,
722
+ modified: hit.entry.modified,
723
+ })
724
+ }
725
+
726
+ const row = createMeta(entry.source, entry.spec, entry.target, hit, entry.id)
727
+ for (const plugin of collectPluginEntries(entry, row)) {
728
+ if (!addPluginEntry(state, plugin)) {
729
+ ok = false
730
+ continue
731
+ }
732
+ plugins.push(plugin)
733
+ }
734
+ }
735
+
736
+ return { plugins, ok }
737
+ }
738
+
739
+ function defaultPluginMeta(state: RuntimeState): TuiConfig.PluginMeta {
740
+ return {
741
+ scope: "local",
742
+ source: state.api.state.path.config || path.join(state.directory, ".nikcli", "tui.json"),
743
+ }
744
+ }
745
+
746
+ function installCause(err: unknown) {
747
+ if (!err || typeof err !== "object") return
748
+ if (!("cause" in err)) return
749
+ return (err as { cause?: unknown }).cause
750
+ }
751
+
752
+ function installDetail(err: unknown) {
753
+ const hit = installCause(err) ?? err
754
+ if (!(hit instanceof Process.RunFailedError)) {
755
+ return {
756
+ message: errorMessage(hit),
757
+ missing: false,
758
+ }
759
+ }
760
+
761
+ const lines = hit.stderr
762
+ .toString()
763
+ .split(/\r?\n/)
764
+ .map((line) => line.trim())
765
+ .filter(Boolean)
766
+ const errs = lines.filter((line) => line.startsWith("error:")).map((line) => line.replace(/^error:\s*/, ""))
767
+ return {
768
+ message: errs[0] ?? lines.at(-1) ?? errorMessage(hit),
769
+ missing: lines.some((line) => line.includes("No version matching")),
770
+ }
771
+ }
772
+
773
+ async function addPluginBySpec(state: RuntimeState | undefined, raw: string) {
774
+ if (!state) return false
775
+ const spec = raw.trim()
776
+ if (!spec) return false
777
+
778
+ const pending = state.pending.get(spec)
779
+ const item = pending?.item ?? spec
780
+ const nextSpec = Config.pluginSpecifier(item)
781
+ if (state.plugins.some((plugin) => plugin.load.spec === nextSpec)) {
782
+ state.pending.delete(spec)
783
+ return true
784
+ }
785
+
786
+ const meta = pending?.meta ?? defaultPluginMeta(state)
787
+
788
+ const ready = await Instance.provide({
789
+ directory: state.directory,
790
+ fn: () =>
791
+ resolveExternalPlugins(
792
+ [item],
793
+ () => TuiConfig.waitForDependencies(),
794
+ () => meta,
795
+ ),
796
+ }).catch((error) => {
797
+ fail("failed to add tui plugin", { path: nextSpec, error })
798
+ return [] as PluginLoad[]
799
+ })
800
+ if (!ready.length) {
801
+ fail("failed to add tui plugin", { path: nextSpec })
802
+ return false
803
+ }
804
+
805
+ const first = ready[0]
806
+ if (!first) {
807
+ fail("failed to add tui plugin", { path: nextSpec })
808
+ return false
809
+ }
810
+ if (state.plugins_by_id.has(first.id)) {
811
+ state.pending.delete(spec)
812
+ return true
813
+ }
814
+
815
+ const out = await addExternalPluginEntries(state, [first])
816
+ let ok = out.ok && out.plugins.length > 0
817
+ for (const plugin of out.plugins) {
818
+ const active = await activatePluginEntry(state, plugin, false)
819
+ if (!active) ok = false
820
+ }
821
+
822
+ if (ok) state.pending.delete(spec)
823
+ if (!ok) {
824
+ fail("failed to add tui plugin", { path: nextSpec })
825
+ }
826
+ return ok
827
+ }
828
+
829
+ async function installPluginBySpec(
830
+ state: RuntimeState | undefined,
831
+ raw: string,
832
+ global = false,
833
+ ): Promise<TuiPluginInstallResult> {
834
+ if (!state) {
835
+ return {
836
+ ok: false,
837
+ message: "Plugin runtime is not ready.",
838
+ }
839
+ }
840
+
841
+ const spec = raw.trim()
842
+ if (!spec) {
843
+ return {
844
+ ok: false,
845
+ message: "Plugin package name is required",
846
+ }
847
+ }
848
+
849
+ const dir = state.api.state.path
850
+ if (!dir.directory) {
851
+ return {
852
+ ok: false,
853
+ message: "Paths are still syncing. Try again in a moment.",
854
+ }
855
+ }
856
+
857
+ const install = await installModulePlugin(spec)
858
+ if (!install.ok) {
859
+ const out = installDetail(install.error)
860
+ return {
861
+ ok: false,
862
+ message: out.message,
863
+ missing: out.missing,
864
+ }
865
+ }
866
+
867
+ const manifest = await readPluginManifest(install.target)
868
+ if (!manifest.ok) {
869
+ if (manifest.code === "manifest_no_targets") {
870
+ return {
871
+ ok: false,
872
+ message: `"${spec}" does not declare supported targets in package.json`,
873
+ }
874
+ }
875
+
876
+ return {
877
+ ok: false,
878
+ message: `Installed "${spec}" but failed to read ${manifest.file}`,
879
+ }
880
+ }
881
+
882
+ const patch = await patchPluginConfig({
883
+ spec,
884
+ targets: manifest.targets,
885
+ global,
886
+ vcs: dir.worktree && dir.worktree !== "/" ? "git" : undefined,
887
+ worktree: dir.worktree,
888
+ directory: dir.directory,
889
+ })
890
+ if (!patch.ok) {
891
+ if (patch.code === "invalid_json") {
892
+ return {
893
+ ok: false,
894
+ message: `Invalid JSON in ${patch.file} (${patch.parse} at line ${patch.line}, column ${patch.col})`,
895
+ }
896
+ }
897
+
898
+ return {
899
+ ok: false,
900
+ message: errorMessage(patch.error),
901
+ }
902
+ }
903
+
904
+ const tui = manifest.targets.find((item) => item.kind === "tui")
905
+ if (tui) {
906
+ const file = patch.items.find((item) => item.kind === "tui")?.file
907
+ state.pending.set(spec, {
908
+ item: tui.opts ? [spec, tui.opts] : spec,
909
+ meta: {
910
+ scope: global ? "global" : "local",
911
+ source: (file ?? dir.config) || path.join(patch.dir, "tui.json"),
912
+ },
913
+ })
914
+ }
915
+
916
+ return {
917
+ ok: true,
918
+ dir: patch.dir,
919
+ tui: Boolean(tui),
920
+ }
921
+ }
922
+
923
+ export namespace TuiPluginRuntime {
924
+ let dir = ""
925
+ let loaded: Promise<void> | undefined
926
+ let runtime: RuntimeState | undefined
927
+ export const Slot = View
928
+
929
+ export async function init(api: HostPluginApi) {
930
+ const cwd = process.cwd()
931
+ if (loaded) {
932
+ if (dir !== cwd) {
933
+ throw new Error(`TuiPluginRuntime.init() called with a different working directory. expected=${dir} got=${cwd}`)
934
+ }
935
+ return loaded
936
+ }
937
+
938
+ dir = cwd
939
+ loaded = load(api)
940
+ return loaded
941
+ }
942
+
943
+ export function list() {
944
+ if (!runtime) return []
945
+ return listPluginStatus(runtime)
946
+ }
947
+
948
+ export async function activatePlugin(id: string) {
949
+ return activatePluginById(runtime, id, true)
950
+ }
951
+
952
+ export async function deactivatePlugin(id: string) {
953
+ return deactivatePluginById(runtime, id, true)
954
+ }
955
+
956
+ export async function addPlugin(spec: string) {
957
+ return addPluginBySpec(runtime, spec)
958
+ }
959
+
960
+ export async function installPlugin(spec: string, options?: { global?: boolean }) {
961
+ return installPluginBySpec(runtime, spec, options?.global)
962
+ }
963
+
964
+ export async function dispose() {
965
+ const task = loaded
966
+ loaded = undefined
967
+ dir = ""
968
+ if (task) await task
969
+ const state = runtime
970
+ runtime = undefined
971
+ if (!state) return
972
+ const queue = [...state.plugins].reverse()
973
+ for (const plugin of queue) {
974
+ await deactivatePluginEntry(state, plugin, false)
975
+ }
976
+ }
977
+
978
+ async function load(api: Api) {
979
+ const cwd = process.cwd()
980
+ const slots = setupSlots(api)
981
+ const next: RuntimeState = {
982
+ directory: cwd,
983
+ api,
984
+ slots,
985
+ plugins: [],
986
+ plugins_by_id: new Map(),
987
+ serverPlugins: [],
988
+ pending: new Map(),
989
+ }
990
+ runtime = next
991
+
992
+ await Instance.provide({
993
+ directory: cwd,
994
+ fn: async () => {
995
+ const config = await TuiConfig.get()
996
+ const plugins = Flag.NIKCLI_PURE ? [] : (config.plugin ?? [])
997
+ if (Flag.NIKCLI_PURE && config.plugin?.length) {
998
+ log.info("skipping external tui plugins in pure mode", { count: config.plugin.length })
999
+ }
1000
+
1001
+ for (const item of INTERNAL_TUI_PLUGINS) {
1002
+ log.info("loading internal tui plugin", { id: item.id })
1003
+ const entry = loadInternalPlugin(item)
1004
+ const meta = createMeta(entry.source, entry.spec, entry.target, undefined, entry.id)
1005
+ for (const plugin of collectPluginEntries(entry, meta)) {
1006
+ addPluginEntry(next, plugin)
1007
+ }
1008
+ }
1009
+
1010
+ const ready = await resolveExternalPlugins(
1011
+ plugins,
1012
+ () => TuiConfig.waitForDependencies(),
1013
+ (item) => config.plugin_meta?.[Config.pluginSpecifier(item)],
1014
+ )
1015
+ await addExternalPluginEntries(next, ready)
1016
+
1017
+ for (const entry of ready) {
1018
+ if (!next.serverPlugins.some((p) => p.id === entry.id)) {
1019
+ next.serverPlugins.push({
1020
+ id: entry.id,
1021
+ spec: entry.spec,
1022
+ source: entry.source,
1023
+ })
1024
+ }
1025
+ }
1026
+
1027
+ const allPluginInfo = await loadAllPluginInfo(plugins)
1028
+ for (const info of allPluginInfo) {
1029
+ if (!next.serverPlugins.some((p) => p.id === info.id)) {
1030
+ next.serverPlugins.push(info)
1031
+ }
1032
+ }
1033
+
1034
+ applyInitialPluginEnabledState(next, config)
1035
+ for (const plugin of next.plugins) {
1036
+ if (!plugin.enabled) continue
1037
+ // Keep plugin execution sequential for deterministic side effects:
1038
+ // command registration order affects keybind/command precedence,
1039
+ // route registration is last-wins when ids collide,
1040
+ // and hook chains rely on stable plugin ordering.
1041
+ await activatePluginEntry(next, plugin, false)
1042
+ }
1043
+ },
1044
+ }).catch((error) => {
1045
+ fail("failed to load tui plugins", { directory: cwd, error })
1046
+ })
1047
+ }
1048
+ }