cerebras-cli 1.0.1 → 1.0.4

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 (314) hide show
  1. package/README.md +3 -5
  2. package/cerebras-cli-1.0.0.tgz +0 -0
  3. package/package.json +7 -88
  4. package/AGENTS.md +0 -27
  5. package/Dockerfile +0 -10
  6. package/bunfig.toml +0 -4
  7. package/parsers-config.ts +0 -239
  8. package/script/build.ts +0 -151
  9. package/script/postinstall.mjs +0 -122
  10. package/script/publish.ts +0 -256
  11. package/script/schema.ts +0 -47
  12. package/src/acp/README.md +0 -164
  13. package/src/acp/agent.ts +0 -812
  14. package/src/acp/session.ts +0 -70
  15. package/src/acp/types.ts +0 -22
  16. package/src/agent/agent.ts +0 -310
  17. package/src/agent/generate.txt +0 -75
  18. package/src/auth/index.ts +0 -70
  19. package/src/bun/index.ts +0 -152
  20. package/src/bus/global.ts +0 -10
  21. package/src/bus/index.ts +0 -142
  22. package/src/cli/bootstrap.ts +0 -17
  23. package/src/cli/cmd/acp.ts +0 -88
  24. package/src/cli/cmd/agent.ts +0 -165
  25. package/src/cli/cmd/auth.ts +0 -369
  26. package/src/cli/cmd/cmd.ts +0 -7
  27. package/src/cli/cmd/debug/config.ts +0 -15
  28. package/src/cli/cmd/debug/file.ts +0 -91
  29. package/src/cli/cmd/debug/index.ts +0 -41
  30. package/src/cli/cmd/debug/lsp.ts +0 -47
  31. package/src/cli/cmd/debug/ripgrep.ts +0 -83
  32. package/src/cli/cmd/debug/scrap.ts +0 -15
  33. package/src/cli/cmd/debug/snapshot.ts +0 -48
  34. package/src/cli/cmd/export.ts +0 -88
  35. package/src/cli/cmd/generate.ts +0 -38
  36. package/src/cli/cmd/github.ts +0 -1200
  37. package/src/cli/cmd/import.ts +0 -98
  38. package/src/cli/cmd/mcp.ts +0 -400
  39. package/src/cli/cmd/models.ts +0 -77
  40. package/src/cli/cmd/pr.ts +0 -112
  41. package/src/cli/cmd/run.ts +0 -342
  42. package/src/cli/cmd/serve.ts +0 -31
  43. package/src/cli/cmd/session.ts +0 -106
  44. package/src/cli/cmd/stats.ts +0 -298
  45. package/src/cli/cmd/tui/app.tsx +0 -732
  46. package/src/cli/cmd/tui/attach.ts +0 -25
  47. package/src/cli/cmd/tui/component/border.tsx +0 -21
  48. package/src/cli/cmd/tui/component/dialog-agent.tsx +0 -31
  49. package/src/cli/cmd/tui/component/dialog-command.tsx +0 -124
  50. package/src/cli/cmd/tui/component/dialog-feedback.tsx +0 -160
  51. package/src/cli/cmd/tui/component/dialog-mcp.tsx +0 -86
  52. package/src/cli/cmd/tui/component/dialog-model.tsx +0 -223
  53. package/src/cli/cmd/tui/component/dialog-notification.tsx +0 -78
  54. package/src/cli/cmd/tui/component/dialog-provider.tsx +0 -222
  55. package/src/cli/cmd/tui/component/dialog-session-list.tsx +0 -97
  56. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +0 -31
  57. package/src/cli/cmd/tui/component/dialog-status.tsx +0 -114
  58. package/src/cli/cmd/tui/component/dialog-tag.tsx +0 -44
  59. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +0 -50
  60. package/src/cli/cmd/tui/component/logo.tsx +0 -37
  61. package/src/cli/cmd/tui/component/notification-banner.tsx +0 -58
  62. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +0 -530
  63. package/src/cli/cmd/tui/component/prompt/history.tsx +0 -107
  64. package/src/cli/cmd/tui/component/prompt/index.tsx +0 -931
  65. package/src/cli/cmd/tui/context/args.tsx +0 -14
  66. package/src/cli/cmd/tui/context/directory.ts +0 -12
  67. package/src/cli/cmd/tui/context/exit.tsx +0 -23
  68. package/src/cli/cmd/tui/context/helper.tsx +0 -25
  69. package/src/cli/cmd/tui/context/keybind.tsx +0 -111
  70. package/src/cli/cmd/tui/context/kv.tsx +0 -49
  71. package/src/cli/cmd/tui/context/local.tsx +0 -339
  72. package/src/cli/cmd/tui/context/prompt.tsx +0 -18
  73. package/src/cli/cmd/tui/context/route.tsx +0 -45
  74. package/src/cli/cmd/tui/context/sdk.tsx +0 -75
  75. package/src/cli/cmd/tui/context/sync.tsx +0 -374
  76. package/src/cli/cmd/tui/context/theme/aura.json +0 -69
  77. package/src/cli/cmd/tui/context/theme/ayu.json +0 -80
  78. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +0 -233
  79. package/src/cli/cmd/tui/context/theme/catppuccin.json +0 -112
  80. package/src/cli/cmd/tui/context/theme/cobalt2.json +0 -228
  81. package/src/cli/cmd/tui/context/theme/dracula.json +0 -219
  82. package/src/cli/cmd/tui/context/theme/everforest.json +0 -241
  83. package/src/cli/cmd/tui/context/theme/flexoki.json +0 -237
  84. package/src/cli/cmd/tui/context/theme/github.json +0 -233
  85. package/src/cli/cmd/tui/context/theme/gruvbox.json +0 -95
  86. package/src/cli/cmd/tui/context/theme/kanagawa.json +0 -77
  87. package/src/cli/cmd/tui/context/theme/material.json +0 -235
  88. package/src/cli/cmd/tui/context/theme/matrix.json +0 -77
  89. package/src/cli/cmd/tui/context/theme/mercury.json +0 -252
  90. package/src/cli/cmd/tui/context/theme/monokai.json +0 -221
  91. package/src/cli/cmd/tui/context/theme/nightowl.json +0 -221
  92. package/src/cli/cmd/tui/context/theme/nord.json +0 -223
  93. package/src/cli/cmd/tui/context/theme/one-dark.json +0 -84
  94. package/src/cli/cmd/tui/context/theme/orng.json +0 -245
  95. package/src/cli/cmd/tui/context/theme/palenight.json +0 -222
  96. package/src/cli/cmd/tui/context/theme/rosepine.json +0 -234
  97. package/src/cli/cmd/tui/context/theme/solarized.json +0 -223
  98. package/src/cli/cmd/tui/context/theme/synthwave84.json +0 -226
  99. package/src/cli/cmd/tui/context/theme/tokyonight.json +0 -243
  100. package/src/cli/cmd/tui/context/theme/vercel.json +0 -245
  101. package/src/cli/cmd/tui/context/theme/vesper.json +0 -218
  102. package/src/cli/cmd/tui/context/theme/zenburn.json +0 -223
  103. package/src/cli/cmd/tui/context/theme.tsx +0 -1077
  104. package/src/cli/cmd/tui/event.ts +0 -39
  105. package/src/cli/cmd/tui/routes/home.tsx +0 -104
  106. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +0 -93
  107. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +0 -37
  108. package/src/cli/cmd/tui/routes/session/footer.tsx +0 -76
  109. package/src/cli/cmd/tui/routes/session/header.tsx +0 -183
  110. package/src/cli/cmd/tui/routes/session/index.tsx +0 -1703
  111. package/src/cli/cmd/tui/routes/session/sidebar.tsx +0 -586
  112. package/src/cli/cmd/tui/spawn.ts +0 -60
  113. package/src/cli/cmd/tui/thread.ts +0 -120
  114. package/src/cli/cmd/tui/ui/dialog-alert.tsx +0 -55
  115. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +0 -81
  116. package/src/cli/cmd/tui/ui/dialog-help.tsx +0 -36
  117. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +0 -75
  118. package/src/cli/cmd/tui/ui/dialog-select.tsx +0 -317
  119. package/src/cli/cmd/tui/ui/dialog.tsx +0 -170
  120. package/src/cli/cmd/tui/ui/spinner.ts +0 -368
  121. package/src/cli/cmd/tui/ui/toast.tsx +0 -100
  122. package/src/cli/cmd/tui/util/clipboard.ts +0 -127
  123. package/src/cli/cmd/tui/util/editor.ts +0 -32
  124. package/src/cli/cmd/tui/util/terminal.ts +0 -114
  125. package/src/cli/cmd/tui/worker.ts +0 -63
  126. package/src/cli/cmd/uninstall.ts +0 -344
  127. package/src/cli/cmd/upgrade.ts +0 -67
  128. package/src/cli/cmd/web.ts +0 -84
  129. package/src/cli/error.ts +0 -55
  130. package/src/cli/ui.ts +0 -84
  131. package/src/cli/upgrade.ts +0 -25
  132. package/src/command/index.ts +0 -79
  133. package/src/command/template/initialize.txt +0 -10
  134. package/src/command/template/review.txt +0 -73
  135. package/src/config/config.ts +0 -886
  136. package/src/config/markdown.ts +0 -41
  137. package/src/env/index.ts +0 -26
  138. package/src/file/fzf.ts +0 -124
  139. package/src/file/ignore.ts +0 -83
  140. package/src/file/index.ts +0 -326
  141. package/src/file/ripgrep.ts +0 -391
  142. package/src/file/time.ts +0 -38
  143. package/src/file/watcher.ts +0 -89
  144. package/src/flag/flag.ts +0 -28
  145. package/src/format/formatter.ts +0 -277
  146. package/src/format/index.ts +0 -137
  147. package/src/global/index.ts +0 -52
  148. package/src/id/id.ts +0 -73
  149. package/src/ide/index.ts +0 -75
  150. package/src/index.ts +0 -158
  151. package/src/installation/index.ts +0 -194
  152. package/src/lsp/client.ts +0 -215
  153. package/src/lsp/index.ts +0 -370
  154. package/src/lsp/language.ts +0 -111
  155. package/src/lsp/server.ts +0 -1327
  156. package/src/mcp/auth.ts +0 -82
  157. package/src/mcp/index.ts +0 -576
  158. package/src/mcp/oauth-callback.ts +0 -203
  159. package/src/mcp/oauth-provider.ts +0 -132
  160. package/src/notification/index.ts +0 -101
  161. package/src/patch/index.ts +0 -622
  162. package/src/permission/index.ts +0 -198
  163. package/src/plugin/index.ts +0 -95
  164. package/src/project/bootstrap.ts +0 -31
  165. package/src/project/instance.ts +0 -68
  166. package/src/project/project.ts +0 -133
  167. package/src/project/state.ts +0 -65
  168. package/src/project/vcs.ts +0 -77
  169. package/src/provider/auth.ts +0 -143
  170. package/src/provider/models-macro.ts +0 -11
  171. package/src/provider/models.ts +0 -93
  172. package/src/provider/provider.ts +0 -996
  173. package/src/provider/sdk/openai-compatible/src/README.md +0 -5
  174. package/src/provider/sdk/openai-compatible/src/index.ts +0 -2
  175. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +0 -100
  176. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +0 -303
  177. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +0 -27
  178. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +0 -18
  179. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +0 -22
  180. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +0 -207
  181. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +0 -1713
  182. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +0 -177
  183. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +0 -1
  184. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +0 -88
  185. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +0 -128
  186. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +0 -115
  187. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +0 -65
  188. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +0 -104
  189. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +0 -103
  190. package/src/provider/transform.ts +0 -406
  191. package/src/pty/index.ts +0 -226
  192. package/src/ratelimit/index.ts +0 -185
  193. package/src/server/error.ts +0 -36
  194. package/src/server/project.ts +0 -50
  195. package/src/server/server.ts +0 -2463
  196. package/src/server/tui.ts +0 -71
  197. package/src/session/compaction.ts +0 -257
  198. package/src/session/index.ts +0 -470
  199. package/src/session/message-v2.ts +0 -641
  200. package/src/session/message.ts +0 -189
  201. package/src/session/processor.ts +0 -443
  202. package/src/session/prompt/anthropic-20250930.txt +0 -166
  203. package/src/session/prompt/anthropic.txt +0 -105
  204. package/src/session/prompt/anthropic_spoof.txt +0 -1
  205. package/src/session/prompt/beast.txt +0 -147
  206. package/src/session/prompt/build-switch.txt +0 -5
  207. package/src/session/prompt/codex.txt +0 -318
  208. package/src/session/prompt/compaction.txt +0 -12
  209. package/src/session/prompt/copilot-gpt-5.txt +0 -143
  210. package/src/session/prompt/gemini.txt +0 -155
  211. package/src/session/prompt/max-steps.txt +0 -16
  212. package/src/session/prompt/plan-reminder-anthropic.txt +0 -67
  213. package/src/session/prompt/plan.txt +0 -26
  214. package/src/session/prompt/polaris.txt +0 -107
  215. package/src/session/prompt/qwen.txt +0 -109
  216. package/src/session/prompt/summarize.txt +0 -4
  217. package/src/session/prompt/title.txt +0 -36
  218. package/src/session/prompt.ts +0 -1541
  219. package/src/session/retry.ts +0 -82
  220. package/src/session/revert.ts +0 -108
  221. package/src/session/status.ts +0 -75
  222. package/src/session/summary.ts +0 -203
  223. package/src/session/system.ts +0 -148
  224. package/src/session/todo.ts +0 -36
  225. package/src/share/share-next.ts +0 -195
  226. package/src/share/share.ts +0 -87
  227. package/src/snapshot/index.ts +0 -197
  228. package/src/storage/storage.ts +0 -226
  229. package/src/telemetry/index.ts +0 -232
  230. package/src/tool/bash.ts +0 -365
  231. package/src/tool/bash.txt +0 -128
  232. package/src/tool/batch.ts +0 -173
  233. package/src/tool/batch.txt +0 -28
  234. package/src/tool/codesearch.ts +0 -138
  235. package/src/tool/codesearch.txt +0 -12
  236. package/src/tool/edit.ts +0 -674
  237. package/src/tool/edit.txt +0 -10
  238. package/src/tool/glob.ts +0 -65
  239. package/src/tool/glob.txt +0 -6
  240. package/src/tool/grep.ts +0 -120
  241. package/src/tool/grep.txt +0 -8
  242. package/src/tool/invalid.ts +0 -17
  243. package/src/tool/ls.ts +0 -110
  244. package/src/tool/ls.txt +0 -1
  245. package/src/tool/lsp-diagnostics.ts +0 -26
  246. package/src/tool/lsp-diagnostics.txt +0 -1
  247. package/src/tool/lsp-hover.ts +0 -31
  248. package/src/tool/lsp-hover.txt +0 -1
  249. package/src/tool/multiedit.ts +0 -46
  250. package/src/tool/multiedit.txt +0 -41
  251. package/src/tool/patch.ts +0 -233
  252. package/src/tool/patch.txt +0 -1
  253. package/src/tool/read.ts +0 -217
  254. package/src/tool/read.txt +0 -12
  255. package/src/tool/registry.ts +0 -148
  256. package/src/tool/task.ts +0 -135
  257. package/src/tool/task.txt +0 -60
  258. package/src/tool/todo.ts +0 -39
  259. package/src/tool/todoread.txt +0 -14
  260. package/src/tool/todowrite.txt +0 -167
  261. package/src/tool/tool.ts +0 -66
  262. package/src/tool/webfetch.ts +0 -187
  263. package/src/tool/webfetch.txt +0 -14
  264. package/src/tool/websearch.ts +0 -150
  265. package/src/tool/websearch.txt +0 -11
  266. package/src/tool/write.ts +0 -99
  267. package/src/tool/write.txt +0 -8
  268. package/src/types/shims.d.ts +0 -3
  269. package/src/util/color.ts +0 -19
  270. package/src/util/context.ts +0 -25
  271. package/src/util/defer.ts +0 -12
  272. package/src/util/eventloop.ts +0 -20
  273. package/src/util/filesystem.ts +0 -69
  274. package/src/util/fn.ts +0 -11
  275. package/src/util/iife.ts +0 -3
  276. package/src/util/keybind.ts +0 -79
  277. package/src/util/lazy.ts +0 -11
  278. package/src/util/locale.ts +0 -81
  279. package/src/util/lock.ts +0 -98
  280. package/src/util/log.ts +0 -177
  281. package/src/util/queue.ts +0 -32
  282. package/src/util/rpc.ts +0 -42
  283. package/src/util/scrap.ts +0 -10
  284. package/src/util/signal.ts +0 -12
  285. package/src/util/timeout.ts +0 -14
  286. package/src/util/token.ts +0 -7
  287. package/src/util/wildcard.ts +0 -54
  288. package/sst-env.d.ts +0 -9
  289. package/test/bun.test.ts +0 -53
  290. package/test/config/agent-color.test.ts +0 -66
  291. package/test/config/config.test.ts +0 -503
  292. package/test/config/markdown.test.ts +0 -89
  293. package/test/file/ignore.test.ts +0 -10
  294. package/test/fixture/fixture.ts +0 -28
  295. package/test/fixture/lsp/fake-lsp-server.js +0 -77
  296. package/test/ide/ide.test.ts +0 -82
  297. package/test/keybind.test.ts +0 -317
  298. package/test/lsp/client.test.ts +0 -95
  299. package/test/patch/patch.test.ts +0 -348
  300. package/test/preload.ts +0 -38
  301. package/test/project/project.test.ts +0 -42
  302. package/test/provider/provider.test.ts +0 -1809
  303. package/test/provider/transform.test.ts +0 -305
  304. package/test/session/retry.test.ts +0 -61
  305. package/test/session/session.test.ts +0 -71
  306. package/test/snapshot/snapshot.test.ts +0 -939
  307. package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
  308. package/test/tool/bash.test.ts +0 -55
  309. package/test/tool/patch.test.ts +0 -259
  310. package/test/util/iife.test.ts +0 -36
  311. package/test/util/lazy.test.ts +0 -50
  312. package/test/util/timeout.test.ts +0 -21
  313. package/test/util/wildcard.test.ts +0 -55
  314. package/tsconfig.json +0 -17
