rird 2.1.231 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (381) hide show
  1. package/AGENTS.md +86 -0
  2. package/COMPLETED_TEST_SUITE.txt +280 -0
  3. package/Dockerfile +18 -0
  4. package/README.md +397 -6
  5. package/RIRD_ERROR_HANDLING_SUMMARY.md +307 -0
  6. package/TESTING.md +512 -0
  7. package/TEST_IMPLEMENTATION_REPORT.md +463 -0
  8. package/TEST_SUITE.md +307 -0
  9. package/TEST_SUMMARY.txt +380 -0
  10. package/bin/rird-perf.js +37 -0
  11. package/bin/rird.js +43 -8
  12. package/bunfig.toml +4 -0
  13. package/create-wrapper.ps1 +51 -0
  14. package/docs/ARCHITECTURE.md +768 -0
  15. package/docs/CLI_REFERENCE.md +681 -0
  16. package/docs/DOCUMENTATION_MANIFEST.md +392 -0
  17. package/docs/INDEX.md +295 -0
  18. package/docs/PRODUCTION_SETUP.md +633 -0
  19. package/docs/TROUBLESHOOTING.md +914 -0
  20. package/facebook_ads_library.png +0 -0
  21. package/nul +0 -0
  22. package/nul`nif +0 -0
  23. package/package.json +104 -15
  24. package/parsers-config.ts +239 -0
  25. package/rird-1.0.199.tgz +0 -0
  26. package/rird-1.0.205.tgz +0 -0
  27. package/script/build-windows.ts +56 -0
  28. package/script/build.ts +165 -0
  29. package/{postinstall.mjs → script/postinstall.mjs} +47 -68
  30. package/script/publish-registries.ts +187 -0
  31. package/script/publish.ts +85 -0
  32. package/script/schema.ts +47 -0
  33. package/src/acp/README.md +164 -0
  34. package/src/acp/agent.ts +1063 -0
  35. package/src/acp/session.ts +101 -0
  36. package/src/acp/types.ts +22 -0
  37. package/src/agent/agent.ts +367 -0
  38. package/src/agent/generate.txt +75 -0
  39. package/src/agent/prompt/compaction.txt +12 -0
  40. package/src/agent/prompt/explore.txt +18 -0
  41. package/src/agent/prompt/summary.txt +10 -0
  42. package/src/agent/prompt/title.txt +36 -0
  43. package/src/auth/index.ts +70 -0
  44. package/src/bun/index.ts +114 -0
  45. package/src/bus/bus-event.ts +43 -0
  46. package/src/bus/global.ts +10 -0
  47. package/src/bus/index.ts +105 -0
  48. package/src/cli/bootstrap.ts +17 -0
  49. package/src/cli/cmd/acp.ts +104 -0
  50. package/src/cli/cmd/activate.ts +50 -0
  51. package/src/cli/cmd/agent.ts +256 -0
  52. package/src/cli/cmd/auth.ts +412 -0
  53. package/src/cli/cmd/cmd.ts +7 -0
  54. package/src/cli/cmd/debug/config.ts +15 -0
  55. package/src/cli/cmd/debug/file.ts +91 -0
  56. package/src/cli/cmd/debug/index.ts +43 -0
  57. package/src/cli/cmd/debug/lsp.ts +48 -0
  58. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  59. package/src/cli/cmd/debug/scrap.ts +15 -0
  60. package/src/cli/cmd/debug/skill.ts +15 -0
  61. package/src/cli/cmd/debug/snapshot.ts +48 -0
  62. package/src/cli/cmd/export.ts +88 -0
  63. package/src/cli/cmd/generate.ts +38 -0
  64. package/src/cli/cmd/github.ts +1400 -0
  65. package/src/cli/cmd/import.ts +98 -0
  66. package/src/cli/cmd/mcp.ts +654 -0
  67. package/src/cli/cmd/models.ts +68 -0
  68. package/src/cli/cmd/pr.ts +112 -0
  69. package/src/cli/cmd/run.ts +434 -0
  70. package/src/cli/cmd/serve.ts +31 -0
  71. package/src/cli/cmd/session.ts +106 -0
  72. package/src/cli/cmd/stats.ts +298 -0
  73. package/src/cli/cmd/tui/app.tsx +694 -0
  74. package/src/cli/cmd/tui/attach.ts +30 -0
  75. package/src/cli/cmd/tui/component/border.tsx +21 -0
  76. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  77. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  78. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  79. package/src/cli/cmd/tui/component/dialog-model.tsx +236 -0
  80. package/src/cli/cmd/tui/component/dialog-provider.tsx +240 -0
  81. package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
  82. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  83. package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
  84. package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
  85. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  86. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  87. package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
  88. package/src/cli/cmd/tui/component/logo.tsx +48 -0
  89. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
  90. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  91. package/src/cli/cmd/tui/component/prompt/index.tsx +1087 -0
  92. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  93. package/src/cli/cmd/tui/component/tips.ts +27 -0
  94. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  95. package/src/cli/cmd/tui/context/args.tsx +14 -0
  96. package/src/cli/cmd/tui/context/directory.ts +13 -0
  97. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  98. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  99. package/src/cli/cmd/tui/context/keybind.tsx +101 -0
  100. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  101. package/src/cli/cmd/tui/context/local.tsx +345 -0
  102. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  103. package/src/cli/cmd/tui/context/route.tsx +46 -0
  104. package/src/cli/cmd/tui/context/sdk.tsx +74 -0
  105. package/src/cli/cmd/tui/context/sync.tsx +372 -0
  106. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  107. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  108. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  109. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  110. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  111. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  112. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  113. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  114. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  115. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  116. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  117. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  118. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  119. package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
  120. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  121. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  122. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  123. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  124. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  125. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  126. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  127. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  128. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  129. package/src/cli/cmd/tui/context/theme/rird.json +245 -0
  130. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  131. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  132. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  133. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  134. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  135. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  136. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  137. package/src/cli/cmd/tui/context/theme.tsx +1109 -0
  138. package/src/cli/cmd/tui/event.ts +40 -0
  139. package/src/cli/cmd/tui/hooks/use-safe-terminal-dimensions.ts +12 -0
  140. package/src/cli/cmd/tui/routes/home.tsx +138 -0
  141. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
  142. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
  143. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  144. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  145. package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
  146. package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
  147. package/src/cli/cmd/tui/routes/session/index.tsx +1876 -0
  148. package/src/cli/cmd/tui/routes/session/sidebar.tsx +320 -0
  149. package/src/cli/cmd/tui/spawn.ts +60 -0
  150. package/src/cli/cmd/tui/thread.ts +142 -0
  151. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  152. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  153. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  154. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
  155. package/src/cli/cmd/tui/ui/dialog-select.tsx +333 -0
  156. package/src/cli/cmd/tui/ui/dialog.tsx +171 -0
  157. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  158. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  159. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  160. package/src/cli/cmd/tui/util/editor.ts +32 -0
  161. package/src/cli/cmd/tui/util/terminal.ts +146 -0
  162. package/src/cli/cmd/tui/worker.ts +63 -0
  163. package/src/cli/cmd/uninstall.ts +344 -0
  164. package/src/cli/cmd/upgrade.ts +127 -0
  165. package/src/cli/cmd/web.ts +84 -0
  166. package/src/cli/error.ts +69 -0
  167. package/src/cli/ui.ts +101 -0
  168. package/src/cli/upgrade.ts +28 -0
  169. package/src/command/index.ts +80 -0
  170. package/src/command/template/initialize.txt +10 -0
  171. package/src/command/template/review.txt +97 -0
  172. package/src/config/config.ts +994 -0
  173. package/src/config/markdown.ts +41 -0
  174. package/src/env/index.ts +26 -0
  175. package/src/file/ignore.ts +83 -0
  176. package/src/file/index.ts +328 -0
  177. package/src/file/ripgrep.ts +393 -0
  178. package/src/file/time.ts +64 -0
  179. package/src/file/watcher.ts +103 -0
  180. package/src/flag/flag.ts +84 -0
  181. package/src/format/formatter.ts +315 -0
  182. package/src/format/index.ts +137 -0
  183. package/src/global/index.ts +101 -0
  184. package/src/id/id.ts +73 -0
  185. package/src/ide/index.ts +76 -0
  186. package/src/index.ts +297 -0
  187. package/src/index.ts.backup +271 -0
  188. package/src/installation/index.ts +258 -0
  189. package/src/lib/IMPLEMENTATION_NOTES.md +345 -0
  190. package/src/lib/error-handler.ts +225 -0
  191. package/src/lib/error-testing-guide.md +258 -0
  192. package/src/lib/errors.ts +285 -0
  193. package/src/lib/performance.ts +70 -0
  194. package/src/lib/telemetry.ts +282 -0
  195. package/src/lsp/client.ts +229 -0
  196. package/src/lsp/index.ts +485 -0
  197. package/src/lsp/language.ts +116 -0
  198. package/src/lsp/server.ts +1895 -0
  199. package/src/mcp/auth.ts +135 -0
  200. package/src/mcp/index.ts +1117 -0
  201. package/src/mcp/intent-analyzer.ts +376 -0
  202. package/src/mcp/oauth-callback.ts +200 -0
  203. package/src/mcp/oauth-provider.ts +154 -0
  204. package/src/patch/index.ts +632 -0
  205. package/src/permission/index.ts +199 -0
  206. package/src/plugin/index.ts +91 -0
  207. package/src/project/bootstrap.ts +33 -0
  208. package/src/project/instance.ts +78 -0
  209. package/src/project/project.ts +236 -0
  210. package/src/project/state.ts +65 -0
  211. package/src/project/vcs.ts +76 -0
  212. package/src/provider/auth.ts +143 -0
  213. package/src/provider/models-macro.ts +55 -0
  214. package/src/provider/models.ts +161 -0
  215. package/src/provider/provider.ts +1109 -0
  216. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  217. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  218. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  219. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  220. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  221. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  222. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  223. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  224. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  225. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  226. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  227. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  228. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  229. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  230. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  231. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  232. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  233. package/src/provider/transform.ts +455 -0
  234. package/src/pty/index.ts +231 -0
  235. package/src/security/guardrails.test.ts +341 -0
  236. package/src/security/guardrails.ts +570 -0
  237. package/src/security/index.ts +19 -0
  238. package/src/server/error.ts +36 -0
  239. package/src/server/project.ts +79 -0
  240. package/src/server/server.ts +2641 -0
  241. package/src/server/tui.ts +71 -0
  242. package/src/session/compaction.ts +228 -0
  243. package/src/session/index.ts +464 -0
  244. package/src/session/llm.ts +201 -0
  245. package/src/session/message-v2.ts +695 -0
  246. package/src/session/message.ts +189 -0
  247. package/src/session/processor.ts +409 -0
  248. package/src/session/prompt/act-switch.txt +5 -0
  249. package/src/session/prompt/anthropic-20250930.txt +166 -0
  250. package/src/session/prompt/anthropic.txt +63 -0
  251. package/src/session/prompt/anthropic_spoof.txt +1 -0
  252. package/src/session/prompt/beast.txt +76 -0
  253. package/src/session/prompt/codex.txt +304 -0
  254. package/src/session/prompt/copilot-gpt-5.txt +137 -0
  255. package/src/session/prompt/gemini.txt +62 -0
  256. package/src/session/prompt/max-steps.txt +16 -0
  257. package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
  258. package/src/session/prompt/plan.txt +24 -0
  259. package/src/session/prompt/polaris.txt +88 -0
  260. package/src/session/prompt/qwen.txt +59 -0
  261. package/src/session/prompt.ts +1552 -0
  262. package/src/session/retry.ts +86 -0
  263. package/src/session/revert.ts +108 -0
  264. package/src/session/sensitive-filter.test.ts +327 -0
  265. package/src/session/sensitive-filter.ts +466 -0
  266. package/src/session/status.ts +76 -0
  267. package/src/session/summary.ts +209 -0
  268. package/src/session/system.ts +122 -0
  269. package/src/session/todo.ts +37 -0
  270. package/src/share/share-next.ts +222 -0
  271. package/src/share/share.ts +87 -0
  272. package/src/shell/shell.ts +67 -0
  273. package/src/skill/index.ts +1 -0
  274. package/src/skill/skill.ts +83 -0
  275. package/src/snapshot/index.ts +197 -0
  276. package/src/storage/storage.ts +226 -0
  277. package/src/tests/agent.test.ts +308 -0
  278. package/src/tests/build-guards.test.ts +267 -0
  279. package/src/tests/config.test.ts +664 -0
  280. package/src/tests/tool-registry.test.ts +589 -0
  281. package/src/tool/bash.ts +314 -0
  282. package/src/tool/bash.txt +158 -0
  283. package/src/tool/batch.ts +175 -0
  284. package/src/tool/batch.txt +24 -0
  285. package/src/tool/codesearch.ts +184 -0
  286. package/src/tool/codesearch.txt +12 -0
  287. package/src/tool/edit.ts +675 -0
  288. package/src/tool/edit.txt +10 -0
  289. package/src/tool/glob.ts +65 -0
  290. package/src/tool/glob.txt +6 -0
  291. package/src/tool/grep.ts +121 -0
  292. package/src/tool/grep.txt +8 -0
  293. package/src/tool/invalid.ts +17 -0
  294. package/src/tool/ls.ts +110 -0
  295. package/src/tool/ls.txt +1 -0
  296. package/src/tool/lsp-diagnostics.ts +26 -0
  297. package/src/tool/lsp-diagnostics.txt +1 -0
  298. package/src/tool/lsp-hover.ts +31 -0
  299. package/src/tool/lsp-hover.txt +1 -0
  300. package/src/tool/lsp.ts +87 -0
  301. package/src/tool/lsp.txt +19 -0
  302. package/src/tool/multiedit.ts +46 -0
  303. package/src/tool/multiedit.txt +41 -0
  304. package/src/tool/patch.ts +233 -0
  305. package/src/tool/patch.txt +1 -0
  306. package/src/tool/read.ts +219 -0
  307. package/src/tool/read.txt +12 -0
  308. package/src/tool/registry.ts +162 -0
  309. package/src/tool/skill.ts +100 -0
  310. package/src/tool/task.ts +136 -0
  311. package/src/tool/task.txt +51 -0
  312. package/src/tool/todo.ts +39 -0
  313. package/src/tool/todoread.txt +14 -0
  314. package/src/tool/todowrite.txt +167 -0
  315. package/src/tool/tool.ts +71 -0
  316. package/src/tool/webfetch.ts +198 -0
  317. package/src/tool/webfetch.txt +13 -0
  318. package/src/tool/websearch.ts +268 -0
  319. package/src/tool/websearch.txt +13 -0
  320. package/src/tool/write.ts +110 -0
  321. package/src/tool/write.txt +8 -0
  322. package/src/util/archive.ts +16 -0
  323. package/src/util/color.ts +19 -0
  324. package/src/util/context.ts +25 -0
  325. package/src/util/defer.ts +12 -0
  326. package/src/util/eventloop.ts +20 -0
  327. package/src/util/filesystem.ts +83 -0
  328. package/src/util/fn.ts +11 -0
  329. package/src/util/iife.ts +3 -0
  330. package/src/util/keybind.ts +102 -0
  331. package/src/util/lazy.ts +11 -0
  332. package/src/util/license.ts +362 -0
  333. package/src/util/locale.ts +81 -0
  334. package/src/util/lock.ts +98 -0
  335. package/src/util/log.ts +180 -0
  336. package/src/util/queue.ts +32 -0
  337. package/src/util/rpc.ts +42 -0
  338. package/src/util/scrap.ts +10 -0
  339. package/src/util/signal.ts +12 -0
  340. package/src/util/timeout.ts +14 -0
  341. package/src/util/token.ts +7 -0
  342. package/src/util/wildcard.ts +54 -0
  343. package/sst-env.d.ts +9 -0
  344. package/test/agent/agent.test.ts +146 -0
  345. package/test/bun.test.ts +53 -0
  346. package/test/cli/cmd/acp.test.ts +144 -0
  347. package/test/cli/cmd/run.test.ts +250 -0
  348. package/test/cli/github-remote.test.ts +80 -0
  349. package/test/config/agent-color.test.ts +66 -0
  350. package/test/config/config.test.ts +536 -0
  351. package/test/config/markdown.test.ts +89 -0
  352. package/test/file/ignore.test.ts +10 -0
  353. package/test/fixture/fixture.ts +37 -0
  354. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  355. package/test/helpers.ts +172 -0
  356. package/test/ide/ide.test.ts +82 -0
  357. package/test/installation/installation.test.ts +143 -0
  358. package/test/keybind.test.ts +421 -0
  359. package/test/lsp/client.test.ts +95 -0
  360. package/test/mcp/headers.test.ts +153 -0
  361. package/test/patch/patch.test.ts +348 -0
  362. package/test/preload.ts +57 -0
  363. package/test/project/project.test.ts +74 -0
  364. package/test/provider/provider.test.ts +74 -0
  365. package/test/provider/transform.test.ts +411 -0
  366. package/test/session/retry.test.ts +111 -0
  367. package/test/session/session.test.ts +71 -0
  368. package/test/skill/skill.test.ts +131 -0
  369. package/test/snapshot/snapshot.test.ts +940 -0
  370. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  371. package/test/tool/bash.test.ts +434 -0
  372. package/test/tool/grep.test.ts +108 -0
  373. package/test/tool/patch.test.ts +259 -0
  374. package/test/tool/read.test.ts +42 -0
  375. package/test/util/iife.test.ts +36 -0
  376. package/test/util/lazy.test.ts +50 -0
  377. package/test/util/license.test.ts +235 -0
  378. package/test/util/timeout.test.ts +21 -0
  379. package/test/util/wildcard.test.ts +55 -0
  380. package/tsconfig.json +16 -0
  381. package/update-versions.ps1 +65 -0
@@ -0,0 +1,240 @@
1
+ import { createMemo, createSignal, onMount, Show } from "solid-js"
2
+ import { useSync } from "@tui/context/sync"
3
+ import { map, pipe, sortBy } from "remeda"
4
+ import { DialogSelect } from "@tui/ui/dialog-select"
5
+ import { useDialog } from "@tui/ui/dialog"
6
+ import { useSDK } from "../context/sdk"
7
+ import { DialogPrompt } from "../ui/dialog-prompt"
8
+ import { useTheme } from "../context/theme"
9
+ import { TextAttributes } from "@opentui/core"
10
+ import type { ProviderAuthAuthorization } from "@opencode-ai/sdk/v2"
11
+ import { DialogModel } from "./dialog-model"
12
+
13
+ const PROVIDER_PRIORITY: Record<string, number> = {
14
+ rird: 0,
15
+ }
16
+
17
+ export function createDialogProviderOptions() {
18
+ const sync = useSync()
19
+ const dialog = useDialog()
20
+ const sdk = useSDK()
21
+ const options = createMemo(() => {
22
+ return pipe(
23
+ sync.data.provider_next.all.filter((provider) => provider.id === "rird"),
24
+ sortBy((x) => PROVIDER_PRIORITY[x.id] ?? 99),
25
+ map((provider) => ({
26
+ title: "RIRD",
27
+ value: provider.id,
28
+ description: {
29
+ rird: "(Recommended)",
30
+ }[provider.id],
31
+ category: provider.id in PROVIDER_PRIORITY ? "Popular" : "Other",
32
+ async onSelect() {
33
+ // RIRD uses the license key from `rird activate` for auth; no external providers.
34
+ const methods = sync.data.provider_auth[provider.id] ?? []
35
+ if (methods.length === 0) {
36
+ dialog.replace(
37
+ <DialogSelect
38
+ title="RIRD Authentication"
39
+ options={[
40
+ {
41
+ title: "Use your license key",
42
+ value: "license",
43
+ async onSelect() {
44
+ dialog.replace(
45
+ <DialogPrompt
46
+ title="Activate RIRD"
47
+ description={() => (
48
+ <text>Run `rird activate YOUR_LICENSE_KEY` in your terminal.</text>
49
+ )}
50
+ placeholder="Press enter to close"
51
+ onConfirm={() => dialog.clear()}
52
+ />
53
+ )
54
+ },
55
+ },
56
+ ]}
57
+ />
58
+ )
59
+ return
60
+ }
61
+ let index: number | null = 0
62
+ if (methods.length > 1) {
63
+ index = await new Promise<number | null>((resolve) => {
64
+ dialog.replace(
65
+ () => (
66
+ <DialogSelect
67
+ title="Select auth method"
68
+ options={methods.map((x, index) => ({
69
+ title: x.label,
70
+ value: index,
71
+ }))}
72
+ onSelect={(option) => resolve(option.value)}
73
+ />
74
+ ),
75
+ () => resolve(null),
76
+ )
77
+ })
78
+ }
79
+ if (index == null) return
80
+ const method = methods[index]
81
+ if (method.type === "oauth") {
82
+ const result = await sdk.client.provider.oauth.authorize({
83
+ providerID: provider.id,
84
+ method: index,
85
+ })
86
+ if (result.data?.method === "code") {
87
+ dialog.replace(() => (
88
+ <CodeMethod providerID={provider.id} title={method.label} index={index} authorization={result.data!} />
89
+ ))
90
+ }
91
+ if (result.data?.method === "auto") {
92
+ dialog.replace(() => (
93
+ <AutoMethod providerID={provider.id} title={method.label} index={index} authorization={result.data!} />
94
+ ))
95
+ }
96
+ }
97
+ if (method.type === "api") {
98
+ return dialog.replace(() => <ApiMethod providerID={provider.id} title={method.label} />)
99
+ }
100
+ },
101
+ })),
102
+ )
103
+ })
104
+ return options
105
+ }
106
+
107
+ export function DialogProvider() {
108
+ const options = createDialogProviderOptions()
109
+ return <DialogSelect title="Connect RIRD" options={options()} />
110
+ }
111
+
112
+ interface AutoMethodProps {
113
+ index: number
114
+ providerID: string
115
+ title: string
116
+ authorization: ProviderAuthAuthorization
117
+ }
118
+ function AutoMethod(props: AutoMethodProps) {
119
+ const { theme } = useTheme()
120
+ const sdk = useSDK()
121
+ const dialog = useDialog()
122
+ const sync = useSync()
123
+
124
+ onMount(async () => {
125
+ const result = await sdk.client.provider.oauth.callback({
126
+ providerID: props.providerID,
127
+ method: props.index,
128
+ })
129
+ if (result.error) {
130
+ dialog.clear()
131
+ return
132
+ }
133
+ await sdk.client.instance.dispose()
134
+ await sync.bootstrap()
135
+ dialog.replace(() => <DialogModel providerID={props.providerID} />)
136
+ })
137
+
138
+ return (
139
+ <box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
140
+ <box flexDirection="row" justifyContent="space-between">
141
+ <text attributes={TextAttributes.BOLD} fg={theme.text}>
142
+ {props.title}
143
+ </text>
144
+ <text fg={theme.textMuted}>esc</text>
145
+ </box>
146
+ <box gap={1}>
147
+ <text fg={theme.primary}>{props.authorization.url}</text>
148
+ <text fg={theme.textMuted}>{props.authorization.instructions}</text>
149
+ </box>
150
+ <text fg={theme.textMuted}>Waiting for authorization...</text>
151
+ </box>
152
+ )
153
+ }
154
+
155
+ interface CodeMethodProps {
156
+ index: number
157
+ title: string
158
+ providerID: string
159
+ authorization: ProviderAuthAuthorization
160
+ }
161
+ function CodeMethod(props: CodeMethodProps) {
162
+ const { theme } = useTheme()
163
+ const sdk = useSDK()
164
+ const sync = useSync()
165
+ const dialog = useDialog()
166
+ const [error, setError] = createSignal(false)
167
+
168
+ return (
169
+ <DialogPrompt
170
+ title={props.title}
171
+ placeholder="Authorization code"
172
+ onConfirm={async (value) => {
173
+ const { error } = await sdk.client.provider.oauth.callback({
174
+ providerID: props.providerID,
175
+ method: props.index,
176
+ code: value,
177
+ })
178
+ if (!error) {
179
+ await sdk.client.instance.dispose()
180
+ await sync.bootstrap()
181
+ dialog.replace(() => <DialogModel providerID={props.providerID} />)
182
+ return
183
+ }
184
+ setError(true)
185
+ }}
186
+ description={() => (
187
+ <box gap={1}>
188
+ <text fg={theme.textMuted}>{props.authorization.instructions}</text>
189
+ <text fg={theme.primary}>{props.authorization.url}</text>
190
+ <Show when={error()}>
191
+ <text fg={theme.error}>Invalid code</text>
192
+ </Show>
193
+ </box>
194
+ )}
195
+ />
196
+ )
197
+ }
198
+
199
+ interface ApiMethodProps {
200
+ providerID: string
201
+ title: string
202
+ }
203
+ function ApiMethod(props: ApiMethodProps) {
204
+ const dialog = useDialog()
205
+ const sdk = useSDK()
206
+ const sync = useSync()
207
+ const { theme } = useTheme()
208
+
209
+ return (
210
+ <DialogPrompt
211
+ title={props.title}
212
+ placeholder="API key"
213
+ description={
214
+ props.providerID === "rird" ? (
215
+ <box gap={1}>
216
+ <text fg={theme.textMuted}>
217
+ Access all AI models with a single API key.
218
+ </text>
219
+ <text fg={theme.text}>
220
+ Go to <span style={{ fg: theme.primary }}>https://rird.ai</span> to get a key
221
+ </text>
222
+ </box>
223
+ ) : undefined
224
+ }
225
+ onConfirm={async (value) => {
226
+ if (!value) return
227
+ sdk.client.auth.set({
228
+ providerID: props.providerID,
229
+ auth: {
230
+ type: "api",
231
+ key: value,
232
+ },
233
+ })
234
+ await sdk.client.instance.dispose()
235
+ await sync.bootstrap()
236
+ dialog.replace(() => <DialogModel providerID={props.providerID} />)
237
+ }}
238
+ />
239
+ )
240
+ }
@@ -0,0 +1,102 @@
1
+ import { useDialog } from "@tui/ui/dialog"
2
+ import { DialogSelect } from "@tui/ui/dialog-select"
3
+ import { useRoute } from "@tui/context/route"
4
+ import { useSync } from "@tui/context/sync"
5
+ import { createEffect, createMemo, createSignal, onMount } from "solid-js"
6
+ import { Locale } from "@/util/locale"
7
+ import { Keybind } from "@/util/keybind"
8
+ import { useTheme } from "../context/theme"
9
+ import { useSDK } from "../context/sdk"
10
+ import { DialogSessionRename } from "./dialog-session-rename"
11
+ import "opentui-spinner/solid"
12
+
13
+ export function DialogSessionList() {
14
+ const dialog = useDialog()
15
+ const sync = useSync()
16
+ const { theme } = useTheme()
17
+ const route = useRoute()
18
+ const sdk = useSDK()
19
+
20
+ const [toDelete, setToDelete] = createSignal<string>()
21
+
22
+ const deleteKeybind = "ctrl+d"
23
+
24
+ const currentSessionID = createMemo(() => (route.data.type === "session" ? route.data.sessionID : undefined))
25
+
26
+ const spinnerFrames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
27
+
28
+ const options = createMemo(() => {
29
+ const today = new Date().toDateString()
30
+ return sync.data.session
31
+ .filter((x) => x.parentID === undefined)
32
+ .toSorted((a, b) => b.time.updated - a.time.updated)
33
+ .map((x) => {
34
+ const date = new Date(x.time.updated)
35
+ let category = date.toDateString()
36
+ if (category === today) {
37
+ category = "Today"
38
+ }
39
+ const isDeleting = toDelete() === x.id
40
+ const status = sync.data.session_status?.[x.id]
41
+ const isWorking = status?.type === "busy"
42
+ return {
43
+ title: isDeleting ? `Press ${deleteKeybind} again to confirm` : x.title,
44
+ bg: isDeleting ? theme.error : undefined,
45
+ value: x.id,
46
+ category,
47
+ footer: Locale.time(x.time.updated),
48
+ gutter: isWorking ? <spinner frames={spinnerFrames} interval={80} color={theme.primary} /> : undefined,
49
+ }
50
+ })
51
+ .slice(0, 150)
52
+ })
53
+
54
+ createEffect(() => {
55
+ console.log("session count", sync.data.session.length)
56
+ })
57
+
58
+ onMount(() => {
59
+ dialog.setSize("large")
60
+ })
61
+
62
+ return (
63
+ <DialogSelect
64
+ title="Sessions"
65
+ options={options()}
66
+ current={currentSessionID()}
67
+ onMove={() => {
68
+ setToDelete(undefined)
69
+ }}
70
+ onSelect={(option) => {
71
+ route.navigate({
72
+ type: "session",
73
+ sessionID: option.value,
74
+ })
75
+ dialog.clear()
76
+ }}
77
+ keybind={[
78
+ {
79
+ keybind: Keybind.parse(deleteKeybind)[0],
80
+ title: "delete",
81
+ onTrigger: async (option) => {
82
+ if (toDelete() === option.value) {
83
+ sdk.client.session.delete({
84
+ sessionID: option.value,
85
+ })
86
+ setToDelete(undefined)
87
+ return
88
+ }
89
+ setToDelete(option.value)
90
+ },
91
+ },
92
+ {
93
+ keybind: Keybind.parse("ctrl+r")[0],
94
+ title: "rename",
95
+ onTrigger: async (option) => {
96
+ dialog.replace(() => <DialogSessionRename session={option.value} />)
97
+ },
98
+ },
99
+ ]}
100
+ />
101
+ )
102
+ }
@@ -0,0 +1,31 @@
1
+ import { DialogPrompt } from "@tui/ui/dialog-prompt"
2
+ import { useDialog } from "@tui/ui/dialog"
3
+ import { useSync } from "@tui/context/sync"
4
+ import { createMemo } from "solid-js"
5
+ import { useSDK } from "../context/sdk"
6
+
7
+ interface DialogSessionRenameProps {
8
+ session: string
9
+ }
10
+
11
+ export function DialogSessionRename(props: DialogSessionRenameProps) {
12
+ const dialog = useDialog()
13
+ const sync = useSync()
14
+ const sdk = useSDK()
15
+ const session = createMemo(() => sync.session.get(props.session))
16
+
17
+ return (
18
+ <DialogPrompt
19
+ title="Rename Session"
20
+ value={session()?.title}
21
+ onConfirm={(value) => {
22
+ sdk.client.session.update({
23
+ sessionID: props.session,
24
+ title: value,
25
+ })
26
+ dialog.clear()
27
+ }}
28
+ onCancel={() => dialog.clear()}
29
+ />
30
+ )
31
+ }
@@ -0,0 +1,86 @@
1
+ import { useDialog } from "@tui/ui/dialog"
2
+ import { DialogSelect } from "@tui/ui/dialog-select"
3
+ import { createMemo, createSignal } from "solid-js"
4
+ import { Locale } from "@/util/locale"
5
+ import { Keybind } from "@/util/keybind"
6
+ import { useTheme } from "../context/theme"
7
+ import { usePromptStash, type StashEntry } from "./prompt/stash"
8
+
9
+ function getRelativeTime(timestamp: number): string {
10
+ const now = Date.now()
11
+ const diff = now - timestamp
12
+ const seconds = Math.floor(diff / 1000)
13
+ const minutes = Math.floor(seconds / 60)
14
+ const hours = Math.floor(minutes / 60)
15
+ const days = Math.floor(hours / 24)
16
+
17
+ if (seconds < 60) return "just now"
18
+ if (minutes < 60) return `${minutes}m ago`
19
+ if (hours < 24) return `${hours}h ago`
20
+ if (days < 7) return `${days}d ago`
21
+ return Locale.datetime(timestamp)
22
+ }
23
+
24
+ function getStashPreview(input: string, maxLength: number = 50): string {
25
+ const firstLine = input.split("\n")[0].trim()
26
+ return Locale.truncate(firstLine, maxLength)
27
+ }
28
+
29
+ export function DialogStash(props: { onSelect: (entry: StashEntry) => void }) {
30
+ const dialog = useDialog()
31
+ const stash = usePromptStash()
32
+ const { theme } = useTheme()
33
+
34
+ const [toDelete, setToDelete] = createSignal<number>()
35
+
36
+ const options = createMemo(() => {
37
+ const entries = stash.list()
38
+ // Show most recent first
39
+ return entries
40
+ .map((entry, index) => {
41
+ const isDeleting = toDelete() === index
42
+ const lineCount = (entry.input.match(/\n/g)?.length ?? 0) + 1
43
+ return {
44
+ title: isDeleting ? "Press ctrl+d again to confirm" : getStashPreview(entry.input),
45
+ bg: isDeleting ? theme.error : undefined,
46
+ value: index,
47
+ description: getRelativeTime(entry.timestamp),
48
+ footer: lineCount > 1 ? `~${lineCount} lines` : undefined,
49
+ }
50
+ })
51
+ .toReversed()
52
+ })
53
+
54
+ return (
55
+ <DialogSelect
56
+ title="Stash"
57
+ options={options()}
58
+ onMove={() => {
59
+ setToDelete(undefined)
60
+ }}
61
+ onSelect={(option) => {
62
+ const entries = stash.list()
63
+ const entry = entries[option.value]
64
+ if (entry) {
65
+ stash.remove(option.value)
66
+ props.onSelect(entry)
67
+ }
68
+ dialog.clear()
69
+ }}
70
+ keybind={[
71
+ {
72
+ keybind: Keybind.parse("ctrl+d")[0],
73
+ title: "delete",
74
+ onTrigger: (option) => {
75
+ if (toDelete() === option.value) {
76
+ stash.remove(option.value)
77
+ setToDelete(undefined)
78
+ return
79
+ }
80
+ setToDelete(option.value)
81
+ },
82
+ },
83
+ ]}
84
+ />
85
+ )
86
+ }
@@ -0,0 +1,162 @@
1
+ import { TextAttributes } from "@opentui/core"
2
+ import { useTheme } from "../context/theme"
3
+ import { useSync } from "@tui/context/sync"
4
+ import { For, Match, Switch, Show, createMemo } from "solid-js"
5
+
6
+ export type DialogStatusProps = {}
7
+
8
+ export function DialogStatus() {
9
+ const sync = useSync()
10
+ const { theme } = useTheme()
11
+
12
+ const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled))
13
+
14
+ const plugins = createMemo(() => {
15
+ const list = sync.data.config.plugin ?? []
16
+ const result = list.map((value) => {
17
+ if (value.startsWith("file://")) {
18
+ const path = value.substring("file://".length)
19
+ const parts = path.split("/")
20
+ const filename = parts.pop() || path
21
+ if (!filename.includes(".")) return { name: filename }
22
+ const basename = filename.split(".")[0]
23
+ if (basename === "index") {
24
+ const dirname = parts.pop()
25
+ const name = dirname || basename
26
+ return { name }
27
+ }
28
+ return { name: basename }
29
+ }
30
+ const index = value.lastIndexOf("@")
31
+ if (index <= 0) return { name: value, version: "latest" }
32
+ const name = value.substring(0, index)
33
+ const version = value.substring(index + 1)
34
+ return { name, version }
35
+ })
36
+ return result.toSorted((a, b) => a.name.localeCompare(b.name))
37
+ })
38
+
39
+ return (
40
+ <box paddingLeft={2} paddingRight={2} gap={1} paddingBottom={1}>
41
+ <box flexDirection="row" justifyContent="space-between">
42
+ <text fg={theme.text} attributes={TextAttributes.BOLD}>
43
+ Status
44
+ </text>
45
+ <text fg={theme.textMuted}>esc</text>
46
+ </box>
47
+ <Show when={Object.keys(sync.data.mcp).length > 0} fallback={<text fg={theme.text}>No MCP Servers</text>}>
48
+ <box>
49
+ <text fg={theme.text}>{Object.keys(sync.data.mcp).length} MCP Servers</text>
50
+ <For each={Object.entries(sync.data.mcp)}>
51
+ {([key, item]) => (
52
+ <box flexDirection="row" gap={1}>
53
+ <text
54
+ flexShrink={0}
55
+ style={{
56
+ fg: (
57
+ {
58
+ connected: theme.success,
59
+ failed: theme.error,
60
+ disabled: theme.textMuted,
61
+ needs_auth: theme.warning,
62
+ needs_client_registration: theme.error,
63
+ } as Record<string, typeof theme.success>
64
+ )[item.status],
65
+ }}
66
+ >
67
+
68
+ </text>
69
+ <text fg={theme.text} wrapMode="word">
70
+ <b>{key}</b>{" "}
71
+ <span style={{ fg: theme.textMuted }}>
72
+ <Switch fallback={item.status}>
73
+ <Match when={item.status === "connected"}>Connected</Match>
74
+ <Match when={item.status === "failed" && item}>{(val) => val().error}</Match>
75
+ <Match when={item.status === "disabled"}>Disabled in configuration</Match>
76
+ <Match when={(item.status as string) === "needs_auth"}>
77
+ Needs authentication (run: rird mcp auth {key})
78
+ </Match>
79
+ <Match when={(item.status as string) === "needs_client_registration" && item}>
80
+ {(val) => (val() as { error: string }).error}
81
+ </Match>
82
+ </Switch>
83
+ </span>
84
+ </text>
85
+ </box>
86
+ )}
87
+ </For>
88
+ </box>
89
+ </Show>
90
+ {sync.data.lsp.length > 0 && (
91
+ <box>
92
+ <text fg={theme.text}>{sync.data.lsp.length} LSP Servers</text>
93
+ <For each={sync.data.lsp}>
94
+ {(item) => (
95
+ <box flexDirection="row" gap={1}>
96
+ <text
97
+ flexShrink={0}
98
+ style={{
99
+ fg: {
100
+ connected: theme.success,
101
+ error: theme.error,
102
+ }[item.status],
103
+ }}
104
+ >
105
+
106
+ </text>
107
+ <text fg={theme.text} wrapMode="word">
108
+ <b>{item.id}</b> <span style={{ fg: theme.textMuted }}>{item.root}</span>
109
+ </text>
110
+ </box>
111
+ )}
112
+ </For>
113
+ </box>
114
+ )}
115
+ <Show when={enabledFormatters().length > 0} fallback={<text fg={theme.text}>No Formatters</text>}>
116
+ <box>
117
+ <text fg={theme.text}>{enabledFormatters().length} Formatters</text>
118
+ <For each={enabledFormatters()}>
119
+ {(item) => (
120
+ <box flexDirection="row" gap={1}>
121
+ <text
122
+ flexShrink={0}
123
+ style={{
124
+ fg: theme.success,
125
+ }}
126
+ >
127
+
128
+ </text>
129
+ <text wrapMode="word" fg={theme.text}>
130
+ <b>{item.name}</b>
131
+ </text>
132
+ </box>
133
+ )}
134
+ </For>
135
+ </box>
136
+ </Show>
137
+ <Show when={plugins().length > 0} fallback={<text fg={theme.text}>No Plugins</text>}>
138
+ <box>
139
+ <text fg={theme.text}>{plugins().length} Plugins</text>
140
+ <For each={plugins()}>
141
+ {(item) => (
142
+ <box flexDirection="row" gap={1}>
143
+ <text
144
+ flexShrink={0}
145
+ style={{
146
+ fg: theme.success,
147
+ }}
148
+ >
149
+
150
+ </text>
151
+ <text wrapMode="word" fg={theme.text}>
152
+ <b>{item.name}</b>
153
+ {item.version && <span style={{ fg: theme.textMuted }}> @{item.version}</span>}
154
+ </text>
155
+ </box>
156
+ )}
157
+ </For>
158
+ </box>
159
+ </Show>
160
+ </box>
161
+ )
162
+ }
@@ -0,0 +1,44 @@
1
+ import { createMemo, createResource } from "solid-js"
2
+ import { DialogSelect } from "@tui/ui/dialog-select"
3
+ import { useDialog } from "@tui/ui/dialog"
4
+ import { useSDK } from "@tui/context/sdk"
5
+ import { createStore } from "solid-js/store"
6
+
7
+ export function DialogTag(props: { onSelect?: (value: string) => void }) {
8
+ const sdk = useSDK()
9
+ const dialog = useDialog()
10
+
11
+ const [store] = createStore({
12
+ filter: "",
13
+ })
14
+
15
+ const [files] = createResource(
16
+ () => [store.filter],
17
+ async () => {
18
+ const result = await sdk.client.find.files({
19
+ query: store.filter,
20
+ })
21
+ if (result.error) return []
22
+ const sliced = (result.data ?? []).slice(0, 5)
23
+ return sliced
24
+ },
25
+ )
26
+
27
+ const options = createMemo(() =>
28
+ (files() ?? []).map((file) => ({
29
+ value: file,
30
+ title: file,
31
+ })),
32
+ )
33
+
34
+ return (
35
+ <DialogSelect
36
+ title="Autocomplete"
37
+ options={options()}
38
+ onSelect={(option) => {
39
+ props.onSelect?.(option.value)
40
+ dialog.clear()
41
+ }}
42
+ />
43
+ )
44
+ }