cerebras-cli 1.0.1 → 1.0.3

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 (313) hide show
  1. package/cerebras-cli-1.0.0.tgz +0 -0
  2. package/package.json +7 -88
  3. package/AGENTS.md +0 -27
  4. package/Dockerfile +0 -10
  5. package/bunfig.toml +0 -4
  6. package/parsers-config.ts +0 -239
  7. package/script/build.ts +0 -151
  8. package/script/postinstall.mjs +0 -122
  9. package/script/publish.ts +0 -256
  10. package/script/schema.ts +0 -47
  11. package/src/acp/README.md +0 -164
  12. package/src/acp/agent.ts +0 -812
  13. package/src/acp/session.ts +0 -70
  14. package/src/acp/types.ts +0 -22
  15. package/src/agent/agent.ts +0 -310
  16. package/src/agent/generate.txt +0 -75
  17. package/src/auth/index.ts +0 -70
  18. package/src/bun/index.ts +0 -152
  19. package/src/bus/global.ts +0 -10
  20. package/src/bus/index.ts +0 -142
  21. package/src/cli/bootstrap.ts +0 -17
  22. package/src/cli/cmd/acp.ts +0 -88
  23. package/src/cli/cmd/agent.ts +0 -165
  24. package/src/cli/cmd/auth.ts +0 -369
  25. package/src/cli/cmd/cmd.ts +0 -7
  26. package/src/cli/cmd/debug/config.ts +0 -15
  27. package/src/cli/cmd/debug/file.ts +0 -91
  28. package/src/cli/cmd/debug/index.ts +0 -41
  29. package/src/cli/cmd/debug/lsp.ts +0 -47
  30. package/src/cli/cmd/debug/ripgrep.ts +0 -83
  31. package/src/cli/cmd/debug/scrap.ts +0 -15
  32. package/src/cli/cmd/debug/snapshot.ts +0 -48
  33. package/src/cli/cmd/export.ts +0 -88
  34. package/src/cli/cmd/generate.ts +0 -38
  35. package/src/cli/cmd/github.ts +0 -1200
  36. package/src/cli/cmd/import.ts +0 -98
  37. package/src/cli/cmd/mcp.ts +0 -400
  38. package/src/cli/cmd/models.ts +0 -77
  39. package/src/cli/cmd/pr.ts +0 -112
  40. package/src/cli/cmd/run.ts +0 -342
  41. package/src/cli/cmd/serve.ts +0 -31
  42. package/src/cli/cmd/session.ts +0 -106
  43. package/src/cli/cmd/stats.ts +0 -298
  44. package/src/cli/cmd/tui/app.tsx +0 -732
  45. package/src/cli/cmd/tui/attach.ts +0 -25
  46. package/src/cli/cmd/tui/component/border.tsx +0 -21
  47. package/src/cli/cmd/tui/component/dialog-agent.tsx +0 -31
  48. package/src/cli/cmd/tui/component/dialog-command.tsx +0 -124
  49. package/src/cli/cmd/tui/component/dialog-feedback.tsx +0 -160
  50. package/src/cli/cmd/tui/component/dialog-mcp.tsx +0 -86
  51. package/src/cli/cmd/tui/component/dialog-model.tsx +0 -223
  52. package/src/cli/cmd/tui/component/dialog-notification.tsx +0 -78
  53. package/src/cli/cmd/tui/component/dialog-provider.tsx +0 -222
  54. package/src/cli/cmd/tui/component/dialog-session-list.tsx +0 -97
  55. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +0 -31
  56. package/src/cli/cmd/tui/component/dialog-status.tsx +0 -114
  57. package/src/cli/cmd/tui/component/dialog-tag.tsx +0 -44
  58. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +0 -50
  59. package/src/cli/cmd/tui/component/logo.tsx +0 -37
  60. package/src/cli/cmd/tui/component/notification-banner.tsx +0 -58
  61. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +0 -530
  62. package/src/cli/cmd/tui/component/prompt/history.tsx +0 -107
  63. package/src/cli/cmd/tui/component/prompt/index.tsx +0 -931
  64. package/src/cli/cmd/tui/context/args.tsx +0 -14
  65. package/src/cli/cmd/tui/context/directory.ts +0 -12
  66. package/src/cli/cmd/tui/context/exit.tsx +0 -23
  67. package/src/cli/cmd/tui/context/helper.tsx +0 -25
  68. package/src/cli/cmd/tui/context/keybind.tsx +0 -111
  69. package/src/cli/cmd/tui/context/kv.tsx +0 -49
  70. package/src/cli/cmd/tui/context/local.tsx +0 -339
  71. package/src/cli/cmd/tui/context/prompt.tsx +0 -18
  72. package/src/cli/cmd/tui/context/route.tsx +0 -45
  73. package/src/cli/cmd/tui/context/sdk.tsx +0 -75
  74. package/src/cli/cmd/tui/context/sync.tsx +0 -374
  75. package/src/cli/cmd/tui/context/theme/aura.json +0 -69
  76. package/src/cli/cmd/tui/context/theme/ayu.json +0 -80
  77. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +0 -233
  78. package/src/cli/cmd/tui/context/theme/catppuccin.json +0 -112
  79. package/src/cli/cmd/tui/context/theme/cobalt2.json +0 -228
  80. package/src/cli/cmd/tui/context/theme/dracula.json +0 -219
  81. package/src/cli/cmd/tui/context/theme/everforest.json +0 -241
  82. package/src/cli/cmd/tui/context/theme/flexoki.json +0 -237
  83. package/src/cli/cmd/tui/context/theme/github.json +0 -233
  84. package/src/cli/cmd/tui/context/theme/gruvbox.json +0 -95
  85. package/src/cli/cmd/tui/context/theme/kanagawa.json +0 -77
  86. package/src/cli/cmd/tui/context/theme/material.json +0 -235
  87. package/src/cli/cmd/tui/context/theme/matrix.json +0 -77
  88. package/src/cli/cmd/tui/context/theme/mercury.json +0 -252
  89. package/src/cli/cmd/tui/context/theme/monokai.json +0 -221
  90. package/src/cli/cmd/tui/context/theme/nightowl.json +0 -221
  91. package/src/cli/cmd/tui/context/theme/nord.json +0 -223
  92. package/src/cli/cmd/tui/context/theme/one-dark.json +0 -84
  93. package/src/cli/cmd/tui/context/theme/orng.json +0 -245
  94. package/src/cli/cmd/tui/context/theme/palenight.json +0 -222
  95. package/src/cli/cmd/tui/context/theme/rosepine.json +0 -234
  96. package/src/cli/cmd/tui/context/theme/solarized.json +0 -223
  97. package/src/cli/cmd/tui/context/theme/synthwave84.json +0 -226
  98. package/src/cli/cmd/tui/context/theme/tokyonight.json +0 -243
  99. package/src/cli/cmd/tui/context/theme/vercel.json +0 -245
  100. package/src/cli/cmd/tui/context/theme/vesper.json +0 -218
  101. package/src/cli/cmd/tui/context/theme/zenburn.json +0 -223
  102. package/src/cli/cmd/tui/context/theme.tsx +0 -1077
  103. package/src/cli/cmd/tui/event.ts +0 -39
  104. package/src/cli/cmd/tui/routes/home.tsx +0 -104
  105. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +0 -93
  106. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +0 -37
  107. package/src/cli/cmd/tui/routes/session/footer.tsx +0 -76
  108. package/src/cli/cmd/tui/routes/session/header.tsx +0 -183
  109. package/src/cli/cmd/tui/routes/session/index.tsx +0 -1703
  110. package/src/cli/cmd/tui/routes/session/sidebar.tsx +0 -586
  111. package/src/cli/cmd/tui/spawn.ts +0 -60
  112. package/src/cli/cmd/tui/thread.ts +0 -120
  113. package/src/cli/cmd/tui/ui/dialog-alert.tsx +0 -55
  114. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +0 -81
  115. package/src/cli/cmd/tui/ui/dialog-help.tsx +0 -36
  116. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +0 -75
  117. package/src/cli/cmd/tui/ui/dialog-select.tsx +0 -317
  118. package/src/cli/cmd/tui/ui/dialog.tsx +0 -170
  119. package/src/cli/cmd/tui/ui/spinner.ts +0 -368
  120. package/src/cli/cmd/tui/ui/toast.tsx +0 -100
  121. package/src/cli/cmd/tui/util/clipboard.ts +0 -127
  122. package/src/cli/cmd/tui/util/editor.ts +0 -32
  123. package/src/cli/cmd/tui/util/terminal.ts +0 -114
  124. package/src/cli/cmd/tui/worker.ts +0 -63
  125. package/src/cli/cmd/uninstall.ts +0 -344
  126. package/src/cli/cmd/upgrade.ts +0 -67
  127. package/src/cli/cmd/web.ts +0 -84
  128. package/src/cli/error.ts +0 -55
  129. package/src/cli/ui.ts +0 -84
  130. package/src/cli/upgrade.ts +0 -25
  131. package/src/command/index.ts +0 -79
  132. package/src/command/template/initialize.txt +0 -10
  133. package/src/command/template/review.txt +0 -73
  134. package/src/config/config.ts +0 -886
  135. package/src/config/markdown.ts +0 -41
  136. package/src/env/index.ts +0 -26
  137. package/src/file/fzf.ts +0 -124
  138. package/src/file/ignore.ts +0 -83
  139. package/src/file/index.ts +0 -326
  140. package/src/file/ripgrep.ts +0 -391
  141. package/src/file/time.ts +0 -38
  142. package/src/file/watcher.ts +0 -89
  143. package/src/flag/flag.ts +0 -28
  144. package/src/format/formatter.ts +0 -277
  145. package/src/format/index.ts +0 -137
  146. package/src/global/index.ts +0 -52
  147. package/src/id/id.ts +0 -73
  148. package/src/ide/index.ts +0 -75
  149. package/src/index.ts +0 -158
  150. package/src/installation/index.ts +0 -194
  151. package/src/lsp/client.ts +0 -215
  152. package/src/lsp/index.ts +0 -370
  153. package/src/lsp/language.ts +0 -111
  154. package/src/lsp/server.ts +0 -1327
  155. package/src/mcp/auth.ts +0 -82
  156. package/src/mcp/index.ts +0 -576
  157. package/src/mcp/oauth-callback.ts +0 -203
  158. package/src/mcp/oauth-provider.ts +0 -132
  159. package/src/notification/index.ts +0 -101
  160. package/src/patch/index.ts +0 -622
  161. package/src/permission/index.ts +0 -198
  162. package/src/plugin/index.ts +0 -95
  163. package/src/project/bootstrap.ts +0 -31
  164. package/src/project/instance.ts +0 -68
  165. package/src/project/project.ts +0 -133
  166. package/src/project/state.ts +0 -65
  167. package/src/project/vcs.ts +0 -77
  168. package/src/provider/auth.ts +0 -143
  169. package/src/provider/models-macro.ts +0 -11
  170. package/src/provider/models.ts +0 -93
  171. package/src/provider/provider.ts +0 -996
  172. package/src/provider/sdk/openai-compatible/src/README.md +0 -5
  173. package/src/provider/sdk/openai-compatible/src/index.ts +0 -2
  174. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +0 -100
  175. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +0 -303
  176. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +0 -27
  177. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +0 -18
  178. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +0 -22
  179. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +0 -207
  180. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +0 -1713
  181. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +0 -177
  182. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +0 -1
  183. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +0 -88
  184. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +0 -128
  185. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +0 -115
  186. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +0 -65
  187. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +0 -104
  188. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +0 -103
  189. package/src/provider/transform.ts +0 -406
  190. package/src/pty/index.ts +0 -226
  191. package/src/ratelimit/index.ts +0 -185
  192. package/src/server/error.ts +0 -36
  193. package/src/server/project.ts +0 -50
  194. package/src/server/server.ts +0 -2463
  195. package/src/server/tui.ts +0 -71
  196. package/src/session/compaction.ts +0 -257
  197. package/src/session/index.ts +0 -470
  198. package/src/session/message-v2.ts +0 -641
  199. package/src/session/message.ts +0 -189
  200. package/src/session/processor.ts +0 -443
  201. package/src/session/prompt/anthropic-20250930.txt +0 -166
  202. package/src/session/prompt/anthropic.txt +0 -105
  203. package/src/session/prompt/anthropic_spoof.txt +0 -1
  204. package/src/session/prompt/beast.txt +0 -147
  205. package/src/session/prompt/build-switch.txt +0 -5
  206. package/src/session/prompt/codex.txt +0 -318
  207. package/src/session/prompt/compaction.txt +0 -12
  208. package/src/session/prompt/copilot-gpt-5.txt +0 -143
  209. package/src/session/prompt/gemini.txt +0 -155
  210. package/src/session/prompt/max-steps.txt +0 -16
  211. package/src/session/prompt/plan-reminder-anthropic.txt +0 -67
  212. package/src/session/prompt/plan.txt +0 -26
  213. package/src/session/prompt/polaris.txt +0 -107
  214. package/src/session/prompt/qwen.txt +0 -109
  215. package/src/session/prompt/summarize.txt +0 -4
  216. package/src/session/prompt/title.txt +0 -36
  217. package/src/session/prompt.ts +0 -1541
  218. package/src/session/retry.ts +0 -82
  219. package/src/session/revert.ts +0 -108
  220. package/src/session/status.ts +0 -75
  221. package/src/session/summary.ts +0 -203
  222. package/src/session/system.ts +0 -148
  223. package/src/session/todo.ts +0 -36
  224. package/src/share/share-next.ts +0 -195
  225. package/src/share/share.ts +0 -87
  226. package/src/snapshot/index.ts +0 -197
  227. package/src/storage/storage.ts +0 -226
  228. package/src/telemetry/index.ts +0 -232
  229. package/src/tool/bash.ts +0 -365
  230. package/src/tool/bash.txt +0 -128
  231. package/src/tool/batch.ts +0 -173
  232. package/src/tool/batch.txt +0 -28
  233. package/src/tool/codesearch.ts +0 -138
  234. package/src/tool/codesearch.txt +0 -12
  235. package/src/tool/edit.ts +0 -674
  236. package/src/tool/edit.txt +0 -10
  237. package/src/tool/glob.ts +0 -65
  238. package/src/tool/glob.txt +0 -6
  239. package/src/tool/grep.ts +0 -120
  240. package/src/tool/grep.txt +0 -8
  241. package/src/tool/invalid.ts +0 -17
  242. package/src/tool/ls.ts +0 -110
  243. package/src/tool/ls.txt +0 -1
  244. package/src/tool/lsp-diagnostics.ts +0 -26
  245. package/src/tool/lsp-diagnostics.txt +0 -1
  246. package/src/tool/lsp-hover.ts +0 -31
  247. package/src/tool/lsp-hover.txt +0 -1
  248. package/src/tool/multiedit.ts +0 -46
  249. package/src/tool/multiedit.txt +0 -41
  250. package/src/tool/patch.ts +0 -233
  251. package/src/tool/patch.txt +0 -1
  252. package/src/tool/read.ts +0 -217
  253. package/src/tool/read.txt +0 -12
  254. package/src/tool/registry.ts +0 -148
  255. package/src/tool/task.ts +0 -135
  256. package/src/tool/task.txt +0 -60
  257. package/src/tool/todo.ts +0 -39
  258. package/src/tool/todoread.txt +0 -14
  259. package/src/tool/todowrite.txt +0 -167
  260. package/src/tool/tool.ts +0 -66
  261. package/src/tool/webfetch.ts +0 -187
  262. package/src/tool/webfetch.txt +0 -14
  263. package/src/tool/websearch.ts +0 -150
  264. package/src/tool/websearch.txt +0 -11
  265. package/src/tool/write.ts +0 -99
  266. package/src/tool/write.txt +0 -8
  267. package/src/types/shims.d.ts +0 -3
  268. package/src/util/color.ts +0 -19
  269. package/src/util/context.ts +0 -25
  270. package/src/util/defer.ts +0 -12
  271. package/src/util/eventloop.ts +0 -20
  272. package/src/util/filesystem.ts +0 -69
  273. package/src/util/fn.ts +0 -11
  274. package/src/util/iife.ts +0 -3
  275. package/src/util/keybind.ts +0 -79
  276. package/src/util/lazy.ts +0 -11
  277. package/src/util/locale.ts +0 -81
  278. package/src/util/lock.ts +0 -98
  279. package/src/util/log.ts +0 -177
  280. package/src/util/queue.ts +0 -32
  281. package/src/util/rpc.ts +0 -42
  282. package/src/util/scrap.ts +0 -10
  283. package/src/util/signal.ts +0 -12
  284. package/src/util/timeout.ts +0 -14
  285. package/src/util/token.ts +0 -7
  286. package/src/util/wildcard.ts +0 -54
  287. package/sst-env.d.ts +0 -9
  288. package/test/bun.test.ts +0 -53
  289. package/test/config/agent-color.test.ts +0 -66
  290. package/test/config/config.test.ts +0 -503
  291. package/test/config/markdown.test.ts +0 -89
  292. package/test/file/ignore.test.ts +0 -10
  293. package/test/fixture/fixture.ts +0 -28
  294. package/test/fixture/lsp/fake-lsp-server.js +0 -77
  295. package/test/ide/ide.test.ts +0 -82
  296. package/test/keybind.test.ts +0 -317
  297. package/test/lsp/client.test.ts +0 -95
  298. package/test/patch/patch.test.ts +0 -348
  299. package/test/preload.ts +0 -38
  300. package/test/project/project.test.ts +0 -42
  301. package/test/provider/provider.test.ts +0 -1809
  302. package/test/provider/transform.test.ts +0 -305
  303. package/test/session/retry.test.ts +0 -61
  304. package/test/session/session.test.ts +0 -71
  305. package/test/snapshot/snapshot.test.ts +0 -939
  306. package/test/tool/__snapshots__/tool.test.ts.snap +0 -9
  307. package/test/tool/bash.test.ts +0 -55
  308. package/test/tool/patch.test.ts +0 -259
  309. package/test/util/iife.test.ts +0 -36
  310. package/test/util/lazy.test.ts +0 -50
  311. package/test/util/timeout.test.ts +0 -21
  312. package/test/util/wildcard.test.ts +0 -55
  313. 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
- })