@@ -1,586 +0,0 @@
1
- import { useSync } from "@tui/context/sync"
2
- import { createMemo, createEffect, createSignal, For, Show, Switch, Match } from "solid-js"
3
- import { createStore } from "solid-js/store"
4
- import { useTheme } from "../../context/theme"
5
- import { useToast } from "../../ui/toast"
6
- import { useLocal } from "../../context/local"
7
- import { Locale } from "@/util/locale"
8
- import path from "path"
9
- import type { AssistantMessage } from "@opencode-ai/sdk/v2"
10
- import { Installation } from "@/installation"
11
- import { useKeybind } from "../../context/keybind"
12
- import { useDirectory } from "../../context/directory"
13
-
14
- // Threshold for low cache hit rate warning
15
- const LOW_CACHE_HIT_THRESHOLD = 40
16
- const CONSECUTIVE_LOW_COUNT = 3
17
-
18
- // Convert percentage (0-100) to block character (8 levels)
19
- function percentToBar(percent: number): string {
20
- const blocks = [" ", "▁", "▂", "▃", "▄", "▅", "▆", "▇", "█"]
21
- const index = Math.round((percent / 100) * 8)
22
- return blocks[Math.min(8, Math.max(0, index))]
23
- }
24
-
25
- // Row with label and horizontal bar for rate limits
26
- function RateLimitRow(props: {
27
- remaining: number
28
- limit: number
29
- window: string
30
- }) {
31
- const { theme } = useTheme()
32
-
33
- const percentRemaining = createMemo(() =>
34
- props.limit > 0 ? (props.remaining / props.limit) * 100 : 100
35
- )
36
-
37
- // Bar width fits in column
38
- const barWidth = 10
39
- const filledBlocks = createMemo(() => Math.round((percentRemaining() / 100) * barWidth))
40
- const progressBar = createMemo(() => {
41
- const filled = filledBlocks()
42
- const empty = barWidth - filled
43
- return "█".repeat(filled) + "░".repeat(empty)
44
- })
45
-
46
- const barColor = createMemo(() => {
47
- const percent = percentRemaining()
48
- if (percent >= 50) return theme.success
49
- if (percent >= 20) return theme.warning
50
- return theme.error
51
- })
52
-
53
- // Window label
54
- const windowLabel = () => {
55
- if (props.window === "minute") return "min"
56
- if (props.window === "hour") return "hour"
57
- if (props.window === "day") return "day"
58
- return props.window
59
- }
60
-
61
- return (
62
- <box flexDirection="row" gap={1}>
63
- <text fg={theme.textMuted} width={4}>{windowLabel()}</text>
64
- <text>
65
- <span style={{ fg: barColor() }}>{progressBar()}</span>
66
- </text>
67
- </box>
68
- )
69
- }
70
-
71
- // Visual representation of cache hit rate
72
- function CacheVisual(props: {
73
- hitRate: number
74
- cachedTokens: number
75
- promptTokens: number
76
- recentRates: number[] // Last 10 message hit rates
77
- }) {
78
- const { theme } = useTheme()
79
-
80
- // Progress bar using block characters
81
- const barWidth = 20
82
- const filledBlocks = createMemo(() => Math.round((props.hitRate / 100) * barWidth))
83
- const progressBar = createMemo(() => {
84
- const filled = filledBlocks()
85
- const empty = barWidth - filled
86
- return "█".repeat(filled) + "░".repeat(empty)
87
- })
88
-
89
- // Pie/wheel indicator using circle segments
90
- const pieIndicator = createMemo(() => {
91
- const rate = props.hitRate
92
- if (rate >= 87.5) return "●" // Full
93
- if (rate >= 62.5) return "◕" // 3/4
94
- if (rate >= 37.5) return "◑" // Half
95
- if (rate >= 12.5) return "◔" // 1/4
96
- return "○" // Empty
97
- })
98
-
99
- // Minesweeper-style face indicator
100
- const faceIndicator = createMemo(() => {
101
- const rate = props.hitRate
102
- if (rate >= 70) return "😊" // Happy - good cache
103
- if (rate >= 40) return "😐" // Neutral - okay cache
104
- return "😟" // Worried - bad cache
105
- })
106
-
107
- // Color based on hit rate (gradient from red to green)
108
- const rateColor = createMemo(() => {
109
- const rate = props.hitRate
110
- if (rate >= 70) return theme.success
111
- if (rate >= 40) return theme.warning
112
- return theme.error
113
- })
114
-
115
- // Get color for a rate
116
- const getRateColor = (rate: number) => {
117
- if (rate >= 70) return theme.success
118
- if (rate >= 40) return theme.warning
119
- return theme.error
120
- }
121
-
122
- // Last 10 rates as a memo for proper reactivity
123
- const recentRates = createMemo(() => {
124
- const rates = props.recentRates || []
125
- return rates.slice(-10)
126
- })
127
-
128
- // Pre-compute the sparkline to avoid For reactivity issues
129
- const sparkline = createMemo(() => {
130
- return recentRates().map((rate, i) => ({
131
- key: i,
132
- rate,
133
- char: percentToBar(rate),
134
- color: getRateColor(rate),
135
- }))
136
- })
137
-
138
- return (
139
- <>
140
- {/* Wheel indicator with percentage and face */}
141
- <box flexDirection="row" gap={1}>
142
- <text style={{ fg: rateColor() }}>{pieIndicator()}</text>
143
- <text fg={theme.textMuted}>
144
- {props.hitRate.toFixed(1)}% hit rate
145
- </text>
146
- <text>{faceIndicator()}</text>
147
- </box>
148
- {/* Progress bar with sparkline bar chart */}
149
- <box flexDirection="row" gap={1}>
150
- <text>
151
- <span style={{ fg: rateColor() }}>{progressBar()}</span>
152
- </text>
153
- <text>
154
- <For each={sparkline()}>
155
- {(item) => (
156
- <span style={{ fg: item.color }}>{item.char}</span>
157
- )}
158
- </For>
159
- </text>
160
- </box>
161
- {/* Token counts */}
162
- <text fg={theme.textMuted}>
163
- {props.cachedTokens.toLocaleString()} / {props.promptTokens.toLocaleString()} tokens
164
- </text>
165
- </>
166
- )
167
- }
168
-
169
- export function Sidebar(props: { sessionID: string }) {
170
- const sync = useSync()
171
- const { theme } = useTheme()
172
- const toast = useToast()
173
- const local = useLocal()
174
- const session = createMemo(() => sync.session.get(props.sessionID)!)
175
- const diff = createMemo(() => sync.data.session_diff[props.sessionID] ?? [])
176
- const todo = createMemo(() => sync.data.todo[props.sessionID] ?? [])
177
- const messages = createMemo(() => sync.data.message[props.sessionID] ?? [])
178
-
179
- // Track whether we've shown the low cache warning for this session
180
- const [hasShownCacheWarning, setHasShownCacheWarning] = createSignal(false)
181
- const [lastMessageCount, setLastMessageCount] = createSignal(0)
182
-
183
- const [expanded, setExpanded] = createStore({
184
- mcp: true,
185
- diff: true,
186
- todo: true,
187
- lsp: true,
188
- })
189
-
190
- // Sort MCP servers alphabetically for consistent display order
191
- const mcpEntries = createMemo(() => Object.entries(sync.data.mcp).sort(([a], [b]) => a.localeCompare(b)))
192
-
193
- const usage = createMemo(() => {
194
- const now = Date.now()
195
- const assistants = messages().filter((m) => m.role === "assistant")
196
- const total = assistants.length
197
- const countWithin = (ms: number) =>
198
- assistants.filter((m) => {
199
- const t = m.time?.completed ?? m.time?.created ?? 0
200
- return now - t <= ms
201
- }).length
202
- return {
203
- total,
204
- min1: countWithin(60_000),
205
- hour1: countWithin(60 * 60_000),
206
- day1: countWithin(24 * 60 * 60_000),
207
- }
208
- })
209
-
210
- const context = createMemo(() => {
211
- const last = messages().findLast((x) => x.role === "assistant" && x.tokens.output > 0) as AssistantMessage
212
- if (!last) return
213
- const total =
214
- last.tokens.input + last.tokens.output + last.tokens.reasoning + last.tokens.cache.read + last.tokens.cache.write
215
- const model = sync.data.provider.find((x) => x.id === last.providerID)?.models[last.modelID]
216
- return {
217
- tokens: total.toLocaleString(),
218
- percentage: model?.limit.context ? Math.round((total / model.limit.context) * 100) : null,
219
- }
220
- })
221
-
222
- // Get rate limit info from sync store (populated via SSE events)
223
- const rateLimitInfo = createMemo(() => {
224
- const currentModel = local.model.current()
225
- if (!currentModel?.providerID) return undefined
226
- return sync.data.ratelimit[currentModel.providerID]
227
- })
228
-
229
-
230
- const cacheStats = createMemo(() => {
231
- const assistants = messages().filter((m) => m.role === "assistant") as AssistantMessage[]
232
- let totalCachedTokens = 0
233
- let totalPromptTokens = 0
234
- for (const msg of assistants) {
235
- // Total prompt = input + cached (input may be non-cached portion only)
236
- const cached = msg.tokens.cache.read
237
- const total = msg.tokens.input + cached
238
- totalCachedTokens += cached
239
- totalPromptTokens += total
240
- }
241
- const hitRate = totalPromptTokens > 0 ? (totalCachedTokens / totalPromptTokens) * 100 : 0
242
- return {
243
- promptTokens: totalPromptTokens,
244
- cachedTokens: totalCachedTokens,
245
- hitRate: hitRate.toFixed(1),
246
- }
247
- })
248
-
249
- // Calculate per-message cache hit rates for completed assistant messages
250
- const perMessageCacheRates = createMemo(() => {
251
- const assistants = messages().filter(
252
- (m) => m.role === "assistant" && m.time.completed
253
- ) as AssistantMessage[]
254
- return assistants.map((msg) => {
255
- const cached = msg.tokens.cache.read
256
- const total = msg.tokens.input + cached
257
- return total > 0 ? (cached / total) * 100 : 0
258
- })
259
- })
260
-
261
- // Monitor for consecutive low cache hit rates
262
- createEffect(() => {
263
- const rates = perMessageCacheRates()
264
- const currentCount = rates.length
265
-
266
- // Only check when we have new completed messages
267
- if (currentCount <= lastMessageCount()) {
268
- return
269
- }
270
- setLastMessageCount(currentCount)
271
-
272
- if (rates.length < CONSECUTIVE_LOW_COUNT) {
273
- return
274
- }
275
-
276
- const lastNRates = rates.slice(-CONSECUTIVE_LOW_COUNT)
277
- const allBelowThreshold = lastNRates.every((rate) => rate < LOW_CACHE_HIT_THRESHOLD)
278
-
279
- if (allBelowThreshold && !hasShownCacheWarning()) {
280
- setHasShownCacheWarning(true)
281
- toast.show({
282
- variant: "warning",
283
- title: "Low Cache Hit Rate",
284
- message: `Cache hit rate has been below ${LOW_CACHE_HIT_THRESHOLD}% for the last ${CONSECUTIVE_LOW_COUNT} requests. This may increase costs and latency.`,
285
- duration: 8000,
286
- })
287
- }
288
-
289
- if (!allBelowThreshold && hasShownCacheWarning()) {
290
- const lastNAboveThreshold = lastNRates.every((rate) => rate >= LOW_CACHE_HIT_THRESHOLD)
291
- if (lastNAboveThreshold) {
292
- setHasShownCacheWarning(false)
293
- }
294
- }
295
- })
296
-
297
- const keybind = useKeybind()
298
- const directory = useDirectory()
299
-
300
- const hasProviders = createMemo(() =>
301
- sync.data.provider.some((x) => x.id !== "opencode" || Object.values(x.models).some((y) => y.cost?.input !== 0)),
302
- )
303
-
304
- return (
305
- <Show when={session()}>
306
- <box
307
- backgroundColor={theme.backgroundPanel}
308
- width={42}
309
- paddingTop={1}
310
- paddingBottom={1}
311
- paddingLeft={2}
312
- paddingRight={2}
313
- >
314
- <scrollbox flexGrow={1}>
315
- <box flexShrink={0} gap={1} paddingRight={1}>
316
- <box>
317
- <text fg={theme.text}>
318
- <b>{session().title}</b>
319
- </text>
320
- <Show when={session().share?.url}>
321
- <text fg={theme.textMuted}>{session().share!.url}</text>
322
- </Show>
323
- </box>
324
- <box>
325
- <text fg={theme.text}>
326
- <b>Context</b>
327
- </text>
328
- <text fg={theme.textMuted}>{context()?.tokens ?? 0} tokens</text>
329
- <text fg={theme.textMuted}>{context()?.percentage ?? 0}% used</text>
330
- <text fg={theme.textMuted}>
331
- Requests: {usage().total} (1m {usage().min1} / 1h {usage().hour1} / 24h {usage().day1})
332
- </text>
333
- </box>
334
- <Show when={cacheStats().promptTokens > 0}>
335
- <box>
336
- <text fg={theme.text}>
337
- <b>Cache</b>
338
- </text>
339
- <CacheVisual
340
- hitRate={parseFloat(cacheStats().hitRate)}
341
- cachedTokens={cacheStats().cachedTokens}
342
- promptTokens={cacheStats().promptTokens}
343
- recentRates={perMessageCacheRates()}
344
- />
345
- </box>
346
- </Show>
347
- <Show when={rateLimitInfo()}>
348
- <box>
349
- <text fg={theme.text}>
350
- <b>Rate Limits</b>
351
- </text>
352
- <box flexDirection="row" gap={2}>
353
- {/* Tokens column */}
354
- <box flexGrow={1} gap={1}>
355
- <text fg={theme.textMuted}>Tokens</text>
356
- <For each={rateLimitInfo()?.tokenLimits || []}>
357
- {(windowInfo) => (
358
- <RateLimitRow
359
- remaining={windowInfo.remaining}
360
- limit={windowInfo.limit}
361
- window={windowInfo.window}
362
- />
363
- )}
364
- </For>
365
- </box>
366
- {/* Requests column */}
367
- <box flexGrow={1} gap={1}>
368
- <text fg={theme.textMuted}>Requests</text>
369
- <For each={rateLimitInfo()?.requestLimits || []}>
370
- {(windowInfo) => (
371
- <RateLimitRow
372
- remaining={windowInfo.remaining}
373
- limit={windowInfo.limit}
374
- window={windowInfo.window}
375
- />
376
- )}
377
- </For>
378
- </box>
379
- </box>
380
- </box>
381
- </Show>
382
- <Show when={mcpEntries().length > 0}>
383
- <box>
384
- <box
385
- flexDirection="row"
386
- gap={1}
387
- onMouseDown={() => mcpEntries().length > 2 && setExpanded("mcp", !expanded.mcp)}
388
- >
389
- <Show when={mcpEntries().length > 2}>
390
- <text fg={theme.text}>{expanded.mcp ? "▼" : "▶"}</text>
391
- </Show>
392
- <text fg={theme.text}>
393
- <b>MCP</b>
394
- </text>
395
- </box>
396
- <Show when={mcpEntries().length <= 2 || expanded.mcp}>
397
- <For each={mcpEntries()}>
398
- {([key, item]) => (
399
- <box flexDirection="row" gap={1}>
400
- <text
401
- flexShrink={0}
402
- style={{
403
- fg: (
404
- {
405
- connected: theme.success,
406
- failed: theme.error,
407
- disabled: theme.textMuted,
408
- needs_auth: theme.warning,
409
- needs_client_registration: theme.error,
410
- } as Record<string, typeof theme.success>
411
- )[item.status],
412
- }}
413
- >
414
-
415
- </text>
416
- <text fg={theme.text} wrapMode="word">
417
- {key}{" "}
418
- <span style={{ fg: theme.textMuted }}>
419
- <Switch fallback={item.status}>
420
- <Match when={item.status === "connected"}>Connected</Match>
421
- <Match when={item.status === "failed" && item}>{(val) => <i>{val().error}</i>}</Match>
422
- <Match when={item.status === "disabled"}>Disabled</Match>
423
- <Match when={(item.status as string) === "needs_auth"}>Needs auth</Match>
424
- <Match when={(item.status as string) === "needs_client_registration"}>
425
- Needs client ID
426
- </Match>
427
- </Switch>
428
- </span>
429
- </text>
430
- </box>
431
- )}
432
- </For>
433
- </Show>
434
- </box>
435
- </Show>
436
- <box>
437
- <box
438
- flexDirection="row"
439
- gap={1}
440
- onMouseDown={() => sync.data.lsp.length > 2 && setExpanded("lsp", !expanded.lsp)}
441
- >
442
- <Show when={sync.data.lsp.length > 2}>
443
- <text fg={theme.text}>{expanded.lsp ? "▼" : "▶"}</text>
444
- </Show>
445
- <text fg={theme.text}>
446
- <b>LSP</b>
447
- </text>
448
- </box>
449
- <Show when={sync.data.lsp.length <= 2 || expanded.lsp}>
450
- <Show when={sync.data.lsp.length === 0}>
451
- <text fg={theme.textMuted}>LSPs will activate as files are read</text>
452
- </Show>
453
- <For each={sync.data.lsp}>
454
- {(item) => (
455
- <box flexDirection="row" gap={1}>
456
- <text
457
- flexShrink={0}
458
- style={{
459
- fg: {
460
- connected: theme.success,
461
- error: theme.error,
462
- }[item.status],
463
- }}
464
- >
465
-
466
- </text>
467
- <text fg={theme.textMuted}>
468
- {item.id} {item.root}
469
- </text>
470
- </box>
471
- )}
472
- </For>
473
- </Show>
474
- </box>
475
- <Show when={todo().length > 0 && todo().some((t) => t.status !== "completed")}>
476
- <box>
477
- <box
478
- flexDirection="row"
479
- gap={1}
480
- onMouseDown={() => todo().length > 2 && setExpanded("todo", !expanded.todo)}
481
- >
482
- <Show when={todo().length > 2}>
483
- <text fg={theme.text}>{expanded.todo ? "▼" : "▶"}</text>
484
- </Show>
485
- <text fg={theme.text}>
486
- <b>Todo</b>
487
- </text>
488
- </box>
489
- <Show when={todo().length <= 2 || expanded.todo}>
490
- <For each={todo()}>
491
- {(todo) => (
492
- <text style={{ fg: todo.status === "in_progress" ? theme.success : theme.textMuted }}>
493
- [{todo.status === "completed" ? "✓" : " "}] {todo.content}
494
- </text>
495
- )}
496
- </For>
497
- </Show>
498
- </box>
499
- </Show>
500
- <Show when={diff().length > 0}>
501
- <box>
502
- <box
503
- flexDirection="row"
504
- gap={1}
505
- onMouseDown={() => diff().length > 2 && setExpanded("diff", !expanded.diff)}
506
- >
507
- <Show when={diff().length > 2}>
508
- <text fg={theme.text}>{expanded.diff ? "▼" : "▶"}</text>
509
- </Show>
510
- <text fg={theme.text}>
511
- <b>Modified Files</b>
512
- </text>
513
- </box>
514
- <Show when={diff().length <= 2 || expanded.diff}>
515
- <For each={diff() || []}>
516
- {(item) => {
517
- const file = createMemo(() => {
518
- const splits = item.file.split(path.sep).filter(Boolean)
519
- const last = splits.at(-1)!
520
- const rest = splits.slice(0, -1).join(path.sep)
521
- if (!rest) return last
522
- return Locale.truncateMiddle(rest, 30 - last.length) + "/" + last
523
- })
524
- return (
525
- <box flexDirection="row" gap={1} justifyContent="space-between">
526
- <text fg={theme.textMuted} wrapMode="char">
527
- {file()}
528
- </text>
529
- <box flexDirection="row" gap={1} flexShrink={0}>
530
- <Show when={item.additions}>
531
- <text fg={theme.diffAdded}>+{item.additions}</text>
532
- </Show>
533
- <Show when={item.deletions}>
534
- <text fg={theme.diffRemoved}>-{item.deletions}</text>
535
- </Show>
536
- </box>
537
- </box>
538
- )
539
- }}
540
- </For>
541
- </Show>
542
- </box>
543
- </Show>
544
- </box>
545
- </scrollbox>
546
-
547
- <box flexShrink={0} gap={1} paddingTop={1}>
548
- <Show when={!hasProviders()}>
549
- <box
550
- backgroundColor={theme.backgroundElement}
551
- paddingTop={1}
552
- paddingBottom={1}
553
- paddingLeft={2}
554
- paddingRight={2}
555
- flexDirection="row"
556
- gap={1}
557
- >
558
- <text flexShrink={0}>⬖</text>
559
- <box flexGrow={1} gap={1}>
560
- <text>
561
- <b>Getting started</b>
562
- </text>
563
- <text fg={theme.textMuted}>OpenCode includes free models so you can start immediately.</text>
564
- <text fg={theme.textMuted}>
565
- Connect from 75+ providers to use other models, including Claude, GPT, Gemini etc
566
- </text>
567
- <box flexDirection="row" gap={1} justifyContent="space-between">
568
- <text>Connect provider</text>
569
- <text fg={theme.textMuted}>/connect</text>
570
- </box>
571
- </box>
572
- </box>
573
- </Show>
574
- <text fg={theme.text}>{directory()}</text>
575
- <text fg={theme.textMuted}>
576
- <span style={{ fg: theme.success }}>•</span> <b>Open</b>
577
- <span style={{ fg: theme.text }}>
578
- <b>Code</b>
579
- </span>{" "}
580
- <span>{Installation.VERSION}</span>
581
- </text>
582
- </box>
583
- </box>
584
- </Show>
585
- )
586
- }
@@ -1,60 +0,0 @@
1
- import { cmd } from "@/cli/cmd/cmd"
2
- import { Instance } from "@/project/instance"
3
- import path from "path"
4
- import { Server } from "@/server/server"
5
- import { upgrade } from "@/cli/upgrade"
6
-
7
- export const TuiSpawnCommand = cmd({
8
- command: "spawn [project]",
9
- builder: (yargs) =>
10
- yargs
11
- .positional("project", {
12
- type: "string",
13
- describe: "path to start opencode in",
14
- })
15
- .option("port", {
16
- type: "number",
17
- describe: "port to listen on",
18
- default: 0,
19
- })
20
- .option("hostname", {
21
- type: "string",
22
- describe: "hostname to listen on",
23
- default: "127.0.0.1",
24
- }),
25
- handler: async (args) => {
26
- upgrade()
27
- const server = Server.listen({
28
- port: args.port,
29
- hostname: "127.0.0.1",
30
- })
31
- const bin = process.execPath
32
- const cmd = []
33
- let cwd = process.cwd()
34
- if (bin.endsWith("bun")) {
35
- cmd.push(
36
- process.execPath,
37
- "run",
38
- "--conditions",
39
- "browser",
40
- new URL("../../../index.ts", import.meta.url).pathname,
41
- )
42
- cwd = new URL("../../../../", import.meta.url).pathname
43
- } else cmd.push(process.execPath)
44
- cmd.push("attach", server.url.toString(), "--dir", args.project ? path.resolve(args.project) : process.cwd())
45
- const proc = Bun.spawn({
46
- cmd,
47
- cwd,
48
- stdout: "inherit",
49
- stderr: "inherit",
50
- stdin: "inherit",
51
- env: {
52
- ...process.env,
53
- BUN_OPTIONS: "",
54
- },
55
- })
56
- await proc.exited
57
- await Instance.disposeAll()
58
- await server.stop(true)
59
- },
60
- })