bincode-cli 1.0.1

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 (300) hide show
  1. package/AGENTS.md +27 -0
  2. package/README.md +15 -0
  3. package/bin/bincode +98 -0
  4. package/bunfig.toml +4 -0
  5. package/package.json +124 -0
  6. package/parsers-config.ts +239 -0
  7. package/script/build.ts +167 -0
  8. package/script/postinstall.mjs +206 -0
  9. package/script/publish.ts +99 -0
  10. package/script/schema.ts +47 -0
  11. package/src/acp/README.md +164 -0
  12. package/src/acp/agent.ts +1051 -0
  13. package/src/acp/session.ts +101 -0
  14. package/src/acp/types.ts +22 -0
  15. package/src/agent/agent.ts +398 -0
  16. package/src/agent/generate.txt +75 -0
  17. package/src/agent/prompt/compaction.txt +12 -0
  18. package/src/agent/prompt/explore.txt +18 -0
  19. package/src/agent/prompt/summary.txt +10 -0
  20. package/src/agent/prompt/title.txt +36 -0
  21. package/src/auth/bineric-login.ts +506 -0
  22. package/src/auth/index.ts +70 -0
  23. package/src/bun/index.ts +114 -0
  24. package/src/bus/bus-event.ts +43 -0
  25. package/src/bus/global.ts +10 -0
  26. package/src/bus/index.ts +105 -0
  27. package/src/cli/auth-check.ts +61 -0
  28. package/src/cli/bootstrap.ts +21 -0
  29. package/src/cli/cmd/acp.ts +88 -0
  30. package/src/cli/cmd/agent.ts +256 -0
  31. package/src/cli/cmd/auth.ts +436 -0
  32. package/src/cli/cmd/cmd.ts +7 -0
  33. package/src/cli/cmd/debug/config.ts +15 -0
  34. package/src/cli/cmd/debug/file.ts +91 -0
  35. package/src/cli/cmd/debug/index.ts +43 -0
  36. package/src/cli/cmd/debug/lsp.ts +48 -0
  37. package/src/cli/cmd/debug/ripgrep.ts +83 -0
  38. package/src/cli/cmd/debug/scrap.ts +15 -0
  39. package/src/cli/cmd/debug/skill.ts +15 -0
  40. package/src/cli/cmd/debug/snapshot.ts +48 -0
  41. package/src/cli/cmd/export.ts +88 -0
  42. package/src/cli/cmd/generate.ts +38 -0
  43. package/src/cli/cmd/github.ts +1399 -0
  44. package/src/cli/cmd/import.ts +98 -0
  45. package/src/cli/cmd/login.ts +112 -0
  46. package/src/cli/cmd/logout.ts +38 -0
  47. package/src/cli/cmd/mcp.ts +654 -0
  48. package/src/cli/cmd/models.ts +77 -0
  49. package/src/cli/cmd/pr.ts +112 -0
  50. package/src/cli/cmd/run.ts +368 -0
  51. package/src/cli/cmd/serve.ts +31 -0
  52. package/src/cli/cmd/session.ts +106 -0
  53. package/src/cli/cmd/stats.ts +298 -0
  54. package/src/cli/cmd/tui/app.tsx +669 -0
  55. package/src/cli/cmd/tui/attach.ts +30 -0
  56. package/src/cli/cmd/tui/component/border.tsx +21 -0
  57. package/src/cli/cmd/tui/component/dialog-agent.tsx +31 -0
  58. package/src/cli/cmd/tui/component/dialog-command.tsx +123 -0
  59. package/src/cli/cmd/tui/component/dialog-mcp.tsx +86 -0
  60. package/src/cli/cmd/tui/component/dialog-model.tsx +223 -0
  61. package/src/cli/cmd/tui/component/dialog-provider.tsx +224 -0
  62. package/src/cli/cmd/tui/component/dialog-session-list.tsx +102 -0
  63. package/src/cli/cmd/tui/component/dialog-session-rename.tsx +31 -0
  64. package/src/cli/cmd/tui/component/dialog-status.tsx +162 -0
  65. package/src/cli/cmd/tui/component/dialog-tag.tsx +44 -0
  66. package/src/cli/cmd/tui/component/dialog-theme-list.tsx +50 -0
  67. package/src/cli/cmd/tui/component/logo.tsx +32 -0
  68. package/src/cli/cmd/tui/component/prompt/autocomplete.tsx +560 -0
  69. package/src/cli/cmd/tui/component/prompt/history.tsx +108 -0
  70. package/src/cli/cmd/tui/component/prompt/index.tsx +1052 -0
  71. package/src/cli/cmd/tui/context/args.tsx +14 -0
  72. package/src/cli/cmd/tui/context/directory.ts +13 -0
  73. package/src/cli/cmd/tui/context/exit.tsx +23 -0
  74. package/src/cli/cmd/tui/context/helper.tsx +25 -0
  75. package/src/cli/cmd/tui/context/keybind.tsx +101 -0
  76. package/src/cli/cmd/tui/context/kv.tsx +49 -0
  77. package/src/cli/cmd/tui/context/local.tsx +339 -0
  78. package/src/cli/cmd/tui/context/prompt.tsx +18 -0
  79. package/src/cli/cmd/tui/context/route.tsx +46 -0
  80. package/src/cli/cmd/tui/context/sdk.tsx +74 -0
  81. package/src/cli/cmd/tui/context/sync.tsx +372 -0
  82. package/src/cli/cmd/tui/context/theme/aura.json +69 -0
  83. package/src/cli/cmd/tui/context/theme/ayu.json +80 -0
  84. package/src/cli/cmd/tui/context/theme/bincode.json +245 -0
  85. package/src/cli/cmd/tui/context/theme/catppuccin-frappe.json +233 -0
  86. package/src/cli/cmd/tui/context/theme/catppuccin-macchiato.json +233 -0
  87. package/src/cli/cmd/tui/context/theme/catppuccin.json +112 -0
  88. package/src/cli/cmd/tui/context/theme/cobalt2.json +228 -0
  89. package/src/cli/cmd/tui/context/theme/cursor.json +249 -0
  90. package/src/cli/cmd/tui/context/theme/dracula.json +219 -0
  91. package/src/cli/cmd/tui/context/theme/everforest.json +241 -0
  92. package/src/cli/cmd/tui/context/theme/flexoki.json +237 -0
  93. package/src/cli/cmd/tui/context/theme/github.json +233 -0
  94. package/src/cli/cmd/tui/context/theme/gruvbox.json +95 -0
  95. package/src/cli/cmd/tui/context/theme/kanagawa.json +77 -0
  96. package/src/cli/cmd/tui/context/theme/lucent-orng.json +227 -0
  97. package/src/cli/cmd/tui/context/theme/material.json +235 -0
  98. package/src/cli/cmd/tui/context/theme/matrix.json +77 -0
  99. package/src/cli/cmd/tui/context/theme/mercury.json +252 -0
  100. package/src/cli/cmd/tui/context/theme/monokai.json +221 -0
  101. package/src/cli/cmd/tui/context/theme/nightowl.json +221 -0
  102. package/src/cli/cmd/tui/context/theme/nord.json +223 -0
  103. package/src/cli/cmd/tui/context/theme/one-dark.json +84 -0
  104. package/src/cli/cmd/tui/context/theme/orng.json +245 -0
  105. package/src/cli/cmd/tui/context/theme/palenight.json +222 -0
  106. package/src/cli/cmd/tui/context/theme/rosepine.json +234 -0
  107. package/src/cli/cmd/tui/context/theme/solarized.json +223 -0
  108. package/src/cli/cmd/tui/context/theme/synthwave84.json +226 -0
  109. package/src/cli/cmd/tui/context/theme/tokyonight.json +243 -0
  110. package/src/cli/cmd/tui/context/theme/vercel.json +245 -0
  111. package/src/cli/cmd/tui/context/theme/vesper.json +218 -0
  112. package/src/cli/cmd/tui/context/theme/zenburn.json +223 -0
  113. package/src/cli/cmd/tui/context/theme.tsx +1109 -0
  114. package/src/cli/cmd/tui/event.ts +40 -0
  115. package/src/cli/cmd/tui/routes/home.tsx +105 -0
  116. package/src/cli/cmd/tui/routes/session/dialog-fork-from-timeline.tsx +64 -0
  117. package/src/cli/cmd/tui/routes/session/dialog-message.tsx +109 -0
  118. package/src/cli/cmd/tui/routes/session/dialog-subagent.tsx +26 -0
  119. package/src/cli/cmd/tui/routes/session/dialog-timeline.tsx +47 -0
  120. package/src/cli/cmd/tui/routes/session/footer.tsx +88 -0
  121. package/src/cli/cmd/tui/routes/session/header.tsx +141 -0
  122. package/src/cli/cmd/tui/routes/session/index.tsx +1888 -0
  123. package/src/cli/cmd/tui/routes/session/sidebar.tsx +321 -0
  124. package/src/cli/cmd/tui/spawn.ts +60 -0
  125. package/src/cli/cmd/tui/thread.ts +120 -0
  126. package/src/cli/cmd/tui/ui/dialog-alert.tsx +57 -0
  127. package/src/cli/cmd/tui/ui/dialog-confirm.tsx +83 -0
  128. package/src/cli/cmd/tui/ui/dialog-help.tsx +38 -0
  129. package/src/cli/cmd/tui/ui/dialog-prompt.tsx +77 -0
  130. package/src/cli/cmd/tui/ui/dialog-select.tsx +330 -0
  131. package/src/cli/cmd/tui/ui/dialog.tsx +170 -0
  132. package/src/cli/cmd/tui/ui/spinner.ts +368 -0
  133. package/src/cli/cmd/tui/ui/toast.tsx +100 -0
  134. package/src/cli/cmd/tui/util/clipboard.ts +127 -0
  135. package/src/cli/cmd/tui/util/editor.ts +32 -0
  136. package/src/cli/cmd/tui/util/terminal.ts +114 -0
  137. package/src/cli/cmd/tui/worker.ts +63 -0
  138. package/src/cli/cmd/uninstall.ts +344 -0
  139. package/src/cli/cmd/upgrade.ts +67 -0
  140. package/src/cli/cmd/web.ts +84 -0
  141. package/src/cli/error.ts +55 -0
  142. package/src/cli/ui.ts +84 -0
  143. package/src/cli/upgrade.ts +25 -0
  144. package/src/command/index.ts +80 -0
  145. package/src/command/template/initialize.txt +10 -0
  146. package/src/command/template/review.txt +97 -0
  147. package/src/config/config.ts +995 -0
  148. package/src/config/markdown.ts +41 -0
  149. package/src/env/index.ts +26 -0
  150. package/src/file/ignore.ts +83 -0
  151. package/src/file/index.ts +328 -0
  152. package/src/file/ripgrep.ts +393 -0
  153. package/src/file/time.ts +64 -0
  154. package/src/file/watcher.ts +103 -0
  155. package/src/flag/flag.ts +46 -0
  156. package/src/format/formatter.ts +315 -0
  157. package/src/format/index.ts +137 -0
  158. package/src/global/index.ts +52 -0
  159. package/src/id/id.ts +73 -0
  160. package/src/ide/index.ts +76 -0
  161. package/src/index.ts +217 -0
  162. package/src/installation/index.ts +196 -0
  163. package/src/lsp/client.ts +229 -0
  164. package/src/lsp/index.ts +485 -0
  165. package/src/lsp/language.ts +116 -0
  166. package/src/lsp/server.ts +1895 -0
  167. package/src/mcp/auth.ts +135 -0
  168. package/src/mcp/index.ts +654 -0
  169. package/src/mcp/oauth-callback.ts +200 -0
  170. package/src/mcp/oauth-provider.ts +154 -0
  171. package/src/patch/index.ts +622 -0
  172. package/src/permission/index.ts +199 -0
  173. package/src/plugin/index.ts +101 -0
  174. package/src/project/bootstrap.ts +31 -0
  175. package/src/project/instance.ts +78 -0
  176. package/src/project/project.ts +221 -0
  177. package/src/project/state.ts +65 -0
  178. package/src/project/vcs.ts +76 -0
  179. package/src/provider/auth.ts +143 -0
  180. package/src/provider/models-macro.ts +11 -0
  181. package/src/provider/models.ts +106 -0
  182. package/src/provider/provider.ts +1071 -0
  183. package/src/provider/sdk/openai-compatible/src/README.md +5 -0
  184. package/src/provider/sdk/openai-compatible/src/index.ts +2 -0
  185. package/src/provider/sdk/openai-compatible/src/openai-compatible-provider.ts +101 -0
  186. package/src/provider/sdk/openai-compatible/src/responses/convert-to-openai-responses-input.ts +303 -0
  187. package/src/provider/sdk/openai-compatible/src/responses/map-openai-responses-finish-reason.ts +22 -0
  188. package/src/provider/sdk/openai-compatible/src/responses/openai-config.ts +18 -0
  189. package/src/provider/sdk/openai-compatible/src/responses/openai-error.ts +22 -0
  190. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-api-types.ts +207 -0
  191. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-language-model.ts +1713 -0
  192. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-prepare-tools.ts +177 -0
  193. package/src/provider/sdk/openai-compatible/src/responses/openai-responses-settings.ts +1 -0
  194. package/src/provider/sdk/openai-compatible/src/responses/tool/code-interpreter.ts +88 -0
  195. package/src/provider/sdk/openai-compatible/src/responses/tool/file-search.ts +128 -0
  196. package/src/provider/sdk/openai-compatible/src/responses/tool/image-generation.ts +115 -0
  197. package/src/provider/sdk/openai-compatible/src/responses/tool/local-shell.ts +65 -0
  198. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search-preview.ts +104 -0
  199. package/src/provider/sdk/openai-compatible/src/responses/tool/web-search.ts +103 -0
  200. package/src/provider/transform.ts +455 -0
  201. package/src/pty/index.ts +231 -0
  202. package/src/server/error.ts +36 -0
  203. package/src/server/project.ts +79 -0
  204. package/src/server/server.ts +2642 -0
  205. package/src/server/tui.ts +71 -0
  206. package/src/session/compaction.ts +223 -0
  207. package/src/session/index.ts +458 -0
  208. package/src/session/llm.ts +201 -0
  209. package/src/session/message-v2.ts +659 -0
  210. package/src/session/message.ts +189 -0
  211. package/src/session/processor.ts +409 -0
  212. package/src/session/prompt/anthropic-20250930.txt +166 -0
  213. package/src/session/prompt/anthropic.txt +104 -0
  214. package/src/session/prompt/anthropic_spoof.txt +1 -0
  215. package/src/session/prompt/beast.txt +147 -0
  216. package/src/session/prompt/build-switch.txt +5 -0
  217. package/src/session/prompt/codex.txt +318 -0
  218. package/src/session/prompt/copilot-gpt-5.txt +143 -0
  219. package/src/session/prompt/gemini.txt +155 -0
  220. package/src/session/prompt/max-steps.txt +16 -0
  221. package/src/session/prompt/plan-reminder-anthropic.txt +67 -0
  222. package/src/session/prompt/plan.txt +26 -0
  223. package/src/session/prompt/polaris.txt +106 -0
  224. package/src/session/prompt/qwen.txt +109 -0
  225. package/src/session/prompt.ts +1446 -0
  226. package/src/session/retry.ts +86 -0
  227. package/src/session/revert.ts +108 -0
  228. package/src/session/status.ts +76 -0
  229. package/src/session/summary.ts +194 -0
  230. package/src/session/system.ts +120 -0
  231. package/src/session/todo.ts +37 -0
  232. package/src/share/share-next.ts +194 -0
  233. package/src/share/share.ts +87 -0
  234. package/src/shell/shell.ts +67 -0
  235. package/src/skill/index.ts +1 -0
  236. package/src/skill/skill.ts +83 -0
  237. package/src/snapshot/index.ts +197 -0
  238. package/src/storage/storage.ts +226 -0
  239. package/src/tool/bash.ts +306 -0
  240. package/src/tool/bash.txt +158 -0
  241. package/src/tool/batch.ts +175 -0
  242. package/src/tool/batch.txt +24 -0
  243. package/src/tool/codesearch.ts +138 -0
  244. package/src/tool/codesearch.txt +12 -0
  245. package/src/tool/edit.ts +675 -0
  246. package/src/tool/edit.txt +10 -0
  247. package/src/tool/glob.ts +65 -0
  248. package/src/tool/glob.txt +6 -0
  249. package/src/tool/grep.ts +121 -0
  250. package/src/tool/grep.txt +8 -0
  251. package/src/tool/invalid.ts +17 -0
  252. package/src/tool/ls.ts +110 -0
  253. package/src/tool/ls.txt +1 -0
  254. package/src/tool/lsp-diagnostics.ts +26 -0
  255. package/src/tool/lsp-diagnostics.txt +1 -0
  256. package/src/tool/lsp-hover.ts +31 -0
  257. package/src/tool/lsp-hover.txt +1 -0
  258. package/src/tool/lsp.ts +87 -0
  259. package/src/tool/lsp.txt +19 -0
  260. package/src/tool/multiedit.ts +46 -0
  261. package/src/tool/multiedit.txt +41 -0
  262. package/src/tool/patch.ts +233 -0
  263. package/src/tool/patch.txt +1 -0
  264. package/src/tool/read.ts +219 -0
  265. package/src/tool/read.txt +12 -0
  266. package/src/tool/registry.ts +162 -0
  267. package/src/tool/skill.ts +100 -0
  268. package/src/tool/task.ts +136 -0
  269. package/src/tool/task.txt +60 -0
  270. package/src/tool/todo.ts +39 -0
  271. package/src/tool/todoread.txt +14 -0
  272. package/src/tool/todowrite.txt +167 -0
  273. package/src/tool/tool.ts +71 -0
  274. package/src/tool/webfetch.ts +187 -0
  275. package/src/tool/webfetch.txt +13 -0
  276. package/src/tool/websearch.ts +150 -0
  277. package/src/tool/websearch.txt +11 -0
  278. package/src/tool/write.ts +110 -0
  279. package/src/tool/write.txt +8 -0
  280. package/src/util/archive.ts +16 -0
  281. package/src/util/color.ts +19 -0
  282. package/src/util/context.ts +25 -0
  283. package/src/util/defer.ts +12 -0
  284. package/src/util/eventloop.ts +20 -0
  285. package/src/util/filesystem.ts +83 -0
  286. package/src/util/fn.ts +11 -0
  287. package/src/util/iife.ts +3 -0
  288. package/src/util/keybind.ts +102 -0
  289. package/src/util/lazy.ts +11 -0
  290. package/src/util/locale.ts +81 -0
  291. package/src/util/lock.ts +98 -0
  292. package/src/util/log.ts +180 -0
  293. package/src/util/queue.ts +32 -0
  294. package/src/util/rpc.ts +42 -0
  295. package/src/util/scrap.ts +10 -0
  296. package/src/util/signal.ts +12 -0
  297. package/src/util/timeout.ts +14 -0
  298. package/src/util/token.ts +7 -0
  299. package/src/util/wildcard.ts +54 -0
  300. package/tsconfig.json +16 -0
