rird 1.0.200

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 (350) hide show
  1. package/AGENTS.md +27 -0
  2. package/Dockerfile +18 -0
  3. package/README.md +15 -0
  4. package/bin/opencode +336 -0
  5. package/bin/pty-wrapper.js +285 -0
  6. package/bunfig.toml +4 -0
  7. package/facebook_ads_library.png +0 -0
  8. package/nul`nif +0 -0
  9. package/package.json +111 -0
  10. package/parsers-config.ts +239 -0
  11. package/rird-1.0.199.tgz +0 -0
  12. package/script/build-windows.ts +54 -0
  13. package/script/build.ts +167 -0
  14. package/script/postinstall.mjs +544 -0
  15. package/script/publish-registries.ts +187 -0
  16. package/script/publish.ts +72 -0
  17. package/script/schema.ts +47 -0
  18. package/src/acp/README.md +164 -0
  19. package/src/acp/agent.ts +1063 -0
  20. package/src/acp/session.ts +101 -0
  21. package/src/acp/types.ts +22 -0
  22. package/src/agent/agent.ts +367 -0
  23. package/src/agent/generate.txt +75 -0
  24. package/src/agent/prompt/compaction.txt +12 -0
  25. package/src/agent/prompt/explore.txt +18 -0
  26. package/src/agent/prompt/summary.txt +10 -0
  27. package/src/agent/prompt/title.txt +36 -0
  28. package/src/auth/index.ts +70 -0
  29. package/src/bun/index.ts +114 -0
  30. package/src/bus/bus-event.ts +43 -0
  31. package/src/bus/global.ts +10 -0
  32. package/src/bus/index.ts +105 -0
  33. package/src/cli/bootstrap.ts +17 -0
  34. package/src/cli/cmd/acp.ts +88 -0
  35. package/src/cli/cmd/agent.ts +256 -0
  36. package/src/cli/cmd/auth.ts +391 -0
  37. package/src/cli/cmd/cmd.ts +7 -0
  38. package/src/cli/cmd/debug/config.ts +15 -0
  39. package/src/cli/cmd/debug/file.ts +91 -0
  40. package/src/cli/cmd/debug/index.ts +43 -0
  41. package/src/cli/cmd/debug/lsp.ts +48 -0
  42. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  43. package/src/cli/cmd/debug/scrap.ts +15 -0
  44. package/src/cli/cmd/debug/skill.ts +15 -0
  45. package/src/cli/cmd/debug/snapshot.ts +48 -0
  46. package/src/cli/cmd/export.ts +88 -0
  47. package/src/cli/cmd/generate.ts +38 -0
  48. package/src/cli/cmd/github.ts +1400 -0
  49. package/src/cli/cmd/import.ts +98 -0
  50. package/src/cli/cmd/mcp.ts +654 -0
  51. package/src/cli/cmd/models.ts +77 -0
  52. package/src/cli/cmd/pr.ts +112 -0
  53. package/src/cli/cmd/run.ts +368 -0
  54. package/src/cli/cmd/serve.ts +31 -0
  55. package/src/cli/cmd/session.ts +106 -0
  56. package/src/cli/cmd/stats.ts +298 -0
  57. package/src/cli/cmd/tui/app.tsx +696 -0
  58. package/src/cli/cmd/tui/attach.ts +30 -0
  59. package/src/cli/cmd/tui/component/border.tsx +21 -0
  60. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  61. package/src/cli/cmd/tui/component/dialog-command.tsx +124 -0
  62. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  63. package/src/cli/cmd/tui/component/dialog-model.tsx +245 -0
  64. package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
  65. package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
  66. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  67. package/src/cli/cmd/tui/component/dialog-stash.tsx +86 -0
  68. package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
  69. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  70. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  71. package/src/cli/cmd/tui/component/did-you-know.tsx +85 -0
  72. package/src/cli/cmd/tui/component/logo.tsx +35 -0
  73. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +574 -0
  74. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  75. package/src/cli/cmd/tui/component/prompt/index.tsx +1090 -0
  76. package/src/cli/cmd/tui/component/prompt/stash.tsx +101 -0
  77. package/src/cli/cmd/tui/component/tips.ts +27 -0
  78. package/src/cli/cmd/tui/component/todo-item.tsx +32 -0
  79. package/src/cli/cmd/tui/context/args.tsx +14 -0
  80. package/src/cli/cmd/tui/context/directory.ts +13 -0
  81. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  82. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  83. package/src/cli/cmd/tui/context/keybind.tsx +101 -0
  84. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  85. package/src/cli/cmd/tui/context/local.tsx +354 -0
  86. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  87. package/src/cli/cmd/tui/context/route.tsx +46 -0
  88. package/src/cli/cmd/tui/context/sdk.tsx +74 -0
  89. package/src/cli/cmd/tui/context/sync.tsx +372 -0
  90. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  91. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  92. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  93. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  94. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  95. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  96. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  97. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  98. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  99. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  100. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  101. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  102. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  103. package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
  104. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  105. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  106. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  107. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  108. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  109. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  110. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  111. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  112. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  113. package/src/cli/cmd/tui/context/theme/rird.json +245 -0
  114. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  115. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  116. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  117. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  118. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  119. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  120. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  121. package/src/cli/cmd/tui/context/theme.tsx +1109 -0
  122. package/src/cli/cmd/tui/event.ts +40 -0
  123. package/src/cli/cmd/tui/routes/home.tsx +138 -0
  124. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
  125. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
  126. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  127. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  128. package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
  129. package/src/cli/cmd/tui/routes/session/header.tsx +125 -0
  130. package/src/cli/cmd/tui/routes/session/index.tsx +1864 -0
  131. package/src/cli/cmd/tui/routes/session/sidebar.tsx +318 -0
  132. package/src/cli/cmd/tui/spawn.ts +60 -0
  133. package/src/cli/cmd/tui/thread.ts +142 -0
  134. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  135. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  136. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  137. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
  138. package/src/cli/cmd/tui/ui/dialog-select.tsx +332 -0
  139. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  140. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  141. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  142. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  143. package/src/cli/cmd/tui/util/editor.ts +32 -0
  144. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  145. package/src/cli/cmd/tui/worker.ts +63 -0
  146. package/src/cli/cmd/uninstall.ts +344 -0
  147. package/src/cli/cmd/upgrade.ts +100 -0
  148. package/src/cli/cmd/web.ts +84 -0
  149. package/src/cli/error.ts +56 -0
  150. package/src/cli/ui.ts +84 -0
  151. package/src/cli/upgrade.ts +25 -0
  152. package/src/command/index.ts +80 -0
  153. package/src/command/template/initialize.txt +10 -0
  154. package/src/command/template/review.txt +97 -0
  155. package/src/config/config.ts +995 -0
  156. package/src/config/markdown.ts +41 -0
  157. package/src/env/index.ts +26 -0
  158. package/src/file/ignore.ts +83 -0
  159. package/src/file/index.ts +328 -0
  160. package/src/file/ripgrep.ts +393 -0
  161. package/src/file/time.ts +64 -0
  162. package/src/file/watcher.ts +103 -0
  163. package/src/flag/flag.ts +46 -0
  164. package/src/format/formatter.ts +315 -0
  165. package/src/format/index.ts +137 -0
  166. package/src/global/index.ts +52 -0
  167. package/src/id/id.ts +73 -0
  168. package/src/ide/index.ts +76 -0
  169. package/src/index.ts +240 -0
  170. package/src/installation/index.ts +239 -0
  171. package/src/lsp/client.ts +229 -0
  172. package/src/lsp/index.ts +485 -0
  173. package/src/lsp/language.ts +116 -0
  174. package/src/lsp/server.ts +1895 -0
  175. package/src/mcp/auth.ts +135 -0
  176. package/src/mcp/index.ts +690 -0
  177. package/src/mcp/oauth-callback.ts +200 -0
  178. package/src/mcp/oauth-provider.ts +154 -0
  179. package/src/patch/index.ts +622 -0
  180. package/src/permission/index.ts +199 -0
  181. package/src/plugin/index.ts +91 -0
  182. package/src/project/bootstrap.ts +31 -0
  183. package/src/project/instance.ts +78 -0
  184. package/src/project/project.ts +221 -0
  185. package/src/project/state.ts +65 -0
  186. package/src/project/vcs.ts +76 -0
  187. package/src/provider/auth.ts +143 -0
  188. package/src/provider/models-macro.ts +11 -0
  189. package/src/provider/models.ts +106 -0
  190. package/src/provider/provider.ts +1071 -0
  191. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  192. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  193. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +100 -0
  194. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  195. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  196. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  197. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  198. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  199. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  200. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  201. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  202. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  203. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  204. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  205. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  206. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  207. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  208. package/src/provider/transform.ts +455 -0
  209. package/src/pty/index.ts +231 -0
  210. package/src/security/guardrails.test.ts +341 -0
  211. package/src/security/guardrails.ts +558 -0
  212. package/src/security/index.ts +19 -0
  213. package/src/server/error.ts +36 -0
  214. package/src/server/project.ts +79 -0
  215. package/src/server/server.ts +2642 -0
  216. package/src/server/tui.ts +71 -0
  217. package/src/session/compaction.ts +223 -0
  218. package/src/session/index.ts +461 -0
  219. package/src/session/llm.ts +201 -0
  220. package/src/session/message-v2.ts +690 -0
  221. package/src/session/message.ts +189 -0
  222. package/src/session/processor.ts +409 -0
  223. package/src/session/prompt/act-switch.txt +5 -0
  224. package/src/session/prompt/anthropic-20250930.txt +166 -0
  225. package/src/session/prompt/anthropic.txt +85 -0
  226. package/src/session/prompt/anthropic_spoof.txt +1 -0
  227. package/src/session/prompt/beast.txt +103 -0
  228. package/src/session/prompt/codex.txt +304 -0
  229. package/src/session/prompt/copilot-gpt-5.txt +138 -0
  230. package/src/session/prompt/gemini.txt +85 -0
  231. package/src/session/prompt/max-steps.txt +16 -0
  232. package/src/session/prompt/plan-reminder-anthropic.txt +35 -0
  233. package/src/session/prompt/plan.txt +24 -0
  234. package/src/session/prompt/polaris.txt +84 -0
  235. package/src/session/prompt/qwen.txt +106 -0
  236. package/src/session/prompt.ts +1509 -0
  237. package/src/session/retry.ts +86 -0
  238. package/src/session/revert.ts +108 -0
  239. package/src/session/sensitive-filter.test.ts +327 -0
  240. package/src/session/sensitive-filter.ts +466 -0
  241. package/src/session/status.ts +76 -0
  242. package/src/session/summary.ts +194 -0
  243. package/src/session/system.ts +120 -0
  244. package/src/session/todo.ts +37 -0
  245. package/src/share/share-next.ts +194 -0
  246. package/src/share/share.ts +87 -0
  247. package/src/shell/shell.ts +67 -0
  248. package/src/skill/index.ts +1 -0
  249. package/src/skill/skill.ts +83 -0
  250. package/src/snapshot/index.ts +197 -0
  251. package/src/storage/storage.ts +226 -0
  252. package/src/tests/agent.test.ts +308 -0
  253. package/src/tests/build-guards.test.ts +267 -0
  254. package/src/tests/config.test.ts +664 -0
  255. package/src/tests/tool-registry.test.ts +589 -0
  256. package/src/tool/bash.ts +317 -0
  257. package/src/tool/bash.txt +158 -0
  258. package/src/tool/batch.ts +175 -0
  259. package/src/tool/batch.txt +24 -0
  260. package/src/tool/codesearch.ts +168 -0
  261. package/src/tool/codesearch.txt +12 -0
  262. package/src/tool/edit.ts +675 -0
  263. package/src/tool/edit.txt +10 -0
  264. package/src/tool/glob.ts +65 -0
  265. package/src/tool/glob.txt +6 -0
  266. package/src/tool/grep.ts +121 -0
  267. package/src/tool/grep.txt +8 -0
  268. package/src/tool/invalid.ts +17 -0
  269. package/src/tool/ls.ts +110 -0
  270. package/src/tool/ls.txt +1 -0
  271. package/src/tool/lsp-diagnostics.ts +26 -0
  272. package/src/tool/lsp-diagnostics.txt +1 -0
  273. package/src/tool/lsp-hover.ts +31 -0
  274. package/src/tool/lsp-hover.txt +1 -0
  275. package/src/tool/lsp.ts +87 -0
  276. package/src/tool/lsp.txt +19 -0
  277. package/src/tool/multiedit.ts +46 -0
  278. package/src/tool/multiedit.txt +41 -0
  279. package/src/tool/patch.ts +233 -0
  280. package/src/tool/patch.txt +1 -0
  281. package/src/tool/read.ts +219 -0
  282. package/src/tool/read.txt +12 -0
  283. package/src/tool/registry.ts +162 -0
  284. package/src/tool/skill.ts +100 -0
  285. package/src/tool/task.ts +136 -0
  286. package/src/tool/task.txt +51 -0
  287. package/src/tool/todo.ts +39 -0
  288. package/src/tool/todoread.txt +14 -0
  289. package/src/tool/todowrite.txt +167 -0
  290. package/src/tool/tool.ts +71 -0
  291. package/src/tool/webfetch.ts +198 -0
  292. package/src/tool/webfetch.txt +13 -0
  293. package/src/tool/websearch.ts +180 -0
  294. package/src/tool/websearch.txt +11 -0
  295. package/src/tool/write.ts +110 -0
  296. package/src/tool/write.txt +8 -0
  297. package/src/util/archive.ts +16 -0
  298. package/src/util/color.ts +19 -0
  299. package/src/util/context.ts +25 -0
  300. package/src/util/defer.ts +12 -0
  301. package/src/util/eventloop.ts +20 -0
  302. package/src/util/filesystem.ts +83 -0
  303. package/src/util/fn.ts +11 -0
  304. package/src/util/iife.ts +3 -0
  305. package/src/util/keybind.ts +102 -0
  306. package/src/util/lazy.ts +11 -0
  307. package/src/util/license.ts +325 -0
  308. package/src/util/locale.ts +81 -0
  309. package/src/util/lock.ts +98 -0
  310. package/src/util/log.ts +180 -0
  311. package/src/util/queue.ts +32 -0
  312. package/src/util/rpc.ts +42 -0
  313. package/src/util/scrap.ts +10 -0
  314. package/src/util/signal.ts +12 -0
  315. package/src/util/timeout.ts +14 -0
  316. package/src/util/token.ts +7 -0
  317. package/src/util/wildcard.ts +54 -0
  318. package/sst-env.d.ts +9 -0
  319. package/test/agent/agent.test.ts +146 -0
  320. package/test/bun.test.ts +53 -0
  321. package/test/cli/github-remote.test.ts +80 -0
  322. package/test/config/agent-color.test.ts +66 -0
  323. package/test/config/config.test.ts +535 -0
  324. package/test/config/markdown.test.ts +89 -0
  325. package/test/file/ignore.test.ts +10 -0
  326. package/test/fixture/fixture.ts +36 -0
  327. package/test/fixture/lsp/fake-lsp-server.js +77 -0
  328. package/test/ide/ide.test.ts +82 -0
  329. package/test/keybind.test.ts +421 -0
  330. package/test/lsp/client.test.ts +95 -0
  331. package/test/mcp/headers.test.ts +153 -0
  332. package/test/patch/patch.test.ts +348 -0
  333. package/test/preload.ts +57 -0
  334. package/test/project/project.test.ts +72 -0
  335. package/test/provider/provider.test.ts +1809 -0
  336. package/test/provider/transform.test.ts +411 -0
  337. package/test/session/retry.test.ts +111 -0
  338. package/test/session/session.test.ts +71 -0
  339. package/test/skill/skill.test.ts +131 -0
  340. package/test/snapshot/snapshot.test.ts +939 -0
  341. package/test/tool/__snapshots__/tool.test.ts.snap +9 -0
  342. package/test/tool/bash.test.ts +434 -0
  343. package/test/tool/grep.test.ts +108 -0
  344. package/test/tool/patch.test.ts +259 -0
  345. package/test/tool/read.test.ts +42 -0
  346. package/test/util/iife.test.ts +36 -0
  347. package/test/util/lazy.test.ts +50 -0
  348. package/test/util/timeout.test.ts +21 -0
  349. package/test/util/wildcard.test.ts +55 -0
  350. package/tsconfig.json +16 -0
@@ -0,0 +1,664 @@
1
+ import { describe, test, expect } from "bun:test"
2
+ import z from "zod"
3
+ import { Config } from "../config/config"
4
+
5
+ describe("Config.Info schema", () => {
6
+ test("config schema has expected top-level fields", () => {
7
+ const shape = Config.Info.shape
8
+
9
+ expect(shape.$schema).toBeDefined()
10
+ expect(shape.theme).toBeDefined()
11
+ expect(shape.keybinds).toBeDefined()
12
+ expect(shape.command).toBeDefined()
13
+ expect(shape.plugin).toBeDefined()
14
+ expect(shape.model).toBeDefined()
15
+ expect(shape.agent).toBeDefined()
16
+ expect(shape.provider).toBeDefined()
17
+ expect(shape.mcp).toBeDefined()
18
+ expect(shape.permission).toBeDefined()
19
+ expect(shape.tools).toBeDefined()
20
+ })
21
+
22
+ test("empty config is valid", () => {
23
+ const result = Config.Info.safeParse({})
24
+ expect(result.success).toBe(true)
25
+ })
26
+
27
+ test("config with schema field validates", () => {
28
+ const config = {
29
+ $schema: "https://rird.ai/config.json",
30
+ }
31
+
32
+ const result = Config.Info.safeParse(config)
33
+ expect(result.success).toBe(true)
34
+ })
35
+
36
+ test("config with model string validates", () => {
37
+ const config = {
38
+ model: "anthropic/claude-3-opus",
39
+ }
40
+
41
+ const result = Config.Info.safeParse(config)
42
+ expect(result.success).toBe(true)
43
+ if (result.success) {
44
+ expect(result.data.model).toBe("anthropic/claude-3-opus")
45
+ }
46
+ })
47
+
48
+ test("config with invalid field fails strict validation", () => {
49
+ const config = {
50
+ invalid_field: "should fail",
51
+ }
52
+
53
+ const result = Config.Info.safeParse(config)
54
+ expect(result.success).toBe(false)
55
+ })
56
+ })
57
+
58
+ describe("Config.Permission enum", () => {
59
+ test("permission has correct values", () => {
60
+ const validValues = ["ask", "allow", "deny"]
61
+
62
+ for (const value of validValues) {
63
+ const result = Config.Permission.safeParse(value)
64
+ expect(result.success).toBe(true)
65
+ }
66
+ })
67
+
68
+ test("invalid permission value fails", () => {
69
+ const result = Config.Permission.safeParse("invalid")
70
+ expect(result.success).toBe(false)
71
+ })
72
+ })
73
+
74
+ describe("Config.Agent schema", () => {
75
+ test("agent config with model validates", () => {
76
+ const agent = {
77
+ model: "openai/gpt-4",
78
+ }
79
+
80
+ const result = Config.Agent.safeParse(agent)
81
+ expect(result.success).toBe(true)
82
+ })
83
+
84
+ test("agent config with temperature validates", () => {
85
+ const agent = {
86
+ temperature: 0.7,
87
+ }
88
+
89
+ const result = Config.Agent.safeParse(agent)
90
+ expect(result.success).toBe(true)
91
+ if (result.success) {
92
+ expect(result.data.temperature).toBe(0.7)
93
+ }
94
+ })
95
+
96
+ test("agent config with top_p validates", () => {
97
+ const agent = {
98
+ top_p: 0.9,
99
+ }
100
+
101
+ const result = Config.Agent.safeParse(agent)
102
+ expect(result.success).toBe(true)
103
+ if (result.success) {
104
+ expect(result.data.top_p).toBe(0.9)
105
+ }
106
+ })
107
+
108
+ test("agent config with prompt validates", () => {
109
+ const agent = {
110
+ prompt: "You are a helpful coding assistant",
111
+ }
112
+
113
+ const result = Config.Agent.safeParse(agent)
114
+ expect(result.success).toBe(true)
115
+ if (result.success) {
116
+ expect(result.data.prompt).toBe("You are a helpful coding assistant")
117
+ }
118
+ })
119
+
120
+ test("agent config with tools validates", () => {
121
+ const agent = {
122
+ tools: {
123
+ bash: true,
124
+ edit: false,
125
+ read: true,
126
+ },
127
+ }
128
+
129
+ const result = Config.Agent.safeParse(agent)
130
+ expect(result.success).toBe(true)
131
+ if (result.success) {
132
+ expect(result.data.tools?.bash).toBe(true)
133
+ expect(result.data.tools?.edit).toBe(false)
134
+ }
135
+ })
136
+
137
+ test("agent config with disable flag validates", () => {
138
+ const agent = {
139
+ disable: true,
140
+ }
141
+
142
+ const result = Config.Agent.safeParse(agent)
143
+ expect(result.success).toBe(true)
144
+ if (result.success) {
145
+ expect(result.data.disable).toBe(true)
146
+ }
147
+ })
148
+
149
+ test("agent config with mode validates", () => {
150
+ const modes = ["subagent", "primary", "all"]
151
+
152
+ for (const mode of modes) {
153
+ const agent = { mode }
154
+ const result = Config.Agent.safeParse(agent)
155
+ expect(result.success).toBe(true)
156
+ }
157
+ })
158
+
159
+ test("agent config with invalid mode fails", () => {
160
+ const agent = {
161
+ mode: "invalid_mode",
162
+ }
163
+
164
+ const result = Config.Agent.safeParse(agent)
165
+ expect(result.success).toBe(false)
166
+ })
167
+
168
+ test("agent config with color validates hex format", () => {
169
+ const validColors = ["#FF5733", "#000000", "#FFFFFF", "#abc123"]
170
+
171
+ for (const color of validColors) {
172
+ const agent = { color }
173
+ const result = Config.Agent.safeParse(agent)
174
+ expect(result.success).toBe(true)
175
+ }
176
+ })
177
+
178
+ test("agent config with invalid color fails", () => {
179
+ const invalidColors = ["red", "#FFF", "FF5733", "#GGGGGG"]
180
+
181
+ for (const color of invalidColors) {
182
+ const agent = { color }
183
+ const result = Config.Agent.safeParse(agent)
184
+ expect(result.success).toBe(false)
185
+ }
186
+ })
187
+
188
+ test("agent config with maxSteps validates positive integer", () => {
189
+ const agent = {
190
+ maxSteps: 50,
191
+ }
192
+
193
+ const result = Config.Agent.safeParse(agent)
194
+ expect(result.success).toBe(true)
195
+ if (result.success) {
196
+ expect(result.data.maxSteps).toBe(50)
197
+ }
198
+ })
199
+
200
+ test("agent config with permission validates", () => {
201
+ const agent = {
202
+ permission: {
203
+ edit: "allow",
204
+ bash: "ask",
205
+ webfetch: "deny",
206
+ },
207
+ }
208
+
209
+ const result = Config.Agent.safeParse(agent)
210
+ expect(result.success).toBe(true)
211
+ if (result.success) {
212
+ expect(result.data.permission?.edit).toBe("allow")
213
+ expect(result.data.permission?.bash).toBe("ask")
214
+ }
215
+ })
216
+
217
+ test("agent config with bash permission record validates", () => {
218
+ const agent = {
219
+ permission: {
220
+ bash: {
221
+ "*": "deny",
222
+ "echo *": "allow",
223
+ "ls *": "allow",
224
+ },
225
+ },
226
+ }
227
+
228
+ const result = Config.Agent.safeParse(agent)
229
+ expect(result.success).toBe(true)
230
+ if (result.success) {
231
+ expect(result.data.permission?.bash).toEqual({
232
+ "*": "deny",
233
+ "echo *": "allow",
234
+ "ls *": "allow",
235
+ })
236
+ }
237
+ })
238
+
239
+ test("agent config allows additional properties", () => {
240
+ const agent = {
241
+ model: "test/model",
242
+ customProperty: "custom value",
243
+ }
244
+
245
+ const result = Config.Agent.safeParse(agent)
246
+ expect(result.success).toBe(true)
247
+ })
248
+ })
249
+
250
+ describe("Config.Command schema", () => {
251
+ test("command with template validates", () => {
252
+ const command = {
253
+ template: "Run npm test",
254
+ }
255
+
256
+ const result = Config.Command.safeParse(command)
257
+ expect(result.success).toBe(true)
258
+ if (result.success) {
259
+ expect(result.data.template).toBe("Run npm test")
260
+ }
261
+ })
262
+
263
+ test("command with all fields validates", () => {
264
+ const command = {
265
+ template: "Run the build process",
266
+ description: "Builds the project",
267
+ agent: "act",
268
+ model: "anthropic/claude-3-opus",
269
+ subtask: true,
270
+ }
271
+
272
+ const result = Config.Command.safeParse(command)
273
+ expect(result.success).toBe(true)
274
+ if (result.success) {
275
+ expect(result.data.template).toBe("Run the build process")
276
+ expect(result.data.description).toBe("Builds the project")
277
+ expect(result.data.agent).toBe("act")
278
+ expect(result.data.subtask).toBe(true)
279
+ }
280
+ })
281
+
282
+ test("command without template fails", () => {
283
+ const command = {
284
+ description: "Missing template",
285
+ }
286
+
287
+ const result = Config.Command.safeParse(command)
288
+ expect(result.success).toBe(false)
289
+ })
290
+ })
291
+
292
+ describe("Config.Mcp schemas", () => {
293
+ test("local mcp config validates", () => {
294
+ const mcp = {
295
+ type: "local" as const,
296
+ command: ["node", "server.js"],
297
+ }
298
+
299
+ const result = Config.McpLocal.safeParse(mcp)
300
+ expect(result.success).toBe(true)
301
+ if (result.success) {
302
+ expect(result.data.type).toBe("local")
303
+ expect(result.data.command).toEqual(["node", "server.js"])
304
+ }
305
+ })
306
+
307
+ test("local mcp with environment validates", () => {
308
+ const mcp = {
309
+ type: "local" as const,
310
+ command: ["python", "-m", "server"],
311
+ environment: {
312
+ API_KEY: "secret",
313
+ DEBUG: "true",
314
+ },
315
+ }
316
+
317
+ const result = Config.McpLocal.safeParse(mcp)
318
+ expect(result.success).toBe(true)
319
+ if (result.success) {
320
+ expect(result.data.environment?.API_KEY).toBe("secret")
321
+ }
322
+ })
323
+
324
+ test("local mcp with enabled flag validates", () => {
325
+ const mcp = {
326
+ type: "local" as const,
327
+ command: ["npx", "mcp-server"],
328
+ enabled: false,
329
+ }
330
+
331
+ const result = Config.McpLocal.safeParse(mcp)
332
+ expect(result.success).toBe(true)
333
+ if (result.success) {
334
+ expect(result.data.enabled).toBe(false)
335
+ }
336
+ })
337
+
338
+ test("local mcp with timeout validates", () => {
339
+ const mcp = {
340
+ type: "local" as const,
341
+ command: ["mcp"],
342
+ timeout: 10000,
343
+ }
344
+
345
+ const result = Config.McpLocal.safeParse(mcp)
346
+ expect(result.success).toBe(true)
347
+ if (result.success) {
348
+ expect(result.data.timeout).toBe(10000)
349
+ }
350
+ })
351
+
352
+ test("remote mcp config validates", () => {
353
+ const mcp = {
354
+ type: "remote" as const,
355
+ url: "https://mcp.example.com/api",
356
+ }
357
+
358
+ const result = Config.McpRemote.safeParse(mcp)
359
+ expect(result.success).toBe(true)
360
+ if (result.success) {
361
+ expect(result.data.type).toBe("remote")
362
+ expect(result.data.url).toBe("https://mcp.example.com/api")
363
+ }
364
+ })
365
+
366
+ test("remote mcp with headers validates", () => {
367
+ const mcp = {
368
+ type: "remote" as const,
369
+ url: "https://mcp.example.com",
370
+ headers: {
371
+ Authorization: "Bearer token123",
372
+ "Content-Type": "application/json",
373
+ },
374
+ }
375
+
376
+ const result = Config.McpRemote.safeParse(mcp)
377
+ expect(result.success).toBe(true)
378
+ if (result.success) {
379
+ expect(result.data.headers?.Authorization).toBe("Bearer token123")
380
+ }
381
+ })
382
+
383
+ test("remote mcp with oauth config validates", () => {
384
+ const mcp = {
385
+ type: "remote" as const,
386
+ url: "https://mcp.example.com",
387
+ oauth: {
388
+ clientId: "client123",
389
+ clientSecret: "secret456",
390
+ scope: "read write",
391
+ },
392
+ }
393
+
394
+ const result = Config.McpRemote.safeParse(mcp)
395
+ expect(result.success).toBe(true)
396
+ if (result.success) {
397
+ expect(result.data.oauth).toEqual({
398
+ clientId: "client123",
399
+ clientSecret: "secret456",
400
+ scope: "read write",
401
+ })
402
+ }
403
+ })
404
+
405
+ test("remote mcp with oauth disabled validates", () => {
406
+ const mcp = {
407
+ type: "remote" as const,
408
+ url: "https://mcp.example.com",
409
+ oauth: false,
410
+ }
411
+
412
+ const result = Config.McpRemote.safeParse(mcp)
413
+ expect(result.success).toBe(true)
414
+ if (result.success) {
415
+ expect(result.data.oauth).toBe(false)
416
+ }
417
+ })
418
+
419
+ test("discriminated union Mcp validates local", () => {
420
+ const mcp = {
421
+ type: "local" as const,
422
+ command: ["test"],
423
+ }
424
+
425
+ const result = Config.Mcp.safeParse(mcp)
426
+ expect(result.success).toBe(true)
427
+ })
428
+
429
+ test("discriminated union Mcp validates remote", () => {
430
+ const mcp = {
431
+ type: "remote" as const,
432
+ url: "https://example.com",
433
+ }
434
+
435
+ const result = Config.Mcp.safeParse(mcp)
436
+ expect(result.success).toBe(true)
437
+ })
438
+ })
439
+
440
+ describe("Config.Keybinds schema", () => {
441
+ test("keybinds has sensible defaults", () => {
442
+ const result = Config.Keybinds.safeParse({})
443
+ expect(result.success).toBe(true)
444
+ if (result.success) {
445
+ expect(result.data.leader).toBe("ctrl+x")
446
+ expect(result.data.app_exit).toBe("ctrl+c,ctrl+d,<leader>q")
447
+ expect(result.data.session_new).toBe("<leader>n")
448
+ expect(result.data.input_submit).toBe("return")
449
+ }
450
+ })
451
+
452
+ test("keybinds can be customized", () => {
453
+ const keybinds = {
454
+ leader: "ctrl+a",
455
+ app_exit: "ctrl+q",
456
+ }
457
+
458
+ const result = Config.Keybinds.safeParse(keybinds)
459
+ expect(result.success).toBe(true)
460
+ if (result.success) {
461
+ expect(result.data.leader).toBe("ctrl+a")
462
+ expect(result.data.app_exit).toBe("ctrl+q")
463
+ }
464
+ })
465
+
466
+ test("keybinds rejects invalid fields (strict mode)", () => {
467
+ const keybinds = {
468
+ invalid_keybind: "ctrl+x",
469
+ }
470
+
471
+ const result = Config.Keybinds.safeParse(keybinds)
472
+ expect(result.success).toBe(false)
473
+ })
474
+ })
475
+
476
+ describe("Config.Provider schema", () => {
477
+ test("provider with options validates", () => {
478
+ const provider = {
479
+ options: {
480
+ apiKey: "sk-123",
481
+ baseURL: "https://api.example.com",
482
+ },
483
+ }
484
+
485
+ const result = Config.Provider.safeParse(provider)
486
+ expect(result.success).toBe(true)
487
+ if (result.success) {
488
+ expect(result.data.options?.apiKey).toBe("sk-123")
489
+ expect(result.data.options?.baseURL).toBe("https://api.example.com")
490
+ }
491
+ })
492
+
493
+ test("provider with whitelist validates", () => {
494
+ const provider = {
495
+ whitelist: ["gpt-4", "gpt-4-turbo"],
496
+ }
497
+
498
+ const result = Config.Provider.safeParse(provider)
499
+ expect(result.success).toBe(true)
500
+ if (result.success) {
501
+ expect(result.data.whitelist).toEqual(["gpt-4", "gpt-4-turbo"])
502
+ }
503
+ })
504
+
505
+ test("provider with blacklist validates", () => {
506
+ const provider = {
507
+ blacklist: ["gpt-3.5-turbo"],
508
+ }
509
+
510
+ const result = Config.Provider.safeParse(provider)
511
+ expect(result.success).toBe(true)
512
+ if (result.success) {
513
+ expect(result.data.blacklist).toEqual(["gpt-3.5-turbo"])
514
+ }
515
+ })
516
+
517
+ test("provider with timeout number validates", () => {
518
+ const provider = {
519
+ options: {
520
+ timeout: 60000,
521
+ },
522
+ }
523
+
524
+ const result = Config.Provider.safeParse(provider)
525
+ expect(result.success).toBe(true)
526
+ if (result.success) {
527
+ expect(result.data.options?.timeout).toBe(60000)
528
+ }
529
+ })
530
+
531
+ test("provider with timeout false validates", () => {
532
+ const provider = {
533
+ options: {
534
+ timeout: false,
535
+ },
536
+ }
537
+
538
+ const result = Config.Provider.safeParse(provider)
539
+ expect(result.success).toBe(true)
540
+ if (result.success) {
541
+ expect(result.data.options?.timeout).toBe(false)
542
+ }
543
+ })
544
+ })
545
+
546
+ describe("Config share field", () => {
547
+ test("share accepts valid values", () => {
548
+ const validValues = ["manual", "auto", "disabled"]
549
+
550
+ for (const share of validValues) {
551
+ const result = Config.Info.safeParse({ share })
552
+ expect(result.success).toBe(true)
553
+ }
554
+ })
555
+
556
+ test("share rejects invalid value", () => {
557
+ const result = Config.Info.safeParse({ share: "invalid" })
558
+ expect(result.success).toBe(false)
559
+ })
560
+ })
561
+
562
+ describe("Config experimental field", () => {
563
+ test("experimental with batch_tool validates", () => {
564
+ const config = {
565
+ experimental: {
566
+ batch_tool: true,
567
+ },
568
+ }
569
+
570
+ const result = Config.Info.safeParse(config)
571
+ expect(result.success).toBe(true)
572
+ if (result.success) {
573
+ expect(result.data.experimental?.batch_tool).toBe(true)
574
+ }
575
+ })
576
+
577
+ test("experimental with openTelemetry validates", () => {
578
+ const config = {
579
+ experimental: {
580
+ openTelemetry: true,
581
+ },
582
+ }
583
+
584
+ const result = Config.Info.safeParse(config)
585
+ expect(result.success).toBe(true)
586
+ if (result.success) {
587
+ expect(result.data.experimental?.openTelemetry).toBe(true)
588
+ }
589
+ })
590
+
591
+ test("experimental with chatMaxRetries validates", () => {
592
+ const config = {
593
+ experimental: {
594
+ chatMaxRetries: 3,
595
+ },
596
+ }
597
+
598
+ const result = Config.Info.safeParse(config)
599
+ expect(result.success).toBe(true)
600
+ if (result.success) {
601
+ expect(result.data.experimental?.chatMaxRetries).toBe(3)
602
+ }
603
+ })
604
+ })
605
+
606
+ describe("Config full example", () => {
607
+ test("comprehensive config validates", () => {
608
+ const config = {
609
+ $schema: "https://rird.ai/config.json",
610
+ theme: "dark",
611
+ model: "anthropic/claude-3-opus",
612
+ small_model: "anthropic/claude-3-haiku",
613
+ username: "developer",
614
+ default_agent: "act",
615
+ share: "auto",
616
+ autoupdate: true,
617
+ snapshot: true,
618
+ disabled_providers: ["google"],
619
+ enabled_providers: ["anthropic", "openai"],
620
+ agent: {
621
+ act: {
622
+ model: "anthropic/claude-3-opus",
623
+ temperature: 0.7,
624
+ },
625
+ plan: {
626
+ disable: true,
627
+ },
628
+ },
629
+ command: {
630
+ test: {
631
+ template: "Run all tests",
632
+ description: "Execute test suite",
633
+ },
634
+ },
635
+ provider: {
636
+ anthropic: {
637
+ options: {
638
+ apiKey: "sk-ant-123",
639
+ },
640
+ },
641
+ },
642
+ permission: {
643
+ edit: "ask",
644
+ bash: {
645
+ "*": "ask",
646
+ "git *": "allow",
647
+ },
648
+ },
649
+ tools: {
650
+ bash: true,
651
+ edit: true,
652
+ },
653
+ }
654
+
655
+ const result = Config.Info.safeParse(config)
656
+ expect(result.success).toBe(true)
657
+ if (result.success) {
658
+ expect(result.data.model).toBe("anthropic/claude-3-opus")
659
+ expect(result.data.username).toBe("developer")
660
+ expect(result.data.agent?.act?.temperature).toBe(0.7)
661
+ expect(result.data.agent?.plan?.disable).toBe(true)
662
+ }
663
+ })
664
+ })