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,43 @@
1
+ import z from "zod"
2
+ import type { ZodType } from "zod"
3
+ import { Log } from "../util/log"
4
+
5
+ export namespace BusEvent {
6
+ const log = Log.create({ service: "event" })
7
+
8
+ export type Definition = ReturnType<typeof define>
9
+
10
+ const registry = new Map<string, Definition>()
11
+
12
+ export function define<Type extends string, Properties extends ZodType>(type: Type, properties: Properties) {
13
+ const result = {
14
+ type,
15
+ properties,
16
+ }
17
+ registry.set(type, result)
18
+ return result
19
+ }
20
+
21
+ export function payloads() {
22
+ return z
23
+ .discriminatedUnion(
24
+ "type",
25
+ registry
26
+ .entries()
27
+ .map(([type, def]) => {
28
+ return z
29
+ .object({
30
+ type: z.literal(type),
31
+ properties: def.properties,
32
+ })
33
+ .meta({
34
+ ref: "Event" + "." + def.type,
35
+ })
36
+ })
37
+ .toArray() as any,
38
+ )
39
+ .meta({
40
+ ref: "Event",
41
+ })
42
+ }
43
+ }
@@ -0,0 +1,10 @@
1
+ import { EventEmitter } from "events"
2
+
3
+ export const GlobalBus = new EventEmitter<{
4
+ event: [
5
+ {
6
+ directory?: string
7
+ payload: any
8
+ },
9
+ ]
10
+ }>()
@@ -0,0 +1,105 @@
1
+ import z from "zod"
2
+ import { Log } from "../util/log"
3
+ import { Instance } from "../project/instance"
4
+ import { BusEvent } from "./bus-event"
5
+ import { GlobalBus } from "./global"
6
+
7
+ export namespace Bus {
8
+ const log = Log.create({ service: "bus" })
9
+ type Subscription = (event: any) => void
10
+
11
+ export const InstanceDisposed = BusEvent.define(
12
+ "server.instance.disposed",
13
+ z.object({
14
+ directory: z.string(),
15
+ }),
16
+ )
17
+
18
+ const state = Instance.state(
19
+ () => {
20
+ const subscriptions = new Map<any, Subscription[]>()
21
+
22
+ return {
23
+ subscriptions,
24
+ }
25
+ },
26
+ async (entry) => {
27
+ const wildcard = entry.subscriptions.get("*")
28
+ if (!wildcard) return
29
+ const event = {
30
+ type: InstanceDisposed.type,
31
+ properties: {
32
+ directory: Instance.directory,
33
+ },
34
+ }
35
+ for (const sub of [...wildcard]) {
36
+ sub(event)
37
+ }
38
+ },
39
+ )
40
+
41
+ export async function publish<Definition extends BusEvent.Definition>(
42
+ def: Definition,
43
+ properties: z.output<Definition["properties"]>,
44
+ ) {
45
+ const payload = {
46
+ type: def.type,
47
+ properties,
48
+ }
49
+ log.info("publishing", {
50
+ type: def.type,
51
+ })
52
+ const pending = []
53
+ for (const key of [def.type, "*"]) {
54
+ const match = state().subscriptions.get(key)
55
+ for (const sub of match ?? []) {
56
+ pending.push(sub(payload))
57
+ }
58
+ }
59
+ GlobalBus.emit("event", {
60
+ directory: Instance.directory,
61
+ payload,
62
+ })
63
+ return Promise.all(pending)
64
+ }
65
+
66
+ export function subscribe<Definition extends BusEvent.Definition>(
67
+ def: Definition,
68
+ callback: (event: { type: Definition["type"]; properties: z.infer<Definition["properties"]> }) => void,
69
+ ) {
70
+ return raw(def.type, callback)
71
+ }
72
+
73
+ export function once<Definition extends BusEvent.Definition>(
74
+ def: Definition,
75
+ callback: (event: {
76
+ type: Definition["type"]
77
+ properties: z.infer<Definition["properties"]>
78
+ }) => "done" | undefined,
79
+ ) {
80
+ const unsub = subscribe(def, (event) => {
81
+ if (callback(event)) unsub()
82
+ })
83
+ }
84
+
85
+ export function subscribeAll(callback: (event: any) => void) {
86
+ return raw("*", callback)
87
+ }
88
+
89
+ function raw(type: string, callback: (event: any) => void) {
90
+ log.info("subscribing", { type })
91
+ const subscriptions = state().subscriptions
92
+ let match = subscriptions.get(type) ?? []
93
+ match.push(callback)
94
+ subscriptions.set(type, match)
95
+
96
+ return () => {
97
+ log.info("unsubscribing", { type })
98
+ const match = subscriptions.get(type)
99
+ if (!match) return
100
+ const index = match.indexOf(callback)
101
+ if (index === -1) return
102
+ match.splice(index, 1)
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,61 @@
1
+ import { Filesystem } from "../util/filesystem"
2
+
3
+ /**
4
+ * Check if user is authenticated by checking for Bineric API key in config files
5
+ * This function works without requiring Instance initialization
6
+ */
7
+ export async function checkAuth(command: string | undefined): Promise<{ isAuthenticated: boolean; needsLogin: boolean }> {
8
+ // Skip auth check for auth commands
9
+ if (command === "auth" || command === "login" || command === "logout") {
10
+ return { isAuthenticated: true, needsLogin: false }
11
+ }
12
+
13
+ // Skip for help and version commands
14
+ if (command === "help" || command === "version" || command === "--help" || command === "--version" || command === "-h" || command === "-v") {
15
+ return { isAuthenticated: true, needsLogin: false }
16
+ }
17
+
18
+ // Check if Bineric provider has API key in bincode.json files
19
+ try {
20
+ const configFiles = ["bincode.jsonc", "bincode.json"]
21
+ const currentDir = process.cwd()
22
+
23
+ for (const file of configFiles) {
24
+ const found = await Filesystem.findUp(file, currentDir, undefined)
25
+ for (const configPath of found) {
26
+ const config = await Bun.file(configPath).json().catch(() => null)
27
+ // Check for token (preferred) or apiKey (legacy)
28
+ const token = config?.provider?.Bineric?.options?.token
29
+ const apiKey = config?.provider?.Bineric?.options?.apiKey
30
+
31
+ if (token && token.length > 10) {
32
+ return { isAuthenticated: true, needsLogin: false }
33
+ }
34
+
35
+ // Legacy: check apiKey if token not found
36
+ if (apiKey && apiKey !== "not-needed" && apiKey.length > 10) {
37
+ return { isAuthenticated: true, needsLogin: false }
38
+ }
39
+ }
40
+ }
41
+
42
+ // Also check if Bineric is enabled in config - if it is, we need login
43
+ for (const file of configFiles) {
44
+ const found = await Filesystem.findUp(file, currentDir, undefined)
45
+ for (const configPath of found) {
46
+ const config = await Bun.file(configPath).json().catch(() => null)
47
+ if (config?.enabled_providers?.includes("Bineric") || config?.provider?.Bineric) {
48
+ return { isAuthenticated: false, needsLogin: true }
49
+ }
50
+ }
51
+ }
52
+
53
+ // If no config found, assume login is required for CLI usage
54
+ // This ensures users always login before using the CLI
55
+ return { isAuthenticated: false, needsLogin: true }
56
+ } catch (error) {
57
+ // On error, assume login is required
58
+ return { isAuthenticated: false, needsLogin: true }
59
+ }
60
+ }
61
+
@@ -0,0 +1,21 @@
1
+ import { InstanceBootstrap } from "../project/bootstrap"
2
+ import { Instance } from "../project/instance"
3
+ import { BinericLogin } from "../auth/bineric-login"
4
+
5
+ export async function bootstrap<T>(directory: string, cb: () => Promise<T>) {
6
+ return Instance.provide({
7
+ directory,
8
+ init: InstanceBootstrap,
9
+ fn: async () => {
10
+ try {
11
+ // Auth check is now handled in middleware (index.ts)
12
+ // This ensures users are authenticated before any command runs
13
+ const result = await cb()
14
+ return result
15
+ } finally {
16
+ await Instance.dispose()
17
+ await BinericLogin.stop()
18
+ }
19
+ },
20
+ })
21
+ }
@@ -0,0 +1,88 @@
1
+ import { Log } from "@/util/log"
2
+ import { bootstrap } from "../bootstrap"
3
+ import { cmd } from "./cmd"
4
+ import { AgentSideConnection, ndJsonStream } from "@agentclientprotocol/sdk"
5
+ import { ACP } from "@/acp/agent"
6
+ import { Server } from "@/server/server"
7
+ import { createBincodeClient } from "@bincode-ai/sdk/v2"
8
+
9
+ const log = Log.create({ service: "acp-command" })
10
+
11
+ process.on("unhandledRejection", (reason, promise) => {
12
+ log.error("Unhandled rejection", {
13
+ promise,
14
+ reason,
15
+ })
16
+ })
17
+
18
+ export const AcpCommand = cmd({
19
+ command: "acp",
20
+ describe: "start ACP (Agent Client Protocol) server",
21
+ builder: (yargs) => {
22
+ return yargs
23
+ .option("cwd", {
24
+ describe: "working directory",
25
+ type: "string",
26
+ default: process.cwd(),
27
+ })
28
+ .option("port", {
29
+ type: "number",
30
+ describe: "port to listen on",
31
+ default: 0,
32
+ })
33
+ .option("hostname", {
34
+ type: "string",
35
+ describe: "hostname to listen on",
36
+ default: "127.0.0.1",
37
+ })
38
+ },
39
+ handler: async (args) => {
40
+ await bootstrap(process.cwd(), async () => {
41
+ const server = Server.listen({
42
+ port: args.port,
43
+ hostname: args.hostname,
44
+ })
45
+
46
+ const sdk = createBincodeClient({
47
+ baseUrl: `http://${server.hostname}:${server.port}`,
48
+ })
49
+
50
+ const input = new WritableStream<Uint8Array>({
51
+ write(chunk) {
52
+ return new Promise<void>((resolve, reject) => {
53
+ process.stdout.write(chunk, (err) => {
54
+ if (err) {
55
+ reject(err)
56
+ } else {
57
+ resolve()
58
+ }
59
+ })
60
+ })
61
+ },
62
+ })
63
+ const output = new ReadableStream<Uint8Array>({
64
+ start(controller) {
65
+ process.stdin.on("data", (chunk: Buffer) => {
66
+ controller.enqueue(new Uint8Array(chunk))
67
+ })
68
+ process.stdin.on("end", () => controller.close())
69
+ process.stdin.on("error", (err) => controller.error(err))
70
+ },
71
+ })
72
+
73
+ const stream = ndJsonStream(input, output)
74
+ const agent = await ACP.init({ sdk })
75
+
76
+ new AgentSideConnection((conn) => {
77
+ return agent.create(conn, { sdk })
78
+ }, stream)
79
+
80
+ log.info("setup connection")
81
+ process.stdin.resume()
82
+ await new Promise((resolve, reject) => {
83
+ process.stdin.on("end", resolve)
84
+ process.stdin.on("error", reject)
85
+ })
86
+ })
87
+ },
88
+ })
@@ -0,0 +1,256 @@
1
+ import { cmd } from "./cmd"
2
+ import * as prompts from "@clack/prompts"
3
+ import { UI } from "../ui"
4
+ import { Global } from "../../global"
5
+ import { Agent } from "../../agent/agent"
6
+ import { Provider } from "../../provider/provider"
7
+ import path from "path"
8
+ import fs from "fs/promises"
9
+ import matter from "gray-matter"
10
+ import { Instance } from "../../project/instance"
11
+ import { EOL } from "os"
12
+ import type { Argv } from "yargs"
13
+
14
+ type AgentMode = "all" | "primary" | "subagent"
15
+
16
+ const AVAILABLE_TOOLS = [
17
+ "bash",
18
+ "read",
19
+ "write",
20
+ "edit",
21
+ "list",
22
+ "glob",
23
+ "grep",
24
+ "webfetch",
25
+ "task",
26
+ "todowrite",
27
+ "todoread",
28
+ ]
29
+
30
+ const AgentCreateCommand = cmd({
31
+ command: "create",
32
+ describe: "create a new agent",
33
+ builder: (yargs: Argv) =>
34
+ yargs
35
+ .option("path", {
36
+ type: "string",
37
+ describe: "directory path to generate the agent file",
38
+ })
39
+ .option("description", {
40
+ type: "string",
41
+ describe: "what the agent should do",
42
+ })
43
+ .option("mode", {
44
+ type: "string",
45
+ describe: "agent mode",
46
+ choices: ["all", "primary", "subagent"] as const,
47
+ })
48
+ .option("tools", {
49
+ type: "string",
50
+ describe: `comma-separated list of tools to enable (default: all). Available: "${AVAILABLE_TOOLS.join(", ")}"`,
51
+ })
52
+ .option("model", {
53
+ type: "string",
54
+ alias: ["m"],
55
+ describe: "model to use in the format of provider/model",
56
+ }),
57
+ async handler(args) {
58
+ await Instance.provide({
59
+ directory: process.cwd(),
60
+ async fn() {
61
+ const cliPath = args.path
62
+ const cliDescription = args.description
63
+ const cliMode = args.mode as AgentMode | undefined
64
+ const cliTools = args.tools
65
+
66
+ const isFullyNonInteractive = cliPath && cliDescription && cliMode && cliTools !== undefined
67
+
68
+ if (!isFullyNonInteractive) {
69
+ UI.empty()
70
+ prompts.intro("Create agent")
71
+ }
72
+
73
+ const project = Instance.project
74
+
75
+ // Determine scope/path
76
+ let targetPath: string
77
+ if (cliPath) {
78
+ targetPath = path.join(cliPath, "agent")
79
+ } else {
80
+ let scope: "global" | "project" = "global"
81
+ if (project.vcs === "git") {
82
+ const scopeResult = await prompts.select({
83
+ message: "Location",
84
+ options: [
85
+ {
86
+ label: "Current project",
87
+ value: "project" as const,
88
+ hint: Instance.worktree,
89
+ },
90
+ {
91
+ label: "Global",
92
+ value: "global" as const,
93
+ hint: Global.Path.config,
94
+ },
95
+ ],
96
+ })
97
+ if (prompts.isCancel(scopeResult)) throw new UI.CancelledError()
98
+ scope = scopeResult
99
+ }
100
+ targetPath = path.join(
101
+ scope === "global" ? Global.Path.config : path.join(Instance.worktree, ".bincode"),
102
+ "agent",
103
+ )
104
+ }
105
+
106
+ // Get description
107
+ let description: string
108
+ if (cliDescription) {
109
+ description = cliDescription
110
+ } else {
111
+ const query = await prompts.text({
112
+ message: "Description",
113
+ placeholder: "What should this agent do?",
114
+ validate: (x) => (x && x.length > 0 ? undefined : "Required"),
115
+ })
116
+ if (prompts.isCancel(query)) throw new UI.CancelledError()
117
+ description = query
118
+ }
119
+
120
+ // Generate agent
121
+ const spinner = prompts.spinner()
122
+ spinner.start("Generating agent configuration...")
123
+ const model = args.model ? Provider.parseModel(args.model) : undefined
124
+ const generated = await Agent.generate({ description, model }).catch((error) => {
125
+ spinner.stop(`LLM failed to generate agent: ${error.message}`, 1)
126
+ if (isFullyNonInteractive) process.exit(1)
127
+ throw new UI.CancelledError()
128
+ })
129
+ spinner.stop(`Agent ${generated.identifier} generated`)
130
+
131
+ // Select tools
132
+ let selectedTools: string[]
133
+ if (cliTools !== undefined) {
134
+ selectedTools = cliTools ? cliTools.split(",").map((t) => t.trim()) : AVAILABLE_TOOLS
135
+ } else {
136
+ const result = await prompts.multiselect({
137
+ message: "Select tools to enable",
138
+ options: AVAILABLE_TOOLS.map((tool) => ({
139
+ label: tool,
140
+ value: tool,
141
+ })),
142
+ initialValues: AVAILABLE_TOOLS,
143
+ })
144
+ if (prompts.isCancel(result)) throw new UI.CancelledError()
145
+ selectedTools = result
146
+ }
147
+
148
+ // Get mode
149
+ let mode: AgentMode
150
+ if (cliMode) {
151
+ mode = cliMode
152
+ } else {
153
+ const modeResult = await prompts.select({
154
+ message: "Agent mode",
155
+ options: [
156
+ {
157
+ label: "All",
158
+ value: "all" as const,
159
+ hint: "Can function in both primary and subagent roles",
160
+ },
161
+ {
162
+ label: "Primary",
163
+ value: "primary" as const,
164
+ hint: "Acts as a primary/main agent",
165
+ },
166
+ {
167
+ label: "Subagent",
168
+ value: "subagent" as const,
169
+ hint: "Can be used as a subagent by other agents",
170
+ },
171
+ ],
172
+ initialValue: "all" as const,
173
+ })
174
+ if (prompts.isCancel(modeResult)) throw new UI.CancelledError()
175
+ mode = modeResult
176
+ }
177
+
178
+ // Build tools config
179
+ const tools: Record<string, boolean> = {}
180
+ for (const tool of AVAILABLE_TOOLS) {
181
+ if (!selectedTools.includes(tool)) {
182
+ tools[tool] = false
183
+ }
184
+ }
185
+
186
+ // Build frontmatter
187
+ const frontmatter: {
188
+ description: string
189
+ mode: AgentMode
190
+ tools?: Record<string, boolean>
191
+ } = {
192
+ description: generated.whenToUse,
193
+ mode,
194
+ }
195
+ if (Object.keys(tools).length > 0) {
196
+ frontmatter.tools = tools
197
+ }
198
+
199
+ // Write file
200
+ const content = matter.stringify(generated.systemPrompt, frontmatter)
201
+ const filePath = path.join(targetPath, `${generated.identifier}.md`)
202
+
203
+ await fs.mkdir(targetPath, { recursive: true })
204
+
205
+ const file = Bun.file(filePath)
206
+ if (await file.exists()) {
207
+ if (isFullyNonInteractive) {
208
+ console.error(`Error: Agent file already exists: ${filePath}`)
209
+ process.exit(1)
210
+ }
211
+ prompts.log.error(`Agent file already exists: ${filePath}`)
212
+ throw new UI.CancelledError()
213
+ }
214
+
215
+ await Bun.write(filePath, content)
216
+
217
+ if (isFullyNonInteractive) {
218
+ console.log(filePath)
219
+ } else {
220
+ prompts.log.success(`Agent created: ${filePath}`)
221
+ prompts.outro("Done")
222
+ }
223
+ },
224
+ })
225
+ },
226
+ })
227
+
228
+ const AgentListCommand = cmd({
229
+ command: "list",
230
+ describe: "list all available agents",
231
+ async handler() {
232
+ await Instance.provide({
233
+ directory: process.cwd(),
234
+ async fn() {
235
+ const agents = await Agent.list()
236
+ const sortedAgents = agents.sort((a, b) => {
237
+ if (a.native !== b.native) {
238
+ return a.native ? -1 : 1
239
+ }
240
+ return a.name.localeCompare(b.name)
241
+ })
242
+
243
+ for (const agent of sortedAgents) {
244
+ process.stdout.write(`${agent.name} (${agent.mode})${EOL}`)
245
+ }
246
+ },
247
+ })
248
+ },
249
+ })
250
+
251
+ export const AgentCommand = cmd({
252
+ command: "agent",
253
+ describe: "manage agents",
254
+ builder: (yargs) => yargs.command(AgentCreateCommand).command(AgentListCommand).demandCommand(),
255
+ async handler() {},
256
+ })