@@ -0,0 +1,14 @@
1
+ import { createSimpleContext } from "./helper"
2
+
3
+ export interface Args {
4
+ model?: string
5
+ agent?: string
6
+ prompt?: string
7
+ continue?: boolean
8
+ sessionID?: string
9
+ }
10
+
11
+ export const { use: useArgs, provider: ArgsProvider } = createSimpleContext({
12
+ name: "Args",
13
+ init: (props: Args) => props,
14
+ })
@@ -0,0 +1,13 @@
1
+ import { createMemo } from "solid-js"
2
+ import { useSync } from "./sync"
3
+ import { Global } from "@/global"
4
+
5
+ export function useDirectory() {
6
+ const sync = useSync()
7
+ return createMemo(() => {
8
+ const directory = sync.data.path.directory || process.cwd()
9
+ const result = directory.replace(Global.Path.home, "~")
10
+ if (sync.data.vcs?.branch) return result + ":" + sync.data.vcs.branch
11
+ return result
12
+ })
13
+ }
@@ -0,0 +1,23 @@
1
+ import { useRenderer } from "@opentui/solid"
2
+ import { createSimpleContext } from "./helper"
3
+ import { FormatError, FormatUnknownError } from "@/cli/error"
4
+
5
+ export const { use: useExit, provider: ExitProvider } = createSimpleContext({
6
+ name: "Exit",
7
+ init: (input: { onExit?: () => Promise<void> }) => {
8
+ const renderer = useRenderer()
9
+ return async (reason?: any) => {
10
+ // Reset window title before destroying renderer
11
+ renderer.setTerminalTitle("")
12
+ renderer.destroy()
13
+ await input.onExit?.()
14
+ if (reason) {
15
+ const formatted = FormatError(reason) ?? FormatUnknownError(reason)
16
+ if (formatted) {
17
+ process.stderr.write(formatted + "\n")
18
+ }
19
+ }
20
+ process.exit(0)
21
+ }
22
+ },
23
+ })
@@ -0,0 +1,25 @@
1
+ import { createContext, Show, useContext, type ParentProps } from "solid-js"
2
+
3
+ export function createSimpleContext<T, Props extends Record<string, any>>(input: {
4
+ name: string
5
+ init: ((input: Props) => T) | (() => T)
6
+ }) {
7
+ const ctx = createContext<T>()
8
+
9
+ return {
10
+ provider: (props: ParentProps<Props>) => {
11
+ const init = input.init(props)
12
+ return (
13
+ // @ts-expect-error
14
+ <Show when={init.ready === undefined || init.ready === true}>
15
+ <ctx.Provider value={init}>{props.children}</ctx.Provider>
16
+ </Show>
17
+ )
18
+ },
19
+ use() {
20
+ const value = useContext(ctx)
21
+ if (!value) throw new Error(`${input.name} context must be used within a context provider`)
22
+ return value
23
+ },
24
+ }
25
+ }
@@ -0,0 +1,101 @@
1
+ import { createMemo } from "solid-js"
2
+ import { useSync } from "@tui/context/sync"
3
+ import { Keybind } from "@/util/keybind"
4
+ import { pipe, mapValues } from "remeda"
5
+ import type { KeybindsConfig } from "@bincode-ai/sdk/v2"
6
+ import type { ParsedKey, Renderable } from "@opentui/core"
7
+ import { createStore } from "solid-js/store"
8
+ import { useKeyboard, useRenderer } from "@opentui/solid"
9
+ import { createSimpleContext } from "./helper"
10
+
11
+ export const { use: useKeybind, provider: KeybindProvider } = createSimpleContext({
12
+ name: "Keybind",
13
+ init: () => {
14
+ const sync = useSync()
15
+ const keybinds = createMemo(() => {
16
+ return pipe(
17
+ sync.data.config.keybinds ?? {},
18
+ mapValues((value) => Keybind.parse(value)),
19
+ )
20
+ })
21
+ const [store, setStore] = createStore({
22
+ leader: false,
23
+ })
24
+ const renderer = useRenderer()
25
+
26
+ let focus: Renderable | null
27
+ let timeout: NodeJS.Timeout
28
+ function leader(active: boolean) {
29
+ if (active) {
30
+ setStore("leader", true)
31
+ focus = renderer.currentFocusedRenderable
32
+ focus?.blur()
33
+ if (timeout) clearTimeout(timeout)
34
+ timeout = setTimeout(() => {
35
+ if (!store.leader) return
36
+ leader(false)
37
+ if (focus) {
38
+ focus.focus()
39
+ }
40
+ }, 2000)
41
+ return
42
+ }
43
+
44
+ if (!active) {
45
+ if (focus && !renderer.currentFocusedRenderable) {
46
+ focus.focus()
47
+ }
48
+ setStore("leader", false)
49
+ }
50
+ }
51
+
52
+ useKeyboard(async (evt) => {
53
+ if (!store.leader && result.match("leader", evt)) {
54
+ leader(true)
55
+ return
56
+ }
57
+
58
+ if (store.leader && evt.name) {
59
+ setImmediate(() => {
60
+ if (focus && renderer.currentFocusedRenderable === focus) {
61
+ focus.focus()
62
+ }
63
+ leader(false)
64
+ })
65
+ }
66
+ })
67
+
68
+ const result = {
69
+ get all() {
70
+ return keybinds()
71
+ },
72
+ get leader() {
73
+ return store.leader
74
+ },
75
+ parse(evt: ParsedKey): Keybind.Info {
76
+ // Handle special case for Ctrl+Underscore (represented as \x1F)
77
+ if (evt.name === "\x1F") {
78
+ return Keybind.fromParsedKey({ ...evt, name: "_", ctrl: true }, store.leader)
79
+ }
80
+ return Keybind.fromParsedKey(evt, store.leader)
81
+ },
82
+ match(key: keyof KeybindsConfig, evt: ParsedKey) {
83
+ const keybind = keybinds()[key]
84
+ if (!keybind) return false
85
+ const parsed: Keybind.Info = result.parse(evt)
86
+ for (const key of keybind) {
87
+ if (Keybind.match(key, parsed)) {
88
+ return true
89
+ }
90
+ }
91
+ },
92
+ print(key: keyof KeybindsConfig) {
93
+ const first = keybinds()[key]?.at(0)
94
+ if (!first) return ""
95
+ const result = Keybind.toString(first)
96
+ return result.replace("<leader>", Keybind.toString(keybinds().leader![0]!))
97
+ },
98
+ }
99
+ return result
100
+ },
101
+ })
@@ -0,0 +1,49 @@
1
+ import { Global } from "@/global"
2
+ import { createSignal, type Setter } from "solid-js"
3
+ import { createStore } from "solid-js/store"
4
+ import { createSimpleContext } from "./helper"
5
+ import path from "path"
6
+
7
+ export const { use: useKV, provider: KVProvider } = createSimpleContext({
8
+ name: "KV",
9
+ init: () => {
10
+ const [ready, setReady] = createSignal(false)
11
+ const [kvStore, setKvStore] = createStore<Record<string, any>>()
12
+ const file = Bun.file(path.join(Global.Path.state, "kv.json"))
13
+
14
+ file
15
+ .json()
16
+ .then((x) => {
17
+ setKvStore(x)
18
+ })
19
+ .catch(() => {})
20
+ .finally(() => {
21
+ setReady(true)
22
+ })
23
+
24
+ const result = {
25
+ get ready() {
26
+ return ready()
27
+ },
28
+ signal<T>(name: string, defaultValue: T) {
29
+ if (!kvStore[name]) setKvStore(name, defaultValue)
30
+ return [
31
+ function () {
32
+ return result.get(name)
33
+ },
34
+ function setter(next: Setter<T>) {
35
+ result.set(name, next)
36
+ },
37
+ ] as const
38
+ },
39
+ get(key: string, defaultValue?: any) {
40
+ return kvStore[key] ?? defaultValue
41
+ },
42
+ set(key: string, value: any) {
43
+ setKvStore(key, value)
44
+ Bun.write(file, JSON.stringify(kvStore, null, 2))
45
+ },
46
+ }
47
+ return result
48
+ },
49
+ })
@@ -0,0 +1,339 @@
1
+ import { createStore } from "solid-js/store"
2
+ import { batch, createEffect, createMemo } from "solid-js"
3
+ import { useSync } from "@tui/context/sync"
4
+ import { useTheme } from "@tui/context/theme"
5
+ import { uniqueBy } from "remeda"
6
+ import path from "path"
7
+ import { Global } from "@/global"
8
+ import { iife } from "@/util/iife"
9
+ import { createSimpleContext } from "./helper"
10
+ import { useToast } from "../ui/toast"
11
+ import { Provider } from "@/provider/provider"
12
+ import { useArgs } from "./args"
13
+ import { useSDK } from "./sdk"
14
+ import { RGBA } from "@opentui/core"
15
+
16
+ export const { use: useLocal, provider: LocalProvider } = createSimpleContext({
17
+ name: "Local",
18
+ init: () => {
19
+ const sync = useSync()
20
+ const sdk = useSDK()
21
+ const toast = useToast()
22
+
23
+ function isModelValid(model: { providerID: string; modelID: string }) {
24
+ const provider = sync.data.provider.find((x) => x.id === model.providerID)
25
+ return !!provider?.models[model.modelID]
26
+ }
27
+
28
+ function getFirstValidModel(...modelFns: (() => { providerID: string; modelID: string } | undefined)[]) {
29
+ for (const modelFn of modelFns) {
30
+ const model = modelFn()
31
+ if (!model) continue
32
+ if (isModelValid(model)) return model
33
+ }
34
+ }
35
+
36
+ // Automatically update model when agent changes
37
+ createEffect(() => {
38
+ const value = agent.current()
39
+ if (value.model) {
40
+ if (isModelValid(value.model))
41
+ model.set({
42
+ providerID: value.model.providerID,
43
+ modelID: value.model.modelID,
44
+ })
45
+ else
46
+ toast.show({
47
+ variant: "warning",
48
+ message: `Agent ${value.name}'s configured model ${value.model.providerID}/${value.model.modelID} is not valid`,
49
+ duration: 3000,
50
+ })
51
+ }
52
+ })
53
+
54
+ const agent = iife(() => {
55
+ const agents = createMemo(() => sync.data.agent.filter((x) => x.mode !== "subagent" && !x.hidden))
56
+ const [agentStore, setAgentStore] = createStore<{
57
+ current: string
58
+ }>({
59
+ current: agents().find((x) => x.default)?.name ?? agents()[0].name,
60
+ })
61
+ const { theme } = useTheme()
62
+ const colors = createMemo(() => [
63
+ theme.secondary,
64
+ theme.accent,
65
+ theme.success,
66
+ theme.warning,
67
+ theme.primary,
68
+ theme.error,
69
+ ])
70
+ return {
71
+ list() {
72
+ return agents()
73
+ },
74
+ current() {
75
+ return agents().find((x) => x.name === agentStore.current)!
76
+ },
77
+ set(name: string) {
78
+ if (!agents().some((x) => x.name === name))
79
+ return toast.show({
80
+ variant: "warning",
81
+ message: `Agent not found: ${name}`,
82
+ duration: 3000,
83
+ })
84
+ setAgentStore("current", name)
85
+ },
86
+ move(direction: 1 | -1) {
87
+ batch(() => {
88
+ let next = agents().findIndex((x) => x.name === agentStore.current) + direction
89
+ if (next < 0) next = agents().length - 1
90
+ if (next >= agents().length) next = 0
91
+ const value = agents()[next]
92
+ setAgentStore("current", value.name)
93
+ })
94
+ },
95
+ color(name: string) {
96
+ const agent = agents().find((x) => x.name === name)
97
+ if (agent?.color) return RGBA.fromHex(agent.color)
98
+ const index = agents().findIndex((x) => x.name === name)
99
+ if (index === -1) return colors()[0]
100
+ return colors()[index % colors().length]
101
+ },
102
+ }
103
+ })
104
+
105
+ const model = iife(() => {
106
+ const [modelStore, setModelStore] = createStore<{
107
+ ready: boolean
108
+ model: Record<
109
+ string,
110
+ {
111
+ providerID: string
112
+ modelID: string
113
+ }
114
+ >
115
+ recent: {
116
+ providerID: string
117
+ modelID: string
118
+ }[]
119
+ favorite: {
120
+ providerID: string
121
+ modelID: string
122
+ }[]
123
+ }>({
124
+ ready: false,
125
+ model: {},
126
+ recent: [],
127
+ favorite: [],
128
+ })
129
+
130
+ const file = Bun.file(path.join(Global.Path.state, "model.json"))
131
+
132
+ function save() {
133
+ Bun.write(
134
+ file,
135
+ JSON.stringify({
136
+ recent: modelStore.recent,
137
+ favorite: modelStore.favorite,
138
+ }),
139
+ )
140
+ }
141
+
142
+ file
143
+ .json()
144
+ .then((x) => {
145
+ if (Array.isArray(x.recent)) setModelStore("recent", x.recent)
146
+ if (Array.isArray(x.favorite)) setModelStore("favorite", x.favorite)
147
+ })
148
+ .catch(() => {})
149
+ .finally(() => {
150
+ setModelStore("ready", true)
151
+ })
152
+
153
+ const args = useArgs()
154
+ const fallbackModel = createMemo(() => {
155
+ if (args.model) {
156
+ const { providerID, modelID } = Provider.parseModel(args.model)
157
+ if (isModelValid({ providerID, modelID })) {
158
+ return {
159
+ providerID,
160
+ modelID,
161
+ }
162
+ }
163
+ }
164
+
165
+ if (sync.data.config.model) {
166
+ const { providerID, modelID } = Provider.parseModel(sync.data.config.model)
167
+ if (isModelValid({ providerID, modelID })) {
168
+ return {
169
+ providerID,
170
+ modelID,
171
+ }
172
+ }
173
+ }
174
+
175
+ for (const item of modelStore.recent) {
176
+ if (isModelValid(item)) {
177
+ return item
178
+ }
179
+ }
180
+
181
+ const provider = sync.data.provider[0]
182
+ if (!provider) return undefined
183
+ const defaultModel = sync.data.provider_default[provider.id]
184
+ const firstModel = Object.values(provider.models)[0]
185
+ const model = defaultModel ?? firstModel?.id
186
+ if (!model) return undefined
187
+ return {
188
+ providerID: provider.id,
189
+ modelID: model,
190
+ }
191
+ })
192
+
193
+ const currentModel = createMemo(() => {
194
+ const a = agent.current()
195
+ return (
196
+ getFirstValidModel(
197
+ () => modelStore.model[a.name],
198
+ () => a.model,
199
+ fallbackModel,
200
+ ) ?? undefined
201
+ )
202
+ })
203
+
204
+ return {
205
+ current: currentModel,
206
+ get ready() {
207
+ return modelStore.ready
208
+ },
209
+ recent() {
210
+ return modelStore.recent
211
+ },
212
+ favorite() {
213
+ return modelStore.favorite
214
+ },
215
+ parsed: createMemo(() => {
216
+ const value = currentModel()
217
+ if (!value) {
218
+ return {
219
+ provider: "Connect a provider",
220
+ model: "No provider selected",
221
+ }
222
+ }
223
+ const provider = sync.data.provider.find((x) => x.id === value.providerID)
224
+ const info = provider?.models[value.modelID]
225
+ return {
226
+ provider: provider?.name ?? value.providerID,
227
+ model: info?.name ?? value.modelID,
228
+ }
229
+ }),
230
+ cycle(direction: 1 | -1) {
231
+ const current = currentModel()
232
+ if (!current) return
233
+ const recent = modelStore.recent
234
+ const index = recent.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
235
+ if (index === -1) return
236
+ let next = index + direction
237
+ if (next < 0) next = recent.length - 1
238
+ if (next >= recent.length) next = 0
239
+ const val = recent[next]
240
+ if (!val) return
241
+ setModelStore("model", agent.current().name, { ...val })
242
+ },
243
+ cycleFavorite(direction: 1 | -1) {
244
+ const favorites = modelStore.favorite.filter((item) => isModelValid(item))
245
+ if (!favorites.length) {
246
+ toast.show({
247
+ variant: "info",
248
+ message: "Add a favorite model to use this shortcut",
249
+ duration: 3000,
250
+ })
251
+ return
252
+ }
253
+ const current = currentModel()
254
+ let index = -1
255
+ if (current) {
256
+ index = favorites.findIndex((x) => x.providerID === current.providerID && x.modelID === current.modelID)
257
+ }
258
+ if (index === -1) {
259
+ index = direction === 1 ? 0 : favorites.length - 1
260
+ } else {
261
+ index += direction
262
+ if (index < 0) index = favorites.length - 1
263
+ if (index >= favorites.length) index = 0
264
+ }
265
+ const next = favorites[index]
266
+ if (!next) return
267
+ setModelStore("model", agent.current().name, { ...next })
268
+ const uniq = uniqueBy([next, ...modelStore.recent], (x) => x.providerID + x.modelID)
269
+ if (uniq.length > 10) uniq.pop()
270
+ setModelStore("recent", uniq)
271
+ save()
272
+ },
273
+ set(model: { providerID: string; modelID: string }, options?: { recent?: boolean }) {
274
+ batch(() => {
275
+ if (!isModelValid(model)) {
276
+ toast.show({
277
+ message: `Model ${model.providerID}/${model.modelID} is not valid`,
278
+ variant: "warning",
279
+ duration: 3000,
280
+ })
281
+ return
282
+ }
283
+ setModelStore("model", agent.current().name, model)
284
+ if (options?.recent) {
285
+ const uniq = uniqueBy([model, ...modelStore.recent], (x) => x.providerID + x.modelID)
286
+ if (uniq.length > 10) uniq.pop()
287
+ setModelStore("recent", uniq)
288
+ save()
289
+ }
290
+ })
291
+ },
292
+ toggleFavorite(model: { providerID: string; modelID: string }) {
293
+ batch(() => {
294
+ if (!isModelValid(model)) {
295
+ toast.show({
296
+ message: `Model ${model.providerID}/${model.modelID} is not valid`,
297
+ variant: "warning",
298
+ duration: 3000,
299
+ })
300
+ return
301
+ }
302
+ const exists = modelStore.favorite.some(
303
+ (x) => x.providerID === model.providerID && x.modelID === model.modelID,
304
+ )
305
+ const next = exists
306
+ ? modelStore.favorite.filter((x) => x.providerID !== model.providerID || x.modelID !== model.modelID)
307
+ : [model, ...modelStore.favorite]
308
+ setModelStore("favorite", next)
309
+ save()
310
+ })
311
+ },
312
+ }
313
+ })
314
+
315
+ const mcp = {
316
+ isEnabled(name: string) {
317
+ const status = sync.data.mcp[name]
318
+ return status?.status === "connected"
319
+ },
320
+ async toggle(name: string) {
321
+ const status = sync.data.mcp[name]
322
+ if (status?.status === "connected") {
323
+ // Disable: disconnect the MCP
324
+ await sdk.client.mcp.disconnect({ name })
325
+ } else {
326
+ // Enable/Retry: connect the MCP (handles disabled, failed, and other states)
327
+ await sdk.client.mcp.connect({ name })
328
+ }
329
+ },
330
+ }
331
+
332
+ const result = {
333
+ model,
334
+ agent,
335
+ mcp,
336
+ }
337
+ return result
338
+ },
339
+ })
@@ -0,0 +1,18 @@
1
+ import { createSimpleContext } from "./helper"
2
+ import type { PromptRef } from "../component/prompt"
3
+
4
+ export const { use: usePromptRef, provider: PromptRefProvider } = createSimpleContext({
5
+ name: "PromptRef",
6
+ init: () => {
7
+ let current: PromptRef | undefined
8
+
9
+ return {
10
+ get current() {
11
+ return current
12
+ },
13
+ set(ref: PromptRef | undefined) {
14
+ current = ref
15
+ },
16
+ }
17
+ },
18
+ })
@@ -0,0 +1,46 @@
1
+ import { createStore } from "solid-js/store"
2
+ import { createSimpleContext } from "./helper"
3
+ import type { PromptInfo } from "../component/prompt/history"
4
+
5
+ export type HomeRoute = {
6
+ type: "home"
7
+ initialPrompt?: PromptInfo
8
+ }
9
+
10
+ export type SessionRoute = {
11
+ type: "session"
12
+ sessionID: string
13
+ initialPrompt?: PromptInfo
14
+ }
15
+
16
+ export type Route = HomeRoute | SessionRoute
17
+
18
+ export const { use: useRoute, provider: RouteProvider } = createSimpleContext({
19
+ name: "Route",
20
+ init: () => {
21
+ const [store, setStore] = createStore<Route>(
22
+ process.env["BINCODE_ROUTE"]
23
+ ? JSON.parse(process.env["BINCODE_ROUTE"])
24
+ : {
25
+ type: "home",
26
+ },
27
+ )
28
+
29
+ return {
30
+ get data() {
31
+ return store
32
+ },
33
+ navigate(route: Route) {
34
+ console.log("navigate", route)
35
+ setStore(route)
36
+ },
37
+ }
38
+ },
39
+ })
40
+
41
+ export type RouteContext = ReturnType<typeof useRoute>
42
+
43
+ export function useRouteData<T extends Route["type"]>(type: T) {
44
+ const route = useRoute()
45
+ return route.data as Extract<Route, { type: typeof type }>
46
+ }