cerebras-cli 1.0.5 → 1.0.138

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 (322) hide show
  1. package/AGENTS.md +27 -0
  2. package/Dockerfile +10 -0
  3. package/README.md +5 -3
  4. package/bin/{opencode.cjs → opencode} +4 -4
  5. package/bunfig.toml +4 -0
  6. package/package.json +89 -32
  7. package/parsers-config.ts +239 -0
  8. package/script/build.ts +151 -0
  9. package/script/postinstall.mjs +122 -0
  10. package/script/publish.ts +256 -0
  11. package/script/schema.ts +47 -0
  12. package/snake_game.py +111 -0
  13. package/src/acp/README.md +164 -0
  14. package/src/acp/agent.ts +812 -0
  15. package/src/acp/session.ts +70 -0
  16. package/src/acp/types.ts +22 -0
  17. package/src/agent/agent.ts +310 -0
  18. package/src/agent/generate.txt +75 -0
  19. package/src/auth/index.ts +70 -0
  20. package/src/bun/index.ts +152 -0
  21. package/src/bus/global.ts +10 -0
  22. package/src/bus/index.ts +142 -0
  23. package/src/cli/bootstrap.ts +17 -0
  24. package/src/cli/cmd/acp.ts +88 -0
  25. package/src/cli/cmd/agent.ts +165 -0
  26. package/src/cli/cmd/auth.ts +369 -0
  27. package/src/cli/cmd/cmd.ts +7 -0
  28. package/src/cli/cmd/debug/config.ts +15 -0
  29. package/src/cli/cmd/debug/file.ts +91 -0
  30. package/src/cli/cmd/debug/index.ts +43 -0
  31. package/src/cli/cmd/debug/lsp.ts +47 -0
  32. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  33. package/src/cli/cmd/debug/scrap.ts +15 -0
  34. package/src/cli/cmd/debug/skill.ts +36 -0
  35. package/src/cli/cmd/debug/snapshot.ts +48 -0
  36. package/src/cli/cmd/export.ts +88 -0
  37. package/src/cli/cmd/generate.ts +38 -0
  38. package/src/cli/cmd/github.ts +1200 -0
  39. package/src/cli/cmd/import.ts +98 -0
  40. package/src/cli/cmd/mcp.ts +400 -0
  41. package/src/cli/cmd/models.ts +77 -0
  42. package/src/cli/cmd/pr.ts +112 -0
  43. package/src/cli/cmd/run.ts +342 -0
  44. package/src/cli/cmd/serve.ts +31 -0
  45. package/src/cli/cmd/session.ts +106 -0
  46. package/src/cli/cmd/stats.ts +298 -0
  47. package/src/cli/cmd/tui/app.tsx +833 -0
  48. package/src/cli/cmd/tui/attach.ts +25 -0
  49. package/src/cli/cmd/tui/component/border.tsx +21 -0
  50. package/src/cli/cmd/tui/component/cerebras-onboarding.tsx +225 -0
  51. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  52. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  53. package/src/cli/cmd/tui/component/dialog-feedback.tsx +160 -0
  54. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  55. package/src/cli/cmd/tui/component/dialog-model.tsx +223 -0
  56. package/src/cli/cmd/tui/component/dialog-notification.tsx +78 -0
  57. package/src/cli/cmd/tui/component/dialog-provider.tsx +222 -0
  58. package/src/cli/cmd/tui/component/dialog-session-list.tsx +97 -0
  59. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  60. package/src/cli/cmd/tui/component/dialog-status.tsx +114 -0
  61. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  62. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  63. package/src/cli/cmd/tui/component/logo.tsx +43 -0
  64. package/src/cli/cmd/tui/component/notification-banner.tsx +58 -0
  65. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +530 -0
  66. package/src/cli/cmd/tui/component/prompt/history.tsx +107 -0
  67. package/src/cli/cmd/tui/component/prompt/index.tsx +931 -0
  68. package/src/cli/cmd/tui/component/quickstart-onboarding.tsx +116 -0
  69. package/src/cli/cmd/tui/context/args.tsx +14 -0
  70. package/src/cli/cmd/tui/context/directory.ts +12 -0
  71. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  72. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  73. package/src/cli/cmd/tui/context/keybind.tsx +111 -0
  74. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  75. package/src/cli/cmd/tui/context/local.tsx +338 -0
  76. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  77. package/src/cli/cmd/tui/context/route.tsx +45 -0
  78. package/src/cli/cmd/tui/context/sdk.tsx +75 -0
  79. package/src/cli/cmd/tui/context/sync.tsx +374 -0
  80. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  81. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  82. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  83. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  84. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  85. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  86. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  87. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  88. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  89. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  90. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  91. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  92. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  93. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  94. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  95. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  96. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  97. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  98. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  99. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  100. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  101. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  102. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  103. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  104. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  105. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  106. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  107. package/src/cli/cmd/tui/context/theme.tsx +1077 -0
  108. package/src/cli/cmd/tui/event.ts +39 -0
  109. package/src/cli/cmd/tui/routes/home.tsx +150 -0
  110. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +93 -0
  111. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +37 -0
  112. package/src/cli/cmd/tui/routes/session/footer.tsx +76 -0
  113. package/src/cli/cmd/tui/routes/session/header.tsx +181 -0
  114. package/src/cli/cmd/tui/routes/session/index.tsx +1695 -0
  115. package/src/cli/cmd/tui/routes/session/sidebar.tsx +686 -0
  116. package/src/cli/cmd/tui/spawn.ts +60 -0
  117. package/src/cli/cmd/tui/thread.ts +120 -0
  118. package/src/cli/cmd/tui/ui/dialog-alert.tsx +55 -0
  119. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +81 -0
  120. package/src/cli/cmd/tui/ui/dialog-help.tsx +36 -0
  121. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +75 -0
  122. package/src/cli/cmd/tui/ui/dialog-select.tsx +317 -0
  123. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  124. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  125. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  126. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  127. package/src/cli/cmd/tui/util/editor.ts +32 -0
  128. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  129. package/src/cli/cmd/tui/worker.ts +63 -0
  130. package/src/cli/cmd/uninstall.ts +344 -0
  131. package/src/cli/cmd/upgrade.ts +67 -0
  132. package/src/cli/cmd/web.ts +84 -0
  133. package/src/cli/error.ts +55 -0
  134. package/src/cli/ui.ts +84 -0
  135. package/src/cli/upgrade.ts +25 -0
  136. package/src/command/index.ts +79 -0
  137. package/src/command/template/initialize.txt +10 -0
  138. package/src/command/template/review.txt +73 -0
  139. package/src/config/config.ts +886 -0
  140. package/src/config/markdown.ts +41 -0
  141. package/src/env/index.ts +26 -0
  142. package/src/file/fzf.ts +124 -0
  143. package/src/file/ignore.ts +83 -0
  144. package/src/file/index.ts +326 -0
  145. package/src/file/ripgrep.ts +391 -0
  146. package/src/file/time.ts +38 -0
  147. package/src/file/watcher.ts +89 -0
  148. package/src/flag/flag.ts +29 -0
  149. package/src/format/formatter.ts +277 -0
  150. package/src/format/index.ts +137 -0
  151. package/src/global/index.ts +52 -0
  152. package/src/id/id.ts +73 -0
  153. package/src/ide/index.ts +75 -0
  154. package/src/index.ts +158 -0
  155. package/src/installation/index.ts +194 -0
  156. package/src/lsp/client.ts +215 -0
  157. package/src/lsp/index.ts +370 -0
  158. package/src/lsp/language.ts +111 -0
  159. package/src/lsp/server.ts +1327 -0
  160. package/src/mcp/auth.ts +82 -0
  161. package/src/mcp/index.ts +576 -0
  162. package/src/mcp/oauth-callback.ts +203 -0
  163. package/src/mcp/oauth-provider.ts +132 -0
  164. package/src/notification/index.ts +101 -0
  165. package/src/patch/index.ts +622 -0
  166. package/src/permission/index.ts +198 -0
  167. package/src/plugin/index.ts +95 -0
  168. package/src/project/bootstrap.ts +31 -0
  169. package/src/project/instance.ts +68 -0
  170. package/src/project/project.ts +133 -0
  171. package/src/project/state.ts +65 -0
  172. package/src/project/vcs.ts +77 -0
  173. package/src/provider/auth.ts +143 -0
  174. package/src/provider/models-macro.ts +11 -0
  175. package/src/provider/models.ts +93 -0
  176. package/src/provider/provider.ts +1005 -0
  177. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  178. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  179. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  180. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  181. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +27 -0
  182. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  183. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  184. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  185. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  186. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  187. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  188. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  189. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  190. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  191. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  192. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  193. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  194. package/src/provider/transform.ts +406 -0
  195. package/src/pty/index.ts +226 -0
  196. package/src/ratelimit/index.ts +185 -0
  197. package/src/server/error.ts +36 -0
  198. package/src/server/project.ts +50 -0
  199. package/src/server/server.ts +2463 -0
  200. package/src/server/tui.ts +71 -0
  201. package/src/session/compaction.ts +257 -0
  202. package/src/session/index.ts +470 -0
  203. package/src/session/message-v2.ts +641 -0
  204. package/src/session/message.ts +189 -0
  205. package/src/session/processor.ts +448 -0
  206. package/src/session/prompt/anthropic-20250930.txt +166 -0
  207. package/src/session/prompt/anthropic.txt +105 -0
  208. package/src/session/prompt/anthropic_spoof.txt +1 -0
  209. package/src/session/prompt/beast.txt +147 -0
  210. package/src/session/prompt/build-switch.txt +5 -0
  211. package/src/session/prompt/codex.txt +318 -0
  212. package/src/session/prompt/compaction.txt +12 -0
  213. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  214. package/src/session/prompt/gemini.txt +155 -0
  215. package/src/session/prompt/max-steps.txt +16 -0
  216. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  217. package/src/session/prompt/plan.txt +26 -0
  218. package/src/session/prompt/polaris.txt +107 -0
  219. package/src/session/prompt/qwen.txt +109 -0
  220. package/src/session/prompt/summarize.txt +4 -0
  221. package/src/session/prompt/title.txt +36 -0
  222. package/src/session/prompt.ts +1541 -0
  223. package/src/session/retry.ts +82 -0
  224. package/src/session/revert.ts +108 -0
  225. package/src/session/status.ts +75 -0
  226. package/src/session/summary.ts +203 -0
  227. package/src/session/system.ts +148 -0
  228. package/src/session/todo.ts +36 -0
  229. package/src/share/share-next.ts +195 -0
  230. package/src/share/share.ts +87 -0
  231. package/src/skill/index.ts +2 -0
  232. package/src/skill/skill.ts +138 -0
  233. package/src/snapshot/index.ts +197 -0
  234. package/src/storage/storage.ts +226 -0
  235. package/src/telemetry/index.ts +247 -0
  236. package/src/tool/bash.ts +365 -0
  237. package/src/tool/bash.txt +128 -0
  238. package/src/tool/batch.ts +173 -0
  239. package/src/tool/batch.txt +28 -0
  240. package/src/tool/codesearch.ts +138 -0
  241. package/src/tool/codesearch.txt +12 -0
  242. package/src/tool/edit.ts +674 -0
  243. package/src/tool/edit.txt +10 -0
  244. package/src/tool/glob.ts +65 -0
  245. package/src/tool/glob.txt +6 -0
  246. package/src/tool/grep.ts +120 -0
  247. package/src/tool/grep.txt +8 -0
  248. package/src/tool/invalid.ts +17 -0
  249. package/src/tool/ls.ts +110 -0
  250. package/src/tool/ls.txt +1 -0
  251. package/src/tool/lsp-diagnostics.ts +26 -0
  252. package/src/tool/lsp-diagnostics.txt +1 -0
  253. package/src/tool/lsp-hover.ts +31 -0
  254. package/src/tool/lsp-hover.txt +1 -0
  255. package/src/tool/multiedit.ts +46 -0
  256. package/src/tool/multiedit.txt +41 -0
  257. package/src/tool/patch.ts +233 -0
  258. package/src/tool/patch.txt +1 -0
  259. package/src/tool/read.ts +217 -0
  260. package/src/tool/read.txt +12 -0
  261. package/src/tool/registry.ts +150 -0
  262. package/src/tool/skill.ts +85 -0
  263. package/src/tool/task.ts +135 -0
  264. package/src/tool/task.txt +60 -0
  265. package/src/tool/todo.ts +39 -0
  266. package/src/tool/todoread.txt +14 -0
  267. package/src/tool/todowrite.txt +167 -0
  268. package/src/tool/tool.ts +66 -0
  269. package/src/tool/webfetch.ts +187 -0
  270. package/src/tool/webfetch.txt +14 -0
  271. package/src/tool/websearch.ts +150 -0
  272. package/src/tool/websearch.txt +11 -0
  273. package/src/tool/write.ts +99 -0
  274. package/src/tool/write.txt +8 -0
  275. package/src/types/shims.d.ts +3 -0
  276. package/src/util/color.ts +19 -0
  277. package/src/util/context.ts +25 -0
  278. package/src/util/defer.ts +12 -0
  279. package/src/util/eventloop.ts +20 -0
  280. package/src/util/filesystem.ts +69 -0
  281. package/src/util/fn.ts +11 -0
  282. package/src/util/iife.ts +3 -0
  283. package/src/util/keybind.ts +79 -0
  284. package/src/util/lazy.ts +11 -0
  285. package/src/util/locale.ts +81 -0
  286. package/src/util/lock.ts +98 -0
  287. package/src/util/log.ts +177 -0
  288. package/src/util/queue.ts +32 -0
  289. package/src/util/rpc.ts +42 -0
  290. package/src/util/scrap.ts +10 -0
  291. package/src/util/signal.ts +12 -0
  292. package/src/util/timeout.ts +14 -0
  293. package/src/util/token.ts +7 -0
  294. package/src/util/wildcard.ts +54 -0
  295. package/sst-env.d.ts +9 -0
  296. package/test/bun.test.ts +53 -0
  297. package/test/config/agent-color.test.ts +66 -0
  298. package/test/config/config.test.ts +503 -0
  299. package/test/config/markdown.test.ts +89 -0
  300. package/test/file/ignore.test.ts +10 -0
  301. package/test/fixture/fixture.ts +28 -0
  302. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  303. package/test/ide/ide.test.ts +82 -0
  304. package/test/keybind.test.ts +317 -0
  305. package/test/lsp/client.test.ts +95 -0
  306. package/test/patch/patch.test.ts +348 -0
  307. package/test/preload.ts +38 -0
  308. package/test/project/project.test.ts +42 -0
  309. package/test/provider/provider.test.ts +1809 -0
  310. package/test/provider/transform.test.ts +305 -0
  311. package/test/session/retry.test.ts +61 -0
  312. package/test/session/session.test.ts +71 -0
  313. package/test/snapshot/snapshot.test.ts +939 -0
  314. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  315. package/test/tool/bash.test.ts +55 -0
  316. package/test/tool/patch.test.ts +259 -0
  317. package/test/util/iife.test.ts +36 -0
  318. package/test/util/lazy.test.ts +50 -0
  319. package/test/util/timeout.test.ts +21 -0
  320. package/test/util/wildcard.test.ts +55 -0
  321. package/tsconfig.json +17 -0
  322. package/cerebras-cli-1.0.0.tgz +0 -0
