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,503 +0,0 @@
1
- import { test, expect } from "bun:test"
2
- import { Config } from "../../src/config/config"
3
- import { Instance } from "../../src/project/instance"
4
- import { tmpdir } from "../fixture/fixture"
5
- import path from "path"
6
- import fs from "fs/promises"
7
- import { pathToFileURL } from "url"
8
-
9
- test("loads config with defaults when no files exist", async () => {
10
- await using tmp = await tmpdir()
11
- await Instance.provide({
12
- directory: tmp.path,
13
- fn: async () => {
14
- const config = await Config.get()
15
- expect(config.username).toBeDefined()
16
- },
17
- })
18
- })
19
-
20
- test("loads JSON config file", async () => {
21
- await using tmp = await tmpdir({
22
- init: async (dir) => {
23
- await Bun.write(
24
- path.join(dir, "opencode.json"),
25
- JSON.stringify({
26
- $schema: "https://opencode.ai/config.json",
27
- model: "test/model",
28
- username: "testuser",
29
- }),
30
- )
31
- },
32
- })
33
- await Instance.provide({
34
- directory: tmp.path,
35
- fn: async () => {
36
- const config = await Config.get()
37
- expect(config.model).toBe("test/model")
38
- expect(config.username).toBe("testuser")
39
- },
40
- })
41
- })
42
-
43
- test("loads JSONC config file", async () => {
44
- await using tmp = await tmpdir({
45
- init: async (dir) => {
46
- await Bun.write(
47
- path.join(dir, "opencode.jsonc"),
48
- `{
49
- // This is a comment
50
- "$schema": "https://opencode.ai/config.json",
51
- "model": "test/model",
52
- "username": "testuser"
53
- }`,
54
- )
55
- },
56
- })
57
- await Instance.provide({
58
- directory: tmp.path,
59
- fn: async () => {
60
- const config = await Config.get()
61
- expect(config.model).toBe("test/model")
62
- expect(config.username).toBe("testuser")
63
- },
64
- })
65
- })
66
-
67
- test("merges multiple config files with correct precedence", async () => {
68
- await using tmp = await tmpdir({
69
- init: async (dir) => {
70
- await Bun.write(
71
- path.join(dir, "opencode.jsonc"),
72
- JSON.stringify({
73
- $schema: "https://opencode.ai/config.json",
74
- model: "base",
75
- username: "base",
76
- }),
77
- )
78
- await Bun.write(
79
- path.join(dir, "opencode.json"),
80
- JSON.stringify({
81
- $schema: "https://opencode.ai/config.json",
82
- model: "override",
83
- }),
84
- )
85
- },
86
- })
87
- await Instance.provide({
88
- directory: tmp.path,
89
- fn: async () => {
90
- const config = await Config.get()
91
- expect(config.model).toBe("override")
92
- expect(config.username).toBe("base")
93
- },
94
- })
95
- })
96
-
97
- test("handles environment variable substitution", async () => {
98
- const originalEnv = process.env["TEST_VAR"]
99
- process.env["TEST_VAR"] = "test_theme"
100
-
101
- try {
102
- await using tmp = await tmpdir({
103
- init: async (dir) => {
104
- await Bun.write(
105
- path.join(dir, "opencode.json"),
106
- JSON.stringify({
107
- $schema: "https://opencode.ai/config.json",
108
- theme: "{env:TEST_VAR}",
109
- }),
110
- )
111
- },
112
- })
113
- await Instance.provide({
114
- directory: tmp.path,
115
- fn: async () => {
116
- const config = await Config.get()
117
- expect(config.theme).toBe("test_theme")
118
- },
119
- })
120
- } finally {
121
- if (originalEnv !== undefined) {
122
- process.env["TEST_VAR"] = originalEnv
123
- } else {
124
- delete process.env["TEST_VAR"]
125
- }
126
- }
127
- })
128
-
129
- test("handles file inclusion substitution", async () => {
130
- await using tmp = await tmpdir({
131
- init: async (dir) => {
132
- await Bun.write(path.join(dir, "included.txt"), "test_theme")
133
- await Bun.write(
134
- path.join(dir, "opencode.json"),
135
- JSON.stringify({
136
- $schema: "https://opencode.ai/config.json",
137
- theme: "{file:included.txt}",
138
- }),
139
- )
140
- },
141
- })
142
- await Instance.provide({
143
- directory: tmp.path,
144
- fn: async () => {
145
- const config = await Config.get()
146
- expect(config.theme).toBe("test_theme")
147
- },
148
- })
149
- })
150
-
151
- test("validates config schema and throws on invalid fields", async () => {
152
- await using tmp = await tmpdir({
153
- init: async (dir) => {
154
- await Bun.write(
155
- path.join(dir, "opencode.json"),
156
- JSON.stringify({
157
- $schema: "https://opencode.ai/config.json",
158
- invalid_field: "should cause error",
159
- }),
160
- )
161
- },
162
- })
163
- await Instance.provide({
164
- directory: tmp.path,
165
- fn: async () => {
166
- // Strict schema should throw an error for invalid fields
167
- await expect(Config.get()).rejects.toThrow()
168
- },
169
- })
170
- })
171
-
172
- test("throws error for invalid JSON", async () => {
173
- await using tmp = await tmpdir({
174
- init: async (dir) => {
175
- await Bun.write(path.join(dir, "opencode.json"), "{ invalid json }")
176
- },
177
- })
178
- await Instance.provide({
179
- directory: tmp.path,
180
- fn: async () => {
181
- await expect(Config.get()).rejects.toThrow()
182
- },
183
- })
184
- })
185
-
186
- test("handles agent configuration", async () => {
187
- await using tmp = await tmpdir({
188
- init: async (dir) => {
189
- await Bun.write(
190
- path.join(dir, "opencode.json"),
191
- JSON.stringify({
192
- $schema: "https://opencode.ai/config.json",
193
- agent: {
194
- test_agent: {
195
- model: "test/model",
196
- temperature: 0.7,
197
- description: "test agent",
198
- },
199
- },
200
- }),
201
- )
202
- },
203
- })
204
- await Instance.provide({
205
- directory: tmp.path,
206
- fn: async () => {
207
- const config = await Config.get()
208
- expect(config.agent?.["test_agent"]).toEqual({
209
- model: "test/model",
210
- temperature: 0.7,
211
- description: "test agent",
212
- })
213
- },
214
- })
215
- })
216
-
217
- test("handles command configuration", async () => {
218
- await using tmp = await tmpdir({
219
- init: async (dir) => {
220
- await Bun.write(
221
- path.join(dir, "opencode.json"),
222
- JSON.stringify({
223
- $schema: "https://opencode.ai/config.json",
224
- command: {
225
- test_command: {
226
- template: "test template",
227
- description: "test command",
228
- agent: "test_agent",
229
- },
230
- },
231
- }),
232
- )
233
- },
234
- })
235
- await Instance.provide({
236
- directory: tmp.path,
237
- fn: async () => {
238
- const config = await Config.get()
239
- expect(config.command?.["test_command"]).toEqual({
240
- template: "test template",
241
- description: "test command",
242
- agent: "test_agent",
243
- })
244
- },
245
- })
246
- })
247
-
248
- test("migrates autoshare to share field", async () => {
249
- await using tmp = await tmpdir({
250
- init: async (dir) => {
251
- await Bun.write(
252
- path.join(dir, "opencode.json"),
253
- JSON.stringify({
254
- $schema: "https://opencode.ai/config.json",
255
- autoshare: true,
256
- }),
257
- )
258
- },
259
- })
260
- await Instance.provide({
261
- directory: tmp.path,
262
- fn: async () => {
263
- const config = await Config.get()
264
- expect(config.share).toBe("auto")
265
- expect(config.autoshare).toBe(true)
266
- },
267
- })
268
- })
269
-
270
- test("migrates mode field to agent field", async () => {
271
- await using tmp = await tmpdir({
272
- init: async (dir) => {
273
- await Bun.write(
274
- path.join(dir, "opencode.json"),
275
- JSON.stringify({
276
- $schema: "https://opencode.ai/config.json",
277
- mode: {
278
- test_mode: {
279
- model: "test/model",
280
- temperature: 0.5,
281
- },
282
- },
283
- }),
284
- )
285
- },
286
- })
287
- await Instance.provide({
288
- directory: tmp.path,
289
- fn: async () => {
290
- const config = await Config.get()
291
- expect(config.agent?.["test_mode"]).toEqual({
292
- model: "test/model",
293
- temperature: 0.5,
294
- mode: "primary",
295
- })
296
- },
297
- })
298
- })
299
-
300
- test("loads config from .opencode directory", async () => {
301
- await using tmp = await tmpdir({
302
- init: async (dir) => {
303
- const opencodeDir = path.join(dir, ".opencode")
304
- await fs.mkdir(opencodeDir, { recursive: true })
305
- const agentDir = path.join(opencodeDir, "agent")
306
- await fs.mkdir(agentDir, { recursive: true })
307
-
308
- await Bun.write(
309
- path.join(agentDir, "test.md"),
310
- `---
311
- model: test/model
312
- ---
313
- Test agent prompt`,
314
- )
315
- },
316
- })
317
- await Instance.provide({
318
- directory: tmp.path,
319
- fn: async () => {
320
- const config = await Config.get()
321
- expect(config.agent?.["test"]).toEqual({
322
- name: "test",
323
- model: "test/model",
324
- prompt: "Test agent prompt",
325
- })
326
- },
327
- })
328
- })
329
-
330
- test("updates config and writes to file", async () => {
331
- await using tmp = await tmpdir()
332
- await Instance.provide({
333
- directory: tmp.path,
334
- fn: async () => {
335
- const newConfig = { model: "updated/model" }
336
- await Config.update(newConfig as any)
337
-
338
- const writtenConfig = JSON.parse(await Bun.file(path.join(tmp.path, "config.json")).text())
339
- expect(writtenConfig.model).toBe("updated/model")
340
- },
341
- })
342
- })
343
-
344
- test("gets config directories", async () => {
345
- await using tmp = await tmpdir()
346
- await Instance.provide({
347
- directory: tmp.path,
348
- fn: async () => {
349
- const dirs = await Config.directories()
350
- expect(dirs.length).toBeGreaterThanOrEqual(1)
351
- },
352
- })
353
- })
354
-
355
- test("resolves scoped npm plugins in config", async () => {
356
- await using tmp = await tmpdir({
357
- init: async (dir) => {
358
- const pluginDir = path.join(dir, "node_modules", "@scope", "plugin")
359
- await fs.mkdir(pluginDir, { recursive: true })
360
-
361
- await Bun.write(
362
- path.join(dir, "package.json"),
363
- JSON.stringify({ name: "config-fixture", version: "1.0.0", type: "module" }, null, 2),
364
- )
365
-
366
- await Bun.write(
367
- path.join(pluginDir, "package.json"),
368
- JSON.stringify(
369
- {
370
- name: "@scope/plugin",
371
- version: "1.0.0",
372
- type: "module",
373
- main: "./index.js",
374
- },
375
- null,
376
- 2,
377
- ),
378
- )
379
-
380
- await Bun.write(path.join(pluginDir, "index.js"), "export default {}\n")
381
-
382
- await Bun.write(
383
- path.join(dir, "opencode.json"),
384
- JSON.stringify({ $schema: "https://opencode.ai/config.json", plugin: ["@scope/plugin"] }, null, 2),
385
- )
386
- },
387
- })
388
-
389
- await Instance.provide({
390
- directory: tmp.path,
391
- fn: async () => {
392
- const config = await Config.get()
393
- const pluginEntries = config.plugin ?? []
394
-
395
- const baseUrl = pathToFileURL(path.join(tmp.path, "opencode.json")).href
396
- const expected = import.meta.resolve("@scope/plugin", baseUrl)
397
-
398
- expect(pluginEntries.includes(expected)).toBe(true)
399
-
400
- const scopedEntry = pluginEntries.find((entry) => entry === expected)
401
- expect(scopedEntry).toBeDefined()
402
- expect(scopedEntry?.includes("/node_modules/@scope/plugin/")).toBe(true)
403
- },
404
- })
405
- })
406
-
407
- test("merges plugin arrays from global and local configs", async () => {
408
- await using tmp = await tmpdir({
409
- init: async (dir) => {
410
- // Create a nested project structure with local .opencode config
411
- const projectDir = path.join(dir, "project")
412
- const opencodeDir = path.join(projectDir, ".opencode")
413
- await fs.mkdir(opencodeDir, { recursive: true })
414
-
415
- // Global config with plugins
416
- await Bun.write(
417
- path.join(dir, "opencode.json"),
418
- JSON.stringify({
419
- $schema: "https://opencode.ai/config.json",
420
- plugin: ["global-plugin-1", "global-plugin-2"],
421
- }),
422
- )
423
-
424
- // Local .opencode config with different plugins
425
- await Bun.write(
426
- path.join(opencodeDir, "opencode.json"),
427
- JSON.stringify({
428
- $schema: "https://opencode.ai/config.json",
429
- plugin: ["local-plugin-1"],
430
- }),
431
- )
432
- },
433
- })
434
-
435
- await Instance.provide({
436
- directory: path.join(tmp.path, "project"),
437
- fn: async () => {
438
- const config = await Config.get()
439
- const plugins = config.plugin ?? []
440
-
441
- // Should contain both global and local plugins
442
- expect(plugins.some((p) => p.includes("global-plugin-1"))).toBe(true)
443
- expect(plugins.some((p) => p.includes("global-plugin-2"))).toBe(true)
444
- expect(plugins.some((p) => p.includes("local-plugin-1"))).toBe(true)
445
-
446
- // Should have all 3 plugins (not replaced, but merged)
447
- const pluginNames = plugins.filter((p) => p.includes("global-plugin") || p.includes("local-plugin"))
448
- expect(pluginNames.length).toBeGreaterThanOrEqual(3)
449
- },
450
- })
451
- })
452
-
453
- test("deduplicates duplicate plugins from global and local configs", async () => {
454
- await using tmp = await tmpdir({
455
- init: async (dir) => {
456
- // Create a nested project structure with local .opencode config
457
- const projectDir = path.join(dir, "project")
458
- const opencodeDir = path.join(projectDir, ".opencode")
459
- await fs.mkdir(opencodeDir, { recursive: true })
460
-
461
- // Global config with plugins
462
- await Bun.write(
463
- path.join(dir, "opencode.json"),
464
- JSON.stringify({
465
- $schema: "https://opencode.ai/config.json",
466
- plugin: ["duplicate-plugin", "global-plugin-1"],
467
- }),
468
- )
469
-
470
- // Local .opencode config with some overlapping plugins
471
- await Bun.write(
472
- path.join(opencodeDir, "opencode.json"),
473
- JSON.stringify({
474
- $schema: "https://opencode.ai/config.json",
475
- plugin: ["duplicate-plugin", "local-plugin-1"],
476
- }),
477
- )
478
- },
479
- })
480
-
481
- await Instance.provide({
482
- directory: path.join(tmp.path, "project"),
483
- fn: async () => {
484
- const config = await Config.get()
485
- const plugins = config.plugin ?? []
486
-
487
- // Should contain all unique plugins
488
- expect(plugins.some((p) => p.includes("global-plugin-1"))).toBe(true)
489
- expect(plugins.some((p) => p.includes("local-plugin-1"))).toBe(true)
490
- expect(plugins.some((p) => p.includes("duplicate-plugin"))).toBe(true)
491
-
492
- // Should deduplicate the duplicate plugin
493
- const duplicatePlugins = plugins.filter((p) => p.includes("duplicate-plugin"))
494
- expect(duplicatePlugins.length).toBe(1)
495
-
496
- // Should have exactly 3 unique plugins
497
- const pluginNames = plugins.filter(
498
- (p) => p.includes("global-plugin") || p.includes("local-plugin") || p.includes("duplicate-plugin"),
499
- )
500
- expect(pluginNames.length).toBe(3)
501
- },
502
- })
503
- })
@@ -1,89 +0,0 @@
1
- import { expect, test } from "bun:test"
2
- import { ConfigMarkdown } from "../../src/config/markdown"
3
-
4
- const template = `This is a @valid/path/to/a/file and it should also match at
5
- the beginning of a line:
6
-
7
- @another-valid/path/to/a/file
8
-
9
- but this is not:
10
-
11
- - Adds a "Co-authored-by:" footer which clarifies which AI agent
12
- helped create this commit, using an appropriate \`noreply@...\`
13
- or \`noreply@anthropic.com\` email address.
14
-
15
- We also need to deal with files followed by @commas, ones
16
- with @file-extensions.md, even @multiple.extensions.bak,
17
- hidden directorys like @.config/ or files like @.bashrc
18
- and ones at the end of a sentence like @foo.md.
19
-
20
- Also shouldn't forget @/absolute/paths.txt with and @/without/extensions,
21
- as well as @~/home-files and @~/paths/under/home.txt.
22
-
23
- If the reference is \`@quoted/in/backticks\` then it shouldn't match at all.`
24
-
25
- const matches = ConfigMarkdown.files(template)
26
-
27
- test("should extract exactly 12 file references", () => {
28
- expect(matches.length).toBe(12)
29
- })
30
-
31
- test("should extract valid/path/to/a/file", () => {
32
- expect(matches[0][1]).toBe("valid/path/to/a/file")
33
- })
34
-
35
- test("should extract another-valid/path/to/a/file", () => {
36
- expect(matches[1][1]).toBe("another-valid/path/to/a/file")
37
- })
38
-
39
- test("should extract paths ignoring comma after", () => {
40
- expect(matches[2][1]).toBe("commas")
41
- })
42
-
43
- test("should extract a path with a file extension and comma after", () => {
44
- expect(matches[3][1]).toBe("file-extensions.md")
45
- })
46
-
47
- test("should extract a path with multiple dots and comma after", () => {
48
- expect(matches[4][1]).toBe("multiple.extensions.bak")
49
- })
50
-
51
- test("should extract hidden directory", () => {
52
- expect(matches[5][1]).toBe(".config/")
53
- })
54
-
55
- test("should extract hidden file", () => {
56
- expect(matches[6][1]).toBe(".bashrc")
57
- })
58
-
59
- test("should extract a file ignoring period at end of sentence", () => {
60
- expect(matches[7][1]).toBe("foo.md")
61
- })
62
-
63
- test("should extract an absolute path with an extension", () => {
64
- expect(matches[8][1]).toBe("/absolute/paths.txt")
65
- })
66
-
67
- test("should extract an absolute path without an extension", () => {
68
- expect(matches[9][1]).toBe("/without/extensions")
69
- })
70
-
71
- test("should extract an absolute path in home directory", () => {
72
- expect(matches[10][1]).toBe("~/home-files")
73
- })
74
-
75
- test("should extract an absolute path under home directory", () => {
76
- expect(matches[11][1]).toBe("~/paths/under/home.txt")
77
- })
78
-
79
- test("should not match when preceded by backtick", () => {
80
- const backtickTest = "This `@should/not/match` should be ignored"
81
- const backtickMatches = ConfigMarkdown.files(backtickTest)
82
- expect(backtickMatches.length).toBe(0)
83
- })
84
-
85
- test("should not match email addresses", () => {
86
- const emailTest = "Contact user@example.com for help"
87
- const emailMatches = ConfigMarkdown.files(emailTest)
88
- expect(emailMatches.length).toBe(0)
89
- })
@@ -1,10 +0,0 @@
1
- import { test, expect } from "bun:test"
2
- import { FileIgnore } from "../../src/file/ignore"
3
-
4
- test("match nested and non-nested", () => {
5
- expect(FileIgnore.match("node_modules/index.js")).toBe(true)
6
- expect(FileIgnore.match("node_modules")).toBe(true)
7
- expect(FileIgnore.match("node_modules/")).toBe(true)
8
- expect(FileIgnore.match("node_modules/bar")).toBe(true)
9
- expect(FileIgnore.match("node_modules/bar/")).toBe(true)
10
- })
@@ -1,28 +0,0 @@
1
- import { $ } from "bun"
2
- import { realpathSync } from "fs"
3
- import os from "os"
4
- import path from "path"
5
-
6
- type TmpDirOptions<T> = {
7
- git?: boolean
8
- init?: (dir: string) => Promise<T>
9
- dispose?: (dir: string) => Promise<T>
10
- }
11
- export async function tmpdir<T>(options?: TmpDirOptions<T>) {
12
- const dirpath = path.join(os.tmpdir(), "opencode-test-" + Math.random().toString(36).slice(2))
13
- await $`mkdir -p ${dirpath}`.quiet()
14
- if (options?.git) {
15
- await $`git init`.cwd(dirpath).quiet()
16
- await $`git commit --allow-empty -m "root commit ${dirpath}"`.cwd(dirpath).quiet()
17
- }
18
- const extra = await options?.init?.(dirpath)
19
- const result = {
20
- [Symbol.asyncDispose]: async () => {
21
- await options?.dispose?.(dirpath)
22
- await $`rm -rf ${dirpath}`.quiet()
23
- },
24
- path: realpathSync(dirpath),
25
- extra: extra as T,
26
- }
27
- return result
28
- }
@@ -1,77 +0,0 @@
1
- // Simple JSON-RPC 2.0 LSP-like fake server over stdio
2
- // Implements a minimal LSP handshake and triggers a request upon notification
3
-
4
- const net = require("net")
5
-
6
- let nextId = 1
7
-
8
- function encode(message) {
9
- const json = JSON.stringify(message)
10
- const header = `Content-Length: ${Buffer.byteLength(json, "utf8")}\r\n\r\n`
11
- return Buffer.concat([Buffer.from(header, "utf8"), Buffer.from(json, "utf8")])
12
- }
13
-
14
- function decodeFrames(buffer) {
15
- const results = []
16
- let idx
17
- while ((idx = buffer.indexOf("\r\n\r\n")) !== -1) {
18
- const header = buffer.slice(0, idx).toString("utf8")
19
- const m = /Content-Length:\s*(\d+)/i.exec(header)
20
- const len = m ? parseInt(m[1], 10) : 0
21
- const bodyStart = idx + 4
22
- const bodyEnd = bodyStart + len
23
- if (buffer.length < bodyEnd) break
24
- const body = buffer.slice(bodyStart, bodyEnd).toString("utf8")
25
- results.push(body)
26
- buffer = buffer.slice(bodyEnd)
27
- }
28
- return { messages: results, rest: buffer }
29
- }
30
-
31
- let readBuffer = Buffer.alloc(0)
32
-
33
- process.stdin.on("data", (chunk) => {
34
- readBuffer = Buffer.concat([readBuffer, chunk])
35
- const { messages, rest } = decodeFrames(readBuffer)
36
- readBuffer = rest
37
- for (const m of messages) handle(m)
38
- })
39
-
40
- function send(msg) {
41
- process.stdout.write(encode(msg))
42
- }
43
-
44
- function sendRequest(method, params) {
45
- const id = nextId++
46
- send({ jsonrpc: "2.0", id, method, params })
47
- return id
48
- }
49
-
50
- function handle(raw) {
51
- let data
52
- try {
53
- data = JSON.parse(raw)
54
- } catch {
55
- return
56
- }
57
- if (data.method === "initialize") {
58
- send({ jsonrpc: "2.0", id: data.id, result: { capabilities: {} } })
59
- return
60
- }
61
- if (data.method === "initialized") {
62
- return
63
- }
64
- if (data.method === "workspace/didChangeConfiguration") {
65
- return
66
- }
67
- if (data.method === "test/trigger") {
68
- const method = data.params && data.params.method
69
- if (method) sendRequest(method, {})
70
- return
71
- }
72
- if (typeof data.id !== "undefined") {
73
- // Respond OK to any request from client to keep transport flowing
74
- send({ jsonrpc: "2.0", id: data.id, result: null })
75
- return
76
- }
77
- }