@@ -0,0 +1,25 @@
1
+ import { cmd } from "../cmd"
2
+ import { tui } from "./app"
3
+
4
+ export const AttachCommand = cmd({
5
+ command: "attach <url>",
6
+ describe: "attach to a running opencode server",
7
+ builder: (yargs) =>
8
+ yargs
9
+ .positional("url", {
10
+ type: "string",
11
+ describe: "http://localhost:4096",
12
+ demandOption: true,
13
+ })
14
+ .option("dir", {
15
+ type: "string",
16
+ description: "directory to run in",
17
+ }),
18
+ handler: async (args) => {
19
+ if (args.dir) process.chdir(args.dir)
20
+ await tui({
21
+ url: args.url,
22
+ args: {},
23
+ })
24
+ },
25
+ })
@@ -0,0 +1,21 @@
1
+ export const EmptyBorder = {
2
+ topLeft: "",
3
+ bottomLeft: "",
4
+ vertical: "",
5
+ topRight: "",
6
+ bottomRight: "",
7
+ horizontal: " ",
8
+ bottomT: "",
9
+ topT: "",
10
+ cross: "",
11
+ leftT: "",
12
+ rightT: "",
13
+ }
14
+
15
+ export const SplitBorder = {
16
+ border: ["left" as const, "right" as const],
17
+ customBorderChars: {
18
+ ...EmptyBorder,
19
+ vertical: "┃",
20
+ },
21
+ }
@@ -0,0 +1,225 @@
1
+ import { createSignal, createEffect, Show, onMount, For } from "solid-js"
2
+ import { useSync } from "@tui/context/sync"
3
+ import { useSDK } from "../context/sdk"
4
+ import { useTheme } from "../context/theme"
5
+ import { TextAttributes, TextareaRenderable } from "@opentui/core"
6
+ import { useKeyboard, useTerminalDimensions } from "@opentui/solid"
7
+ import { useKV } from "../context/kv"
8
+ import { useLocal } from "../context/local"
9
+
10
+ const WELCOME_OPTIONS = [
11
+ { id: "setup", label: "Set up Cerebras API key", description: "Connect to the fastest inference" },
12
+ { id: "skip", label: "Skip for now", description: "Configure later" },
13
+ ] as const
14
+
15
+ type WelcomeOptionId = typeof WELCOME_OPTIONS[number]["id"]
16
+
17
+ export function CerebrasOnboarding(props: { onComplete: () => void }) {
18
+ const { theme } = useTheme()
19
+ const sdk = useSDK()
20
+ const sync = useSync()
21
+ const kv = useKV()
22
+ const local = useLocal()
23
+ const dimensions = useTerminalDimensions()
24
+ const [step, setStep] = createSignal<"welcome" | "apikey">("welcome")
25
+ const [selected, setSelected] = createSignal(0)
26
+ const [error, setError] = createSignal("")
27
+ const [loading, setLoading] = createSignal(false)
28
+ const [ready, setReady] = createSignal(false)
29
+ let textarea: TextareaRenderable
30
+
31
+ // Small delay before accepting input to avoid accidental dismiss
32
+ onMount(() => {
33
+ setTimeout(() => setReady(true), 300)
34
+ })
35
+
36
+ // Focus textarea when step changes to apikey
37
+ createEffect(() => {
38
+ if (step() === "apikey" && textarea) {
39
+ setTimeout(() => textarea.focus(), 50)
40
+ }
41
+ })
42
+
43
+ const markSeen = () => {
44
+ kv.set("hasSeenCerebrasOnboarding", true)
45
+ }
46
+
47
+ const complete = () => {
48
+ markSeen()
49
+ props.onComplete()
50
+ }
51
+
52
+ const selectOption = (optionId: WelcomeOptionId) => {
53
+ if (optionId === "setup") {
54
+ setStep("apikey")
55
+ } else {
56
+ complete()
57
+ }
58
+ }
59
+
60
+ useKeyboard((evt) => {
61
+ // Always prevent arrow keys from bubbling during onboarding
62
+ if (evt.name === "up" || evt.name === "down") {
63
+ evt.preventDefault?.()
64
+ }
65
+
66
+ if (!ready()) return
67
+ if (loading()) return
68
+
69
+ if (step() === "welcome") {
70
+ if (evt.name === "up") {
71
+ setSelected((s) => (s > 0 ? s - 1 : WELCOME_OPTIONS.length - 1))
72
+ } else if (evt.name === "down") {
73
+ setSelected((s) => (s < WELCOME_OPTIONS.length - 1 ? s + 1 : 0))
74
+ } else if (evt.name === "return") {
75
+ evt.preventDefault?.()
76
+ selectOption(WELCOME_OPTIONS[selected()].id)
77
+ } else if (evt.name === "escape") {
78
+ evt.preventDefault?.()
79
+ complete()
80
+ }
81
+ } else if (step() === "apikey") {
82
+ if (evt.name === "escape") {
83
+ evt.preventDefault?.()
84
+ setStep("welcome")
85
+ }
86
+ }
87
+ })
88
+
89
+ const handleSubmit = async () => {
90
+ const key = textarea?.plainText?.trim() ?? ""
91
+ if (!key) {
92
+ setError("Please enter your API key")
93
+ return
94
+ }
95
+
96
+ setLoading(true)
97
+ setError("")
98
+
99
+ try {
100
+ await sdk.client.auth.set({
101
+ providerID: "cerebras",
102
+ auth: {
103
+ type: "api",
104
+ key: key,
105
+ },
106
+ })
107
+ await sdk.client.instance.dispose()
108
+ await sync.bootstrap()
109
+
110
+ // Set Cerebras as the default model
111
+ local.model.set({ providerID: "cerebras", modelID: "zai-glm-4.7" }, { recent: true })
112
+
113
+ complete()
114
+ } catch (e) {
115
+ setError("Failed to connect. Please check your API key.")
116
+ setLoading(false)
117
+ }
118
+ }
119
+
120
+ return (
121
+ <box
122
+ position="absolute"
123
+ top={0}
124
+ left={0}
125
+ width={dimensions().width}
126
+ height={dimensions().height}
127
+ backgroundColor={theme.background}
128
+ justifyContent="center"
129
+ alignItems="center"
130
+ flexDirection="column"
131
+ >
132
+ <box
133
+ width={Math.min(60, dimensions().width - 4)}
134
+ backgroundColor={theme.backgroundPanel}
135
+ paddingTop={2}
136
+ paddingBottom={2}
137
+ paddingLeft={3}
138
+ paddingRight={3}
139
+ gap={1}
140
+ >
141
+ <Show when={step() === "welcome"}>
142
+ <box gap={1}>
143
+ <text fg={theme.primary} attributes={TextAttributes.BOLD}>
144
+ ◆ Welcome to Cerebras Code CLI
145
+ </text>
146
+ <text fg={theme.text}>
147
+ The fastest AI coding assistant, powered by Cerebras inference.
148
+ </text>
149
+ <box marginTop={1}>
150
+ <text fg={theme.textMuted}>Get your free API key at:</text>
151
+ <text fg={theme.accent}>https://cloud.cerebras.ai?utm-source=cli</text>
152
+ </box>
153
+ <box marginTop={2} gap={0}>
154
+ <For each={WELCOME_OPTIONS}>
155
+ {(option, index) => {
156
+ const isSelected = () => selected() === index()
157
+ return (
158
+ <box
159
+ flexDirection="row"
160
+ backgroundColor={isSelected() ? theme.primary : undefined}
161
+ paddingLeft={1}
162
+ paddingRight={1}
163
+ >
164
+ <text fg={isSelected() ? theme.backgroundPanel : theme.text}>
165
+ {isSelected() ? "▸ " : " "}{option.label}
166
+ <span style={{ fg: isSelected() ? theme.backgroundPanel : theme.textMuted }}>
167
+ {" "}— {option.description}
168
+ </span>
169
+ </text>
170
+ </box>
171
+ )
172
+ }}
173
+ </For>
174
+ </box>
175
+ <box marginTop={1}>
176
+ <text fg={theme.textMuted}>
177
+ ↑↓ navigate • enter select • esc skip
178
+ </text>
179
+ </box>
180
+ </box>
181
+ </Show>
182
+
183
+ <Show when={step() === "apikey"}>
184
+ <box gap={1}>
185
+ <box flexDirection="row" justifyContent="space-between">
186
+ <text fg={theme.text} attributes={TextAttributes.BOLD}>
187
+ Enter Cerebras API Key
188
+ </text>
189
+ <text fg={theme.textMuted}>esc to go back</text>
190
+ </box>
191
+ <text fg={theme.textMuted}>
192
+ Paste your API key from cloud.cerebras.ai?utm-source=cli
193
+ </text>
194
+ <box marginTop={1}>
195
+ <textarea
196
+ ref={(r: TextareaRenderable) => (textarea = r)}
197
+ onSubmit={handleSubmit}
198
+ onContentChange={() => setError("")}
199
+ height={1}
200
+ keyBindings={[{ name: "return", action: "submit" }]}
201
+ placeholder="csk-..."
202
+ textColor={theme.text}
203
+ focusedTextColor={theme.text}
204
+ cursorColor={theme.text}
205
+ backgroundColor={theme.backgroundElement}
206
+ focusedBackgroundColor={theme.backgroundElement}
207
+ />
208
+ </box>
209
+ <Show when={error()}>
210
+ <text fg={theme.error}>{error()}</text>
211
+ </Show>
212
+ <Show when={loading()}>
213
+ <text fg={theme.textMuted}>Connecting...</text>
214
+ </Show>
215
+ <box marginTop={1}>
216
+ <text fg={theme.textMuted}>
217
+ Press <span style={{ fg: theme.text }}>enter</span> to connect
218
+ </text>
219
+ </box>
220
+ </box>
221
+ </Show>
222
+ </box>
223
+ </box>
224
+ )
225
+ }
@@ -0,0 +1,31 @@
1
+ import { createMemo } from "solid-js"
2
+ import { useLocal } from "@tui/context/local"
3
+ import { DialogSelect } from "@tui/ui/dialog-select"
4
+ import { useDialog } from "@tui/ui/dialog"
5
+
6
+ export function DialogAgent() {
7
+ const local = useLocal()
8
+ const dialog = useDialog()
9
+
10
+ const options = createMemo(() =>
11
+ local.agent.list().map((item) => {
12
+ return {
13
+ value: item.name,
14
+ title: item.name,
15
+ description: item.builtIn ? "native" : item.description,
16
+ }
17
+ }),
18
+ )
19
+
20
+ return (
21
+ <DialogSelect
22
+ title="Select agent"
23
+ current={local.agent.current().name}
24
+ options={options()}
25
+ onSelect={(option) => {
26
+ local.agent.set(option.value)
27
+ dialog.clear()
28
+ }}
29
+ />
30
+ )
31
+ }
@@ -0,0 +1,124 @@
1
+ import { useDialog } from "@tui/ui/dialog"
2
+ import { DialogSelect, type DialogSelectOption, type DialogSelectRef } from "@tui/ui/dialog-select"
3
+ import {
4
+ createContext,
5
+ createMemo,
6
+ createSignal,
7
+ onCleanup,
8
+ useContext,
9
+ type Accessor,
10
+ type ParentProps,
11
+ } from "solid-js"
12
+ import { useKeyboard } from "@opentui/solid"
13
+ import { useKeybind } from "@tui/context/keybind"
14
+ import type { KeybindsConfig } from "@opencode-ai/sdk/v2"
15
+
16
+ type Context = ReturnType<typeof init>
17
+ const ctx = createContext<Context>()
18
+
19
+ export type CommandOption = DialogSelectOption & {
20
+ keybind?: keyof KeybindsConfig
21
+ suggested?: boolean
22
+ }
23
+
24
+ function init() {
25
+ const [registrations, setRegistrations] = createSignal<Accessor<CommandOption[]>[]>([])
26
+ const [suspendCount, setSuspendCount] = createSignal(0)
27
+ const dialog = useDialog()
28
+ const keybind = useKeybind()
29
+ const options = createMemo(() => {
30
+ const all = registrations().flatMap((x) => x())
31
+ const suggested = all.filter((x) => x.suggested)
32
+ return [
33
+ ...suggested.map((x) => ({
34
+ ...x,
35
+ category: "Suggested",
36
+ value: "suggested." + x.value,
37
+ })),
38
+ ...all,
39
+ ].map((x) => ({
40
+ ...x,
41
+ footer: x.keybind ? keybind.print(x.keybind) : undefined,
42
+ }))
43
+ })
44
+ const suspended = () => suspendCount() > 0
45
+
46
+ useKeyboard((evt) => {
47
+ if (suspended()) return
48
+ if (dialog.stack.length > 0) return
49
+ for (const option of options()) {
50
+ if (option.keybind && keybind.match(option.keybind, evt)) {
51
+ evt.preventDefault()
52
+ option.onSelect?.(dialog)
53
+ return
54
+ }
55
+ }
56
+ })
57
+
58
+ const result = {
59
+ trigger(name: string, source?: "prompt") {
60
+ for (const option of options()) {
61
+ if (option.value === name) {
62
+ option.onSelect?.(dialog, source)
63
+ return
64
+ }
65
+ }
66
+ },
67
+ keybinds(enabled: boolean) {
68
+ setSuspendCount((count) => count + (enabled ? -1 : 1))
69
+ },
70
+ suspended,
71
+ show() {
72
+ dialog.replace(() => <DialogCommand options={options()} />)
73
+ },
74
+ register(cb: () => CommandOption[]) {
75
+ const results = createMemo(cb)
76
+ setRegistrations((arr) => [results, ...arr])
77
+ onCleanup(() => {
78
+ setRegistrations((arr) => arr.filter((x) => x !== results))
79
+ })
80
+ },
81
+ get options() {
82
+ return options()
83
+ },
84
+ }
85
+ return result
86
+ }
87
+
88
+ export function useCommandDialog() {
89
+ const value = useContext(ctx)
90
+ if (!value) {
91
+ throw new Error("useCommandDialog must be used within a CommandProvider")
92
+ }
93
+ return value
94
+ }
95
+
96
+ export function CommandProvider(props: ParentProps) {
97
+ const value = init()
98
+ const dialog = useDialog()
99
+ const keybind = useKeybind()
100
+
101
+ useKeyboard((evt) => {
102
+ if (value.suspended()) return
103
+ if (dialog.stack.length > 0) return
104
+ if (evt.defaultPrevented) return
105
+ if (keybind.match("command_list", evt)) {
106
+ evt.preventDefault()
107
+ dialog.replace(() => <DialogCommand options={value.options} />)
108
+ return
109
+ }
110
+ })
111
+
112
+ return <ctx.Provider value={value}>{props.children}</ctx.Provider>
113
+ }
114
+
115
+ function DialogCommand(props: { options: CommandOption[] }) {
116
+ let ref: DialogSelectRef<string>
117
+ return (
118
+ <DialogSelect
119
+ ref={(r) => (ref = r)}
120
+ title="Commands"
121
+ options={props.options.filter((x) => !ref?.filter || !x.value.startsWith("suggested."))}
122
+ />
123
+ )
124
+ }
@@ -0,0 +1,160 @@
1
+ import { TextareaRenderable, TextAttributes } from "@opentui/core"
2
+ import { useTheme } from "../context/theme"
3
+ import { useDialog } from "../ui/dialog"
4
+ import { createSignal, onMount } from "solid-js"
5
+ import { useKeyboard } from "@opentui/solid"
6
+ import { Installation } from "@/installation"
7
+
8
+ const FEEDBACK_ENDPOINT = "https://cerebras-code-cli-feedback.kevin-taylor-d8d.workers.dev"
9
+
10
+ export interface FeedbackMetadata {
11
+ error?: {
12
+ name?: string
13
+ message?: string
14
+ data?: unknown
15
+ }
16
+ sessionID?: string
17
+ providerID?: string
18
+ modelID?: string
19
+ [key: string]: unknown
20
+ }
21
+
22
+ export function DialogFeedback(props: { onClose: () => void; metadata?: FeedbackMetadata }) {
23
+ const dialog = useDialog()
24
+ const { theme } = useTheme()
25
+ let textarea: TextareaRenderable
26
+ const [status, setStatus] = createSignal<"idle" | "sending" | "success" | "error">("idle")
27
+ const [errorMsg, setErrorMsg] = createSignal("")
28
+ const [focused, setFocused] = createSignal<"textarea" | "button">("textarea")
29
+
30
+ // Note: Global keybinds are automatically disabled when dialog.stack.length > 0
31
+ // (see CommandProvider in dialog-command.tsx)
32
+
33
+ useKeyboard((evt) => {
34
+ if (evt.name === "escape") {
35
+ props.onClose()
36
+ }
37
+ if (evt.name === "tab") {
38
+ evt.preventDefault?.()
39
+ if (focused() === "textarea") {
40
+ setFocused("button")
41
+ textarea?.blur()
42
+ } else {
43
+ setFocused("textarea")
44
+ textarea?.focus()
45
+ }
46
+ }
47
+ if (evt.name === "return") {
48
+ if (focused() === "button") {
49
+ submit()
50
+ } else {
51
+ // Manually insert newline when textarea is focused
52
+ textarea?.insertText("\n")
53
+ }
54
+ }
55
+ })
56
+
57
+ onMount(() => {
58
+ dialog.setSize("medium")
59
+ setTimeout(() => {
60
+ textarea?.focus()
61
+ }, 1)
62
+ })
63
+
64
+ const submit = async () => {
65
+ const message = textarea.plainText.trim()
66
+ if (!message) return
67
+
68
+ setStatus("sending")
69
+ try {
70
+ const payload: Record<string, unknown> = {
71
+ message,
72
+ version: Installation.VERSION,
73
+ os: process.platform,
74
+ }
75
+
76
+ // Include metadata if provided - flatten for easier email formatting
77
+ if (props.metadata) {
78
+ // Flatten metadata into top-level fields for easier email display
79
+ if (props.metadata.error) {
80
+ payload.errorName = props.metadata.error.name
81
+ payload.errorMessage = props.metadata.error.message
82
+ if (props.metadata.error.data && typeof props.metadata.error.data === "object") {
83
+ const errorData = props.metadata.error.data as Record<string, unknown>
84
+ payload.errorStatusCode = errorData.statusCode
85
+ payload.errorIsRetryable = errorData.isRetryable
86
+ payload.errorResponseBody = errorData.responseBody
87
+ }
88
+ }
89
+ payload.sessionID = props.metadata.sessionID
90
+ payload.providerID = props.metadata.providerID
91
+ payload.modelID = props.metadata.modelID
92
+
93
+ // Also include full metadata object for detailed debugging
94
+ payload.metadata = props.metadata
95
+ }
96
+
97
+ const res = await fetch(FEEDBACK_ENDPOINT, {
98
+ method: "POST",
99
+ headers: { "Content-Type": "application/json" },
100
+ body: JSON.stringify(payload),
101
+ })
102
+
103
+ if (!res.ok) {
104
+ throw new Error(`HTTP ${res.status}`)
105
+ }
106
+
107
+ setStatus("success")
108
+ setTimeout(() => props.onClose(), 1500)
109
+ } catch (e) {
110
+ setStatus("error")
111
+ setErrorMsg(e instanceof Error ? e.message : String(e))
112
+ }
113
+ }
114
+
115
+ return (
116
+ <box paddingLeft={2} paddingRight={2} gap={1}>
117
+ <box flexDirection="row" justifyContent="space-between">
118
+ <text attributes={TextAttributes.BOLD}>Send Feedback</text>
119
+ <text fg={theme.textMuted}>esc to close</text>
120
+ </box>
121
+
122
+ {status() === "idle" || status() === "sending" ? (
123
+ <box gap={1}>
124
+ <text fg={theme.textMuted}>Describe your feedback, bug, or feature request:</text>
125
+ <textarea
126
+ height={5}
127
+ ref={(val: TextareaRenderable) => (textarea = val)}
128
+ placeholder="Your feedback..."
129
+ textColor={theme.text}
130
+ focusedTextColor={theme.text}
131
+ cursorColor={theme.text}
132
+ />
133
+ <box paddingBottom={1} flexDirection="row" justifyContent="space-between">
134
+ <text fg={theme.textMuted}>tab to switch focus</text>
135
+ <box
136
+ paddingLeft={2}
137
+ paddingRight={2}
138
+ backgroundColor={focused() === "button" ? theme.primary : theme.backgroundElement}
139
+ onMouseUp={submit}
140
+ >
141
+ <text fg={focused() === "button" ? theme.selectedListItemText : theme.text}>
142
+ {status() === "sending" ? "Sending..." : "Submit"}
143
+ </text>
144
+ </box>
145
+ </box>
146
+ </box>
147
+ ) : status() === "success" ? (
148
+ <box padding={1}>
149
+ <text fg={theme.success}>✓ Feedback sent! Thank you.</text>
150
+ </box>
151
+ ) : (
152
+ <box padding={1} gap={1}>
153
+ <text fg={theme.error}>✗ Failed to send: {errorMsg()}</text>
154
+ <text fg={theme.textMuted}>Please try again or email support directly.</text>
155
+ </box>
156
+ )}
157
+ </box>
158
+ )
159
+ }
160
+
@@ -0,0 +1,86 @@
1
+ import { createMemo, createSignal } from "solid-js"
2
+ import { useLocal } from "@tui/context/local"
3
+ import { useSync } from "@tui/context/sync"
4
+ import { map, pipe, entries, sortBy } from "remeda"
5
+ import { DialogSelect, type DialogSelectRef, type DialogSelectOption } from "@tui/ui/dialog-select"
6
+ import { useTheme } from "../context/theme"
7
+ import { Keybind } from "@/util/keybind"
8
+ import { TextAttributes } from "@opentui/core"
9
+ import { useSDK } from "@tui/context/sdk"
10
+
11
+ function Status(props: { enabled: boolean; loading: boolean }) {
12
+ const { theme } = useTheme()
13
+ if (props.loading) {
14
+ return <span style={{ fg: theme.textMuted }}>⋯ Loading</span>
15
+ }
16
+ if (props.enabled) {
17
+ return <span style={{ fg: theme.success, attributes: TextAttributes.BOLD }}>✓ Enabled</span>
18
+ }
19
+ return <span style={{ fg: theme.textMuted }}>○ Disabled</span>
20
+ }
21
+
22
+ export function DialogMcp() {
23
+ const local = useLocal()
24
+ const sync = useSync()
25
+ const sdk = useSDK()
26
+ const [, setRef] = createSignal<DialogSelectRef<unknown>>()
27
+ const [loading, setLoading] = createSignal<string | null>(null)
28
+
29
+ const options = createMemo(() => {
30
+ // Track sync data and loading state to trigger re-render when they change
31
+ const mcpData = sync.data.mcp
32
+ const loadingMcp = loading()
33
+
34
+ return pipe(
35
+ mcpData ?? {},
36
+ entries(),
37
+ sortBy(([name]) => name),
38
+ map(([name, status]) => ({
39
+ value: name,
40
+ title: name,
41
+ description: status.status === "failed" ? "failed" : status.status,
42
+ footer: <Status enabled={local.mcp.isEnabled(name)} loading={loadingMcp === name} />,
43
+ category: undefined,
44
+ })),
45
+ )
46
+ })
47
+
48
+ const keybinds = createMemo(() => [
49
+ {
50
+ keybind: Keybind.parse("space")[0],
51
+ title: "toggle",
52
+ onTrigger: async (option: DialogSelectOption<string>) => {
53
+ // Prevent toggling while an operation is already in progress
54
+ if (loading() !== null) return
55
+
56
+ setLoading(option.value)
57
+ try {
58
+ await local.mcp.toggle(option.value)
59
+ // Refresh MCP status from server
60
+ const status = await sdk.client.mcp.status()
61
+ if (status.data) {
62
+ sync.set("mcp", status.data)
63
+ } else {
64
+ console.error("Failed to refresh MCP status: no data returned")
65
+ }
66
+ } catch (error) {
67
+ console.error("Failed to toggle MCP:", error)
68
+ } finally {
69
+ setLoading(null)
70
+ }
71
+ },
72
+ },
73
+ ])
74
+
75
+ return (
76
+ <DialogSelect
77
+ ref={setRef}
78
+ title="MCPs"
79
+ options={options()}
80
+ keybind={keybinds()}
81
+ onSelect={(option) => {
82
+ // Don't close on select, only on escape
83
+ }}
84
+ />
85
+ )
86
+ }