@botconnector/bccli 0.5.0-rc.4 → 0.5.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 (187) hide show
  1. package/LICENSE +21 -203
  2. package/README.en.md +253 -0
  3. package/README.md +250 -0
  4. package/dist/abort.d.ts +5 -0
  5. package/dist/acp-HWHF25KU.js +221 -0
  6. package/dist/acp.d.ts +44 -0
  7. package/dist/agent.d.ts +158 -0
  8. package/dist/agents.d.ts +4 -0
  9. package/dist/args-XA77XUMD.js +13 -0
  10. package/dist/args.d.ts +59 -0
  11. package/dist/budget.d.ts +26 -0
  12. package/dist/checkpoints.d.ts +35 -0
  13. package/dist/chunk-2IP5F3EK.js +56 -0
  14. package/dist/chunk-5CVHTYBQ.js +270 -0
  15. package/dist/chunk-6LO6YRDY.js +216 -0
  16. package/dist/chunk-6PJRWKVI.js +50 -0
  17. package/dist/chunk-6YRTPXGY.js +8 -0
  18. package/dist/chunk-7OTFZK4B.js +159 -0
  19. package/dist/chunk-7U7CMKKI.js +861 -0
  20. package/dist/chunk-A3NBWIEQ.js +2706 -0
  21. package/dist/chunk-FY543EQW.js +50 -0
  22. package/dist/chunk-IDZLHEVL.js +524 -0
  23. package/dist/chunk-IF5RNXBI.js +341 -0
  24. package/dist/chunk-M5T4AM4I.js +256 -0
  25. package/dist/chunk-NGFWBVHD.js +71 -0
  26. package/dist/chunk-OCEQ2TRJ.js +1075 -0
  27. package/dist/chunk-OK2USAXX.js +88 -0
  28. package/dist/chunk-PSO7R5AD.js +10 -0
  29. package/dist/chunk-RLSQAZBT.js +78 -0
  30. package/dist/chunk-VPFCIUC6.js +91 -0
  31. package/dist/chunk-YGBTNDPK.js +202 -0
  32. package/dist/chunk-YYTFJUO2.js +148 -0
  33. package/dist/chunk-ZXWRLN7E.js +378 -0
  34. package/dist/cli.d.ts +1 -0
  35. package/dist/cli.js +251 -0
  36. package/dist/clipboardImage.d.ts +17 -0
  37. package/dist/commands.d.ts +16 -0
  38. package/dist/completion-IRVEVW6H.js +125 -0
  39. package/dist/completion.d.ts +14 -0
  40. package/dist/config-OVWCUVY7.js +25 -0
  41. package/dist/config-S3CZUIBB.js +34 -0
  42. package/dist/config.d.ts +99 -0
  43. package/dist/context.d.ts +13 -0
  44. package/dist/contextWindow.d.ts +3 -0
  45. package/dist/diff.d.ts +7 -0
  46. package/dist/exit.d.ts +9 -0
  47. package/dist/extensions.d.ts +34 -0
  48. package/dist/ghBotCli-FYKJDJOD.js +680 -0
  49. package/dist/ghBotCli.d.ts +9 -0
  50. package/dist/ghBotDispatch.d.ts +68 -0
  51. package/dist/ghBotRunner.d.ts +82 -0
  52. package/dist/ghBotServe.d.ts +21 -0
  53. package/dist/ghBotWebhook.d.ts +45 -0
  54. package/dist/hooks.d.ts +27 -0
  55. package/dist/i18n/id.d.ts +1 -0
  56. package/dist/i18n.d.ts +16 -0
  57. package/dist/integrationCli-GY73S2K7.js +876 -0
  58. package/dist/integrationCli.d.ts +9 -0
  59. package/dist/keybinds.d.ts +22 -0
  60. package/dist/login-KSN63EM4.js +18 -0
  61. package/dist/login.d.ts +21 -0
  62. package/dist/lsp.d.ts +73 -0
  63. package/dist/mcp/catalog.d.ts +14 -0
  64. package/dist/mcp/config.d.ts +36 -0
  65. package/dist/mcp/manager.d.ts +52 -0
  66. package/dist/mcp/oauth.d.ts +82 -0
  67. package/dist/mcpCli-VWSCCDWT.js +113 -0
  68. package/dist/mcpCli.d.ts +3 -0
  69. package/dist/mcpServer/serve.d.ts +8 -0
  70. package/dist/mcpServer/server.d.ts +19 -0
  71. package/dist/models.d.ts +13 -0
  72. package/dist/modelsCli-4Q6KFWV2.js +36 -0
  73. package/dist/modelsCli.d.ts +15 -0
  74. package/dist/npmRunner-LU43GM3W.js +46 -0
  75. package/dist/npmRunner.d.ts +21 -0
  76. package/dist/oauthLogin-JWXQZTMZ.js +210 -0
  77. package/dist/oauthLogin.d.ts +47 -0
  78. package/dist/onboarding.d.ts +6 -0
  79. package/dist/permissions.d.ts +30 -0
  80. package/dist/pluginSpec.d.ts +25 -0
  81. package/dist/plugins.d.ts +45 -0
  82. package/dist/pluginsCli-7XWDLJEL.js +288 -0
  83. package/dist/pluginsCli.d.ts +16 -0
  84. package/dist/prCli-FH7N26D4.js +94 -0
  85. package/dist/prCli.d.ts +46 -0
  86. package/dist/presets.d.ts +8 -0
  87. package/dist/print.d.ts +17 -0
  88. package/dist/provider.d.ts +145 -0
  89. package/dist/providerCli-YMG4G56R.js +15 -0
  90. package/dist/providerCli.d.ts +17 -0
  91. package/dist/providers.d.ts +6 -0
  92. package/dist/reasoning.d.ts +12 -0
  93. package/dist/repomap.d.ts +10 -0
  94. package/dist/sdk.d.ts +61 -0
  95. package/dist/sdk.js +6532 -0
  96. package/dist/serve-HSFF6MGB.js +178 -0
  97. package/dist/serve-R2IHP26A.js +532 -0
  98. package/dist/serve.d.ts +37 -0
  99. package/dist/serveHeartbeat.d.ts +14 -0
  100. package/dist/serveProtocol.d.ts +47 -0
  101. package/dist/session-YM36TGU7.js +12 -0
  102. package/dist/session.d.ts +33 -0
  103. package/dist/setup-3YB6EKYX.js +24 -0
  104. package/dist/setup.d.ts +79 -0
  105. package/dist/slash/copy.d.ts +1 -0
  106. package/dist/slash/diff.d.ts +1 -0
  107. package/dist/slash/doctor.d.ts +18 -0
  108. package/dist/slash/export.d.ts +2 -0
  109. package/dist/slash/info.d.ts +46 -0
  110. package/dist/slash/memory.d.ts +10 -0
  111. package/dist/statsCli-I4TSDU3X.js +353 -0
  112. package/dist/statsCli.d.ts +62 -0
  113. package/dist/textToolCalls.d.ts +6 -0
  114. package/dist/thinking.d.ts +17 -0
  115. package/dist/tokenBudget.d.ts +22 -0
  116. package/dist/tools/bash.d.ts +54 -0
  117. package/dist/tools/diagnostics.d.ts +13 -0
  118. package/dist/tools/edit.d.ts +12 -0
  119. package/dist/tools/fetch.d.ts +41 -0
  120. package/dist/tools/glob.d.ts +5 -0
  121. package/dist/tools/globSafety.d.ts +7 -0
  122. package/dist/tools/grep.d.ts +25 -0
  123. package/dist/tools/ignore.d.ts +2 -0
  124. package/dist/tools/index.d.ts +12 -0
  125. package/dist/tools/memory.d.ts +13 -0
  126. package/dist/tools/paths.d.ts +4 -0
  127. package/dist/tools/plan.d.ts +10 -0
  128. package/dist/tools/read.d.ts +16 -0
  129. package/dist/tools/repomap.d.ts +2 -0
  130. package/dist/tools/sandbox.d.ts +15 -0
  131. package/dist/tools/shell.d.ts +18 -0
  132. package/dist/tools/skill.d.ts +3 -0
  133. package/dist/tools/task.d.ts +28 -0
  134. package/dist/tools/todo.d.ts +13 -0
  135. package/dist/tools/types.d.ts +52 -0
  136. package/dist/tools/websearch.d.ts +9 -0
  137. package/dist/tools/write.d.ts +5 -0
  138. package/dist/transferCli-3B4LMOYI.js +73 -0
  139. package/dist/transferCli.d.ts +23 -0
  140. package/dist/ui/App.d.ts +7 -0
  141. package/dist/ui/DiffView.d.ts +3 -0
  142. package/dist/ui/EditableText.d.ts +7 -0
  143. package/dist/ui/LinePrompt.d.ts +6 -0
  144. package/dist/ui/ListPicker.d.ts +11 -0
  145. package/dist/ui/Markdown.d.ts +8 -0
  146. package/dist/ui/McpMenu.d.ts +11 -0
  147. package/dist/ui/ModelPicker.d.ts +6 -0
  148. package/dist/ui/PermissionPrompt.d.ts +5 -0
  149. package/dist/ui/PlanApproval.d.ts +5 -0
  150. package/dist/ui/PromptInput.d.ts +20 -0
  151. package/dist/ui/ProviderMenu.d.ts +10 -0
  152. package/dist/ui/Spinner.d.ts +4 -0
  153. package/dist/ui/StatusBar.d.ts +14 -0
  154. package/dist/ui/TodoList.d.ts +4 -0
  155. package/dist/ui/ToolBlock.d.ts +14 -0
  156. package/dist/ui/complete.d.ts +8 -0
  157. package/dist/ui/index.d.ts +7 -0
  158. package/dist/ui/lineEdit.d.ts +42 -0
  159. package/dist/ui/liveCap.d.ts +29 -0
  160. package/dist/ui/markdownLayout.d.ts +23 -0
  161. package/dist/ui/replay.d.ts +4 -0
  162. package/dist/ui/stdinFilter.d.ts +18 -0
  163. package/dist/ui/table.d.ts +20 -0
  164. package/dist/ui/theme.d.ts +5 -0
  165. package/dist/ui/thinkingMouse.d.ts +38 -0
  166. package/dist/ui/transcript.d.ts +55 -0
  167. package/dist/ui/tty.d.ts +11 -0
  168. package/dist/ui/vim.d.ts +26 -0
  169. package/dist/ui-TAZMRXRI.js +3044 -0
  170. package/dist/update-XC3B6SJC.js +145 -0
  171. package/dist/update.d.ts +30 -0
  172. package/dist/verify.d.ts +16 -0
  173. package/dist/version.d.ts +1 -0
  174. package/dist/webBudget.d.ts +25 -0
  175. package/dist/worktree.d.ts +12 -0
  176. package/package.json +59 -35
  177. package/THIRD_PARTY_NOTICES.md +0 -9
  178. package/branding.mjs +0 -347
  179. package/cli.mjs +0 -120
  180. package/core.mjs +0 -260
  181. package/credentials.mjs +0 -52
  182. package/gateway.mjs +0 -2
  183. package/model-command.mjs +0 -61
  184. package/provider-cli.mjs +0 -77
  185. package/providers.mjs +0 -166
  186. package/proxy.mjs +0 -242
  187. package/startup-menu.mjs +0 -120
package/README.md ADDED
@@ -0,0 +1,250 @@
1
+ # BCCLI
2
+
3
+ **Bahasa Indonesia** · [English](README.en.md)
4
+
5
+ [![npm](https://img.shields.io/npm/v/@botconnector/bccli?label=@botconnector/bccli)](https://www.npmjs.com/package/@botconnector/bccli?activeTab=versions)
6
+ [![npm downloads](https://img.shields.io/npm/dm/@botconnector/bccli)](https://www.npmjs.com/package/@botconnector/bccli)
7
+ [![Node.js](https://img.shields.io/node/v/@botconnector/bccli?label=Node.js)](https://nodejs.org/)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
9
+ [![Website](https://img.shields.io/badge/website-botconnector.id-06305A)](https://botconnector.id/id/download)
10
+
11
+ Agent AI coding dari [BotConnector](https://botconnector.id/) yang bekerja di terminal: membaca kode, mengedit file,
12
+ dan menjalankan perintah — dengan izin kamu.
13
+
14
+ ![BCCLI membuat hello.js dan menjalankannya dengan node](docs/demo.gif)
15
+
16
+ *Rekaman sesi asli: BCCLI membuat file `hello.js` lalu menjalankannya dengan `node`.*
17
+
18
+ ```bash
19
+ # install satu baris (macOS / Linux / WSL2) — pasang Node portable bila perlu, tanpa sudo
20
+ curl -fsSL https://raw.githubusercontent.com/farijarifriyanto-debug/bccli/main/install.sh | bash
21
+
22
+ # Windows (PowerShell 5.1+) — butuh Node.js 22+ terpasang lebih dulu
23
+ irm https://raw.githubusercontent.com/farijarifriyanto-debug/bccli/main/install.ps1 | iex
24
+
25
+ # atau manual, bila Node.js 22+ sudah terpasang
26
+ npm i -g @botconnector/bccli # rilis stabil terbaru, butuh Node.js 22+
27
+ npm i -g @botconnector/bccli@next # …atau pra-rilis berikutnya
28
+ bccli login # simpan API key BotConnector Cloud
29
+ cd project-kamu && bccli
30
+ ```
31
+
32
+ ## Pemakaian
33
+
34
+ | Perintah | Fungsi |
35
+ |---|---|
36
+ | `bccli` | mode interaktif |
37
+ | `bccli -p "tugas"` | satu tugas tanpa interaksi (skrip/CI) |
38
+ | `bccli -p "tugas" --output-format json` | hasil akhir sebagai satu objek JSON; `stream-json` = event JSONL per tahap (tool_use/tool_result/message/result) untuk CI |
39
+ | `bccli -c` / `bccli -r` | lanjutkan sesi terakhir / pilih sesi |
40
+ | `bccli -m provider/model` | pilih model |
41
+ | `bccli --reasoning <auto|off|low|medium|high|max>` | pilih level reasoning untuk invocation ini |
42
+ | `bccli models` | daftar model |
43
+
44
+ Perintah di dalam sesi (ketik `/` untuk saran, ↑↓ pilih, Enter jalankan):
45
+
46
+ | Perintah | Fungsi |
47
+ |---|---|
48
+ | `/help` | daftar perintah dan pintasan |
49
+ | `/model` | ganti model |
50
+ | `/reasoning` | pilih Auto / Off / Low / Medium / High / Max |
51
+ | `/provider` | tambah/pilih provider AI |
52
+ | `/language` | ganti bahasa tampilan (`en` / `id`) |
53
+ | `/mcp` | pasang/kelola server MCP |
54
+ | `/new` | sesi baru (sesi lama tetap tersimpan) |
55
+ | `/resume` | lanjutkan sesi lain di folder ini |
56
+ | `/session` | info sesi ini |
57
+ | `/status` | versi, model, mode izin, MCP, konteks |
58
+ | `/permissions` | lihat/cabut izin |
59
+ | `/pr` | review pull request (`/pr <n\|url\|branch>`) |
60
+ | `/worktree` | daftar git worktree; buat dengan `bccli -w <nama>` |
61
+ | `/undo` | batalkan edit file giliran terakhir |
62
+ | `/redo` | terapkan ulang edit file yang baru dibatalkan `/undo` |
63
+ | `/rewind` | mundur beberapa giliran: percakapan + edit file (`/rewind 2`) |
64
+ | `/diff` | git diff project |
65
+ | `/copy` | salin jawaban terakhir |
66
+ | `/export` | simpan percakapan ke markdown |
67
+ | `/memory` | instruksi project (/memory <teks>, /memory global <teks>) |
68
+ | `/init` | buat/perbarui AGENTS.md untuk project ini |
69
+ | `/agents` | daftar subagent |
70
+ | `/tasks` | daftar tugas latar belakang |
71
+ | `/skills` | daftar skill dan perintah custom |
72
+ | `/doctor` | cek kesehatan instalasi |
73
+ | `/login` | simpan API key provider aktif |
74
+ | `/logout` | hapus API key provider aktif |
75
+ | `/clear` | mulai percakapan baru |
76
+ | `/compact` | ringkas percakapan |
77
+ | `/cost` | pemakaian token sesi ini |
78
+ | `/exit` | keluar |
79
+
80
+ `/undo` hanya membatalkan perubahan lewat alat edit/tulis (bukan perintah bash), per giliran, sampai 20 giliran ke belakang.
81
+ Shift+Tab ganti mode izin: `default` → `acceptEdits` → `plan` → `allowAll`.
82
+
83
+ Membaca file rahasia (`.env`, `.ssh`, `.aws`, `id_rsa`, `*.pem`, `*.key`, `secret*`,
84
+ `credential*`) selalu meminta izin; `allowAll` satu-satunya mode yang tidak bertanya, dan
85
+ izin itu tidak bisa diberikan "untuk sesi ini".
86
+
87
+ Mode `-p` tidak pernah bertanya: alat yang butuh izin ditolak, kecuali memakai
88
+ `--allow-all` atau `--allowed-tools bash,edit,fetch`.
89
+
90
+ ## Bahasa
91
+
92
+ Tampilan BCCLI (bantuan, pesan, prompt izin) memakai **bahasa Inggris secara default**. Bahasa Indonesia tetap tersedia:
93
+
94
+ ```bash
95
+ bccli --lang id # hanya untuk satu kali jalan
96
+ BCCLI_LANG=id bccli # lewat environment
97
+ /language id # di dalam sesi; disimpan di ~/.bccli/config.json ("language")
98
+ ```
99
+
100
+ Urutan prioritas: `--lang`, lalu `BCCLI_LANG`, lalu `language` di config, lalu Inggris. Bahasa jawaban model mengikuti bahasa yang kamu tulis, dan
101
+ pesan alat yang dibaca model (hasil tool, galat) selalu berbahasa Inggris supaya tidak mengganggu bahasa jawabannya.
102
+
103
+ ## Provider
104
+
105
+ `/provider` di dalam sesi (atau `bccli provider add <id>`) memasang provider hanya dengan API key:
106
+
107
+ | Id | Provider |
108
+ |---|---|
109
+ | `bc-cloud` | BotConnector Cloud (default) |
110
+ | `openrouter` | OpenRouter |
111
+ | `openai` | OpenAI |
112
+ | `gemini` | Google Gemini |
113
+ | `deepseek` | DeepSeek |
114
+ | `groq` | Groq |
115
+ | `ollama-cloud` | Ollama Cloud |
116
+ | `ollama` | Ollama lokal (tanpa key) |
117
+ | `lmstudio` | LM Studio lokal (tanpa key) |
118
+
119
+ Provider lain yang OpenAI-compatible: pilih **Custom…** di `/provider`, atau
120
+ `bccli provider add corp --url https://corp.example/v1 --name "Corp"`.
121
+
122
+ `/model` menampilkan model dari **semua** provider yang sudah punya key, dikelompokkan
123
+ per provider; ketik untuk menyaring. Pilihan tersimpan sebagai default.
124
+
125
+ Reasoning dapat dipilih lewat `bccli --reasoning <auto|off|low|medium|high|max>` atau `/reasoning`.
126
+ **Auto** memakai default model/provider dan BCCLI tidak mengirim override reasoning apa pun. BCCLI tidak
127
+ otomatis menurunkan atau menaikkan reasoning effort. Level manual diteruskan bila kontrak provider/model
128
+ mendukungnya; level yang tidak didukung menghasilkan error yang jelas dan tidak di-downgrade diam-diam.
129
+
130
+ `.bccli/config.json` di dalam project hanya boleh memilih `model` dan menambah provider baru tanpa API key —
131
+ preference `reasoning` hanya dibaca dari config user `~/.bccli/config.json`, bukan dari repo yang di-clone —
132
+ izin (`permissionMode`, `allow`) dan provider yang sudah ada hanya diatur dari `~/.bccli/config.json`,
133
+ supaya repo yang kamu clone tidak bisa memberi dirinya izin penuh atau mencuri API key.
134
+
135
+ Daftar lengkap kunci `~/.bccli/config.json` beserta nilai dan cakupannya: [`docs/config.md`](docs/config.md).
136
+
137
+ ## Integrasi agent eksternal
138
+
139
+ BCCLI dapat memasang BotConnector sebagai provider untuk agent lain:
140
+
141
+ | Perintah | Fungsi |
142
+ |---|---|
143
+ | `bccli integrations` | lihat status integrasi |
144
+ | `bccli connect opencode` | hubungkan OpenCode ke BotConnector |
145
+ | `bccli connect aider` | hubungkan Aider ke BotConnector |
146
+ | `bccli connect cline` | hubungkan Cline CLI ke BotConnector |
147
+ | `bccli connect deepseek-harness` / `bccli connect dsh` | hubungkan DeepSeek Harness ke BotConnector |
148
+ | `bccli connect codex` | buat profile Codex Responses API + launcher BotConnector |
149
+ | `bccli connect claude-code` | buat launcher Claude Code via Anthropic Messages compatibility |
150
+ | `bccli connect cursor` | buat guided setup Cursor + warning override Base URL |
151
+ | `bccli connect openai-cli` | buat launcher OpenAI CLI khusus BotConnector |
152
+ | `bccli connect openai-sdk` | buat env profile untuk OpenAI Python/Node SDK |
153
+ | `bccli connect openai-compatible` | buat env profile universal OpenAI-compatible |
154
+ | `bccli disconnect <agent>` | pulihkan config sebelum integrasi |
155
+
156
+ OpenCode memakai config global `~/.config/opencode/opencode.json`. BCCLI mengambil katalog model BotConnector saat connect,
157
+ menjaga config provider lain, dan menyimpan secret di file terpisah di `~/.bccli/integrations/`.
158
+ Aider memakai `~/.aider.conf.yml` yang menunjuk ke env file BCCLI terpisah; BCCLI menolak menimpa setting
159
+ OpenAI/env-file Aider yang sudah ada.
160
+
161
+ Cline memakai provider `openai-compatible` di `~/.cline/data/settings/providers.json`. Jika slot itu sudah dipakai
162
+ endpoint lain, BCCLI menolak menimpanya. Cline sendiri menyimpan API key provider di file tersebut.
163
+
164
+ DeepSeek Harness memakai custom provider `botconnector` di `$DSH_HOME/settings.yaml` dengan
165
+ `api: openai-completions` dan `apiKeyEnv: BOTCONNECTOR_API_KEY`; key diletakkan pada blok BCCLI di
166
+ `$DSH_HOME/.env`. Provider DSH lain tetap dipertahankan. Setelah connect, jalankan profile web dengan
167
+ `dsh web` (setara dengan `dsh --profile web`); `dsh` tanpa profile memang ditolak oleh DSH 0.1.x.
168
+
169
+ Codex memakai profile `$CODEX_HOME/botconnector.config.toml` dengan `wire_api = "responses"` dan launcher
170
+ BCCLI yang membaca key dari storage BCCLI saat dijalankan. Claude Code memakai launcher terisolasi dengan
171
+ `ANTHROPIC_BASE_URL=https://api.botconnector.id` dan endpoint kompatibilitas `/v1/messages` + token counting.
172
+ Login/config utama Codex dan Claude Code tidak ditimpa.
173
+
174
+ External agent memanggil BotConnector langsung, jadi discovery model tidak memakai identitas khusus BCCLI.
175
+ Akses yang memang dibatasi ke BCCLI/Web App (misalnya Luna launch access) tidak otomatis diwariskan.
176
+ Sebelum mengubah config eksternal, BCCLI membuat backup agar disconnect dapat memulihkannya.
177
+
178
+ ## Internet
179
+
180
+ - `web_search`: mencari di web tanpa perlu izin. Memakai akun BotConnector Cloud
181
+ bila ada key, kalau tidak langsung ke Keenable (gratis, tanpa key). Hasilnya
182
+ judul, URL, dan cuplikan singkat, jadi hemat token.
183
+ - `fetch`: membuka satu URL dari komputer ini (butuh izin per host).
184
+
185
+ ## MCP
186
+
187
+ `/mcp` (atau `bccli mcp add <nama>`) memasang server dari katalog:
188
+ `playwright`, `context7`, `fetch`, `filesystem`, `git`, `github`. Server remote lain:
189
+ `bccli mcp add nama --url https://…`. Tersimpan di `~/.bccli/mcp.json` (format `mcpServers`
190
+ seperti Claude Code). Semua alat MCP selalu minta izin.
191
+
192
+ `.bccli/mcp.json` di dalam repo tidak dijalankan sebelum kamu setujui (ditanya sekali per folder).
193
+
194
+ `bccli mcp serve` menjalankan MCP server stdio dengan tools BotConnector milikmu
195
+ (`bc_search`, `bc_fetch`, `bc_models`, `bc_chat`) untuk dipakai Claude Code, Cursor,
196
+ opencode, dan host MCP lain. Butuh `bccli login` (atau `BOTCONNECTOR_API_KEY`):
197
+
198
+ ```bash
199
+ claude mcp add botconnector -- npx -y @botconnector/bccli mcp serve
200
+ ```
201
+
202
+ ## Subagent
203
+
204
+ Agent bisa mendelegasikan pekerjaan lewat alat `task`:
205
+ - `explore` — hanya baca & cari, bisa jalan paralel;
206
+ - `general` — semua alat (edit/perintah tetap minta izin);
207
+ - custom — `.bccli/agents/nama.md` atau `.claude/agents/nama.md`:
208
+
209
+ ```markdown
210
+ ---
211
+ name: reviewer
212
+ description: review perubahan kode
213
+ tools: [read, grep, glob]
214
+ model: openrouter/qwen/qwen3-coder
215
+ ---
216
+ Kamu reviewer kode. Laporkan bug nyata saja.
217
+ ```
218
+
219
+ ## Skill & perintah custom
220
+
221
+ - Skill: folder berisi `SKILL.md` di `~/.bccli/skills/`, `.bccli/skills/`, `~/.claude/skills/`,
222
+ `.claude/skills/` — dipakai agent otomatis bila cocok, atau `/nama-skill`.
223
+ - Perintah: `commands/nama.md` di folder yang sama → `/nama argumen` (`$ARGUMENTS` diganti argumen).
224
+
225
+ ## Todo & mode plan
226
+
227
+ Tugas ≥ 3 langkah menampilkan checklist di atas kotak input. Di mode plan (Shift+Tab),
228
+ agent hanya membaca lalu mengajukan rencana; `[a]` setujui dengan edit otomatis,
229
+ `[y]` setujui dengan tanya tiap langkah, `[n]` minta perbaikan.
230
+
231
+ Instruksi project dibaca dari `AGENTS.md`, `CLAUDE.md`, dan `BCCLI.md`.
232
+
233
+ ## Pengembangan
234
+
235
+ ```bash
236
+ npm install
237
+ npm test && npm run typecheck && npm run lint && npm run build
238
+ npm run coverage # gerbang coverage (juga dijalankan di CI Linux)
239
+ ```
240
+
241
+ CI (`ci.yml`) menjalankan hal yang sama di Linux, macOS, dan Windows, plus gerbang coverage di Linux.
242
+ Rilis: naikkan `version` di `package.json`, lalu push tag `v<version>` — `release.yml`
243
+ menjalankan test, smoke test ke BotConnector Cloud, dan `npm publish --provenance`.
244
+
245
+ ## Pelajari lebih lanjut
246
+
247
+ - [Unduh & panduan BCCLI](https://botconnector.id/id/download)
248
+ - [Developer API BotConnector](https://botconnector.id/id/developers/api) — endpoint kompatibel OpenAI SDK
249
+ - [Switchboard](https://github.com/farijarifriyanto-debug/switchboard) — harness agent open source dari BotConnector
250
+ - [Berkontribusi](CONTRIBUTING.md)
@@ -0,0 +1,5 @@
1
+ export declare const ABORTED: unique symbol;
2
+ /** Resolves with the promise's value, or with ABORTED as soon as the signal fires. */
3
+ export declare function raceAbort<T>(promise: Promise<T>, signal: AbortSignal): Promise<T | typeof ABORTED>;
4
+ /** Collects an async iterable, giving up (and telling the iterator to stop) as soon as the signal fires. */
5
+ export declare function collectUntilAbort<T>(iterable: AsyncIterable<T>, signal: AbortSignal | undefined, onItem: (item: T) => void): Promise<boolean>;
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ t
4
+ } from "./chunk-IDZLHEVL.js";
5
+
6
+ // src/acp.ts
7
+ var ACP_PROTOCOL_VERSION = 1;
8
+ var TOOL_KINDS = {
9
+ read: "read",
10
+ repo_map: "read",
11
+ edit: "edit",
12
+ write: "edit",
13
+ grep: "search",
14
+ glob: "search",
15
+ bash: "execute",
16
+ powershell: "execute",
17
+ fetch: "fetch",
18
+ web_search: "fetch"
19
+ };
20
+ var kindOf = (tool) => TOOL_KINDS[tool] ?? "other";
21
+ var titleOf = (tool, target) => (target ? `${tool}: ${target}` : tool).slice(0, 200);
22
+ var MAX_OUTPUT = 4e3;
23
+ var PERMISSION_OPTIONS = [
24
+ { optionId: "allow", name: "Allow", kind: "allow_once" },
25
+ { optionId: "allow_session", name: "Allow for this session", kind: "allow_always" },
26
+ { optionId: "reject", name: "Reject", kind: "reject_once" }
27
+ ];
28
+ var ANSWERS = { allow: "yes", allow_session: "session", reject: "no" };
29
+ function runAcp(input, output, opts) {
30
+ const sessions = /* @__PURE__ */ new Map();
31
+ const awaiting = /* @__PURE__ */ new Map();
32
+ let nextSession = 1;
33
+ let nextRequest = 1;
34
+ const write = (msg) => {
35
+ output.write(`${JSON.stringify(msg)}
36
+ `);
37
+ };
38
+ const reply = (id, result) => write({ jsonrpc: "2.0", id, result });
39
+ const replyError = (id, code, message) => write({ jsonrpc: "2.0", id, error: { code, message } });
40
+ const update = (sessionId, body) => write({ jsonrpc: "2.0", method: "session/update", params: { sessionId, update: body } });
41
+ const ioFor = (sid, session) => ({
42
+ onThinking(delta) {
43
+ update(sid, { sessionUpdate: "agent_thought_chunk", content: { type: "text", text: delta } });
44
+ },
45
+ toolStart(id, tool, target) {
46
+ session.open.push({ id, tool, target });
47
+ update(sid, { sessionUpdate: "tool_call", toolCallId: id, title: titleOf(tool, target), kind: kindOf(tool), status: "in_progress", rawInput: { target } });
48
+ },
49
+ toolEnd(id, output2, isError) {
50
+ session.open = session.open.filter((c) => c.id !== id);
51
+ const text = output2.length > MAX_OUTPUT ? `${output2.slice(0, MAX_OUTPUT)}
52
+ [truncated]` : output2;
53
+ update(sid, {
54
+ sessionUpdate: "tool_call_update",
55
+ toolCallId: id,
56
+ status: isError ? "failed" : "completed",
57
+ ...text ? { content: [{ type: "content", content: { type: "text", text } }] } : {}
58
+ });
59
+ },
60
+ askPermission(req) {
61
+ return new Promise((resolve) => {
62
+ const requestId = nextRequest++;
63
+ const call = [...session.open].reverse().find((c) => c.tool === req.tool && c.target === req.target);
64
+ const settle = (answer) => {
65
+ session.pending.delete(settle);
66
+ awaiting.delete(requestId);
67
+ resolve(answer);
68
+ };
69
+ session.pending.add(settle);
70
+ awaiting.set(requestId, (message) => {
71
+ const outcome = message.result?.outcome;
72
+ settle(outcome?.outcome === "selected" ? ANSWERS[outcome.optionId ?? ""] ?? "no" : "no");
73
+ });
74
+ write({
75
+ jsonrpc: "2.0",
76
+ id: requestId,
77
+ method: "session/request_permission",
78
+ params: {
79
+ sessionId: sid,
80
+ toolCall: { toolCallId: call?.id ?? `permission-${requestId}`, title: titleOf(req.tool, req.target), kind: kindOf(req.tool), status: "pending" },
81
+ options: PERMISSION_OPTIONS
82
+ }
83
+ });
84
+ });
85
+ }
86
+ });
87
+ const handle = async (msg) => {
88
+ const { id, method, params } = msg ?? {};
89
+ if (method === void 0 && id !== void 0 && (msg.result !== void 0 || msg.error !== void 0)) {
90
+ awaiting.get(id)?.(msg);
91
+ return;
92
+ }
93
+ if (method === "initialize") {
94
+ return reply(id, {
95
+ protocolVersion: ACP_PROTOCOL_VERSION,
96
+ agentCapabilities: {
97
+ loadSession: false,
98
+ promptCapabilities: { image: false, audio: false, embeddedContext: true }
99
+ }
100
+ });
101
+ }
102
+ if (method === "session/new") {
103
+ const sessionId = `s${nextSession++}`;
104
+ sessions.set(sessionId, { controller: null, pending: /* @__PURE__ */ new Set(), open: [] });
105
+ return reply(id, { sessionId });
106
+ }
107
+ if (method === "session/cancel") {
108
+ const session = sessions.get(params?.sessionId ?? "");
109
+ session?.controller?.abort();
110
+ for (const settle of [...session?.pending ?? []]) settle("no");
111
+ return;
112
+ }
113
+ if (method === "session/prompt") {
114
+ const sid = params?.sessionId ?? "";
115
+ const session = sessions.get(sid);
116
+ if (!session) return replyError(id, -32602, "Unknown session");
117
+ if (session.controller) return replyError(id, -32e3, "Session is busy");
118
+ const text = (params?.prompt ?? []).map((p) => {
119
+ if (p?.type === "text") return String(p.text ?? "");
120
+ if ((p?.type === "resource" || p?.type === "embeddedContext") && p.resource?.text) {
121
+ const label = p.resource.uri ? `
122
+ [${p.resource.uri}]
123
+ ` : "\n";
124
+ return `${label}${p.resource.text}`;
125
+ }
126
+ return "";
127
+ }).filter(Boolean).join("\n");
128
+ const controller = new AbortController();
129
+ session.controller = controller;
130
+ let cancelled = false;
131
+ controller.signal.addEventListener("abort", () => {
132
+ cancelled = true;
133
+ });
134
+ try {
135
+ session.agent ??= opts.newAgent(params?.cwd ?? opts.cwd);
136
+ await session.agent.run(text, (chunk) => update(sid, { sessionUpdate: "agent_message_chunk", content: { type: "text", text: chunk } }), controller.signal, ioFor(sid, session));
137
+ } catch (e) {
138
+ if (cancelled) return reply(id, { stopReason: "cancelled" });
139
+ return replyError(id, -32603, e.message);
140
+ } finally {
141
+ if (session.controller === controller) {
142
+ session.controller = null;
143
+ session.open = [];
144
+ }
145
+ }
146
+ return reply(id, { stopReason: cancelled ? "cancelled" : "end_turn" });
147
+ }
148
+ if (id !== void 0) return replyError(id, -32601, "Method not found");
149
+ };
150
+ return new Promise((resolve) => {
151
+ let buf = "";
152
+ const onData = (chunk) => {
153
+ buf += chunk.toString();
154
+ let idx = buf.indexOf("\n");
155
+ while (idx >= 0) {
156
+ const line = buf.slice(0, idx);
157
+ buf = buf.slice(idx + 1);
158
+ idx = buf.indexOf("\n");
159
+ if (!line.trim()) continue;
160
+ let msg;
161
+ try {
162
+ msg = JSON.parse(line);
163
+ } catch {
164
+ continue;
165
+ }
166
+ void handle(msg);
167
+ }
168
+ };
169
+ const onEnd = () => {
170
+ input.removeListener("data", onData);
171
+ for (const session of sessions.values()) {
172
+ session.controller?.abort();
173
+ for (const settle of [...session.pending]) settle("no");
174
+ }
175
+ resolve();
176
+ };
177
+ input.on("data", onData);
178
+ input.on("end", onEnd);
179
+ });
180
+ }
181
+ function planDecisionFrom(answer) {
182
+ if (answer === "no") return "no";
183
+ if (answer === "session") return "acceptEdits";
184
+ return "default";
185
+ }
186
+ function runtimeAgent(cwd, allowAll = false, deps = {}) {
187
+ let rt;
188
+ return {
189
+ async run(text, onChunk, signal, io) {
190
+ if (!rt) {
191
+ if (deps.createRuntime) rt = deps.createRuntime({ cwd, allowAll });
192
+ else {
193
+ const { parseCliArgs } = await import("./args-XA77XUMD.js");
194
+ const { createRuntime } = await import("./setup-3YB6EKYX.js");
195
+ rt = createRuntime({ cwd, args: { ...parseCliArgs([]), allowAll } });
196
+ }
197
+ }
198
+ if (io && !allowAll) {
199
+ rt.agent.askPermission = (req) => io.askPermission(req);
200
+ rt.interaction.approvePlan = async (plan) => planDecisionFrom(await io.askPermission({ tool: "exit_plan", kind: "edit", target: t("plan"), preview: plan }));
201
+ }
202
+ let turnError;
203
+ rt.agent.onEvent = (event) => {
204
+ if (event.type === "text") onChunk(event.delta);
205
+ else if (event.type === "thinking") io?.onThinking?.(event.delta);
206
+ else if (event.type === "toolStart") io?.toolStart(event.id, event.tool, event.target);
207
+ else if (event.type === "toolEnd") io?.toolEnd(event.id, event.output, event.isError);
208
+ else if (event.type === "error") turnError = event.message;
209
+ else if (event.type === "budgetExceeded") turnError = `Budget exceeded (${event.kind})`;
210
+ };
211
+ await rt.agent.run(text, signal);
212
+ if (turnError) throw new Error(turnError);
213
+ }
214
+ };
215
+ }
216
+ export {
217
+ ACP_PROTOCOL_VERSION,
218
+ planDecisionFrom,
219
+ runAcp,
220
+ runtimeAgent
221
+ };
package/dist/acp.d.ts ADDED
@@ -0,0 +1,44 @@
1
+ import type { PermissionAnswer, PermissionAsk } from './agent';
2
+ import type { Runtime } from './setup';
3
+ import type { PlanDecision } from './tools/plan';
4
+ export declare const ACP_PROTOCOL_VERSION = 1;
5
+ /** What the agent reports to the editor besides text: tool calls, and permission questions the editor answers. */
6
+ export interface AcpIo {
7
+ toolStart(id: string, tool: string, target: string): void;
8
+ toolEnd(id: string, output: string, isError: boolean): void;
9
+ askPermission(req: PermissionAsk): Promise<PermissionAnswer>;
10
+ onThinking?(delta: string): void;
11
+ }
12
+ /** The minimum an ACP client needs: run one prompt, stream text chunks, allow cancellation. */
13
+ export interface AcpAgent {
14
+ run(text: string, onChunk: (text: string) => void, signal: AbortSignal, io?: AcpIo): Promise<void>;
15
+ }
16
+ /**
17
+ * Agent Client Protocol over newline-delimited JSON-RPC (the transport editors like Zed use).
18
+ * Implements the subset: initialize, session/new, session/prompt (streaming session/update
19
+ * notifications: text chunks and tool calls), session/request_permission, session/cancel;
20
+ * anything else with an id gets -32601.
21
+ */
22
+ export declare function runAcp(input: NodeJS.ReadableStream, output: NodeJS.WritableStream, opts: {
23
+ cwd: string;
24
+ newAgent: (cwd: string) => AcpAgent;
25
+ }): Promise<void>;
26
+ /**
27
+ * A plan has no ACP primitive of its own, so it is asked through session/request_permission.
28
+ * 'yes' approves and keeps the normal per-tool prompts; 'session' also stops asking for edits
29
+ * for the rest of the session (acceptEdits). A plan answer never turns on allowAll, which stays
30
+ * an explicit user choice.
31
+ */
32
+ export declare function planDecisionFrom(answer: PermissionAnswer): PlanDecision;
33
+ export interface AcpRuntimeDeps {
34
+ /** Test seam: build the runtime without reading config/plugins. */
35
+ createRuntime?: (opts: {
36
+ cwd: string;
37
+ allowAll: boolean;
38
+ }) => Runtime;
39
+ }
40
+ /**
41
+ * Production agent: one bccli runtime per ACP session. Permission questions go to the editor
42
+ * (session/request_permission); `allowAll` skips them and runs everything unasked.
43
+ */
44
+ export declare function runtimeAgent(cwd: string, allowAll?: boolean, deps?: AcpRuntimeDeps): AcpAgent;
@@ -0,0 +1,158 @@
1
+ import type { PermissionRequest, Permissions } from './permissions';
2
+ import { type ChatMessage, type Provider, type Usage } from './provider';
3
+ import type { ReasoningLevel } from './reasoning';
4
+ import { type HookEvent, type HooksConfig } from './hooks';
5
+ import type { Tool } from './tools/types';
6
+ import { type UsageCap } from './budget';
7
+ export type AgentEvent = {
8
+ type: 'text';
9
+ delta: string;
10
+ } | {
11
+ type: 'thinking';
12
+ delta: string;
13
+ } | {
14
+ type: 'textReplace';
15
+ text: string;
16
+ } | {
17
+ type: 'toolStart';
18
+ id: string;
19
+ tool: string;
20
+ target: string;
21
+ } | {
22
+ type: 'toolEnd';
23
+ id: string;
24
+ tool: string;
25
+ output: string;
26
+ display?: string;
27
+ isError: boolean;
28
+ } | {
29
+ type: 'usage';
30
+ inputTokens: number;
31
+ outputTokens: number;
32
+ cachedInputTokens?: number;
33
+ cacheWriteTokens?: number;
34
+ /** Prompt tokens of the most recent model call: the size of the context, not the session total. */
35
+ lastInputTokens?: number;
36
+ /** Cache reads for the most recent model call, rather than the session total. */
37
+ lastCachedInputTokens?: number;
38
+ /** True when the provider omitted usage and token counts use the local chars/4 estimate. */
39
+ estimated?: boolean;
40
+ } | {
41
+ type: 'compacted';
42
+ } | {
43
+ type: 'stepLimit';
44
+ maxSteps: number;
45
+ } | {
46
+ type: 'budgetExceeded';
47
+ kind: 'tokens' | 'usd';
48
+ used: number;
49
+ limit: number;
50
+ } | {
51
+ type: 'aborted';
52
+ } | {
53
+ type: 'error';
54
+ message: string;
55
+ } | {
56
+ type: 'done';
57
+ } | {
58
+ type: 'subagent';
59
+ parentId: string;
60
+ agent: string;
61
+ event: AgentEvent;
62
+ };
63
+ export type PermissionAnswer = 'yes' | 'session' | 'all' | 'no';
64
+ export type PermissionAsk = PermissionRequest & {
65
+ preview?: string;
66
+ sessionRules?: string[];
67
+ agent?: string;
68
+ };
69
+ export type AskPermission = (req: PermissionAsk) => Promise<PermissionAnswer>;
70
+ export interface AgentOptions {
71
+ provider: Provider;
72
+ tools: Tool[];
73
+ permissions: Permissions;
74
+ systemPrompt: string;
75
+ cwd: string;
76
+ history?: ChatMessage[];
77
+ maxSteps?: number | null;
78
+ contextWindow?: number;
79
+ onMessage?: (message: ChatMessage) => void;
80
+ onReset?: () => void;
81
+ /** Per-turn token usage delta (provider-reported or estimated); the session persists it for stats. */
82
+ onUsage?: (usage: Usage) => void;
83
+ /** Marks the start of a user turn (undo boundary). */
84
+ onTurnStart?: () => void;
85
+ checkpoint?: (absPath: string) => Promise<void>;
86
+ /** Subagent name, shown on its permission prompts. */
87
+ label?: string;
88
+ reasoning?: ReasoningLevel;
89
+ /** Tool/session hooks (global config only). */
90
+ hooks?: HooksConfig;
91
+ /** In-process plugin observers fired alongside the shell hooks (global config plugins). */
92
+ pluginEmit?: (event: HookEvent, payload?: {
93
+ tool?: string;
94
+ input?: unknown;
95
+ output?: string;
96
+ }) => Promise<void>;
97
+ /** Base environment handed to hook processes (defaults to the process env at spawn). */
98
+ env?: NodeJS.ProcessEnv;
99
+ /** Per-session token/USD budget (global config usageCap); blocks model calls once exceeded. */
100
+ usageCap?: UsageCap;
101
+ /** Model ref used to match usageCap.prices keys. */
102
+ budgetModel?: string;
103
+ }
104
+ export declare class Agent {
105
+ private readonly opts;
106
+ provider: Provider;
107
+ permissions: Permissions;
108
+ messages: ChatMessage[];
109
+ totalUsage: Usage;
110
+ lastInputTokens: number;
111
+ lastInputEstimated: boolean;
112
+ onEvent: (event: AgentEvent) => void;
113
+ reasoning: ReasoningLevel;
114
+ askPermission: AskPermission;
115
+ tools: Tool[];
116
+ private definitions;
117
+ private turnTools;
118
+ private turnDefinitions;
119
+ readonly readFiles: Set<string>;
120
+ private readonly webBudget;
121
+ private readonly maxSteps?;
122
+ private contextWindow;
123
+ private budgetModel;
124
+ private systemPrompt;
125
+ constructor(opts: AgentOptions);
126
+ setSystemPrompt(text: string): void;
127
+ setContextWindow(window: number): void;
128
+ getContextWindow(): number;
129
+ setBudgetModel(model: string): void;
130
+ getBudgetModel(): string;
131
+ setTools(tools: Tool[]): void;
132
+ private isParallelSafe;
133
+ private push;
134
+ private estimateCurrentInputTokens;
135
+ private seedEstimatedContext;
136
+ /** Publish the latest context after prompt/history changes, without changing session usage totals. */
137
+ private publishContextMetadata;
138
+ /** Swaps in another conversation (resume, new session) without writing to the session log. */
139
+ load(messages: ChatMessage[]): void;
140
+ clear(): void;
141
+ compact(signal: AbortSignal): Promise<void>;
142
+ /**
143
+ * Events of one tool execution. A cancelled turn gives up on a tool that does not stop, and whatever that tool
144
+ * reports later must not reach the screen or a newer turn, so this is per execution and not per call id (ids repeat).
145
+ */
146
+ private report;
147
+ run(text: string, signal: AbortSignal): Promise<void>;
148
+ /** Current cap status; undefined means the budget is not exhausted. */
149
+ private overBudget;
150
+ /** Every tool call needs an answer in the history, or the next request to the model is rejected. */
151
+ private cancelCalls;
152
+ /**
153
+ * Every tool call must resolve with text: a rejected one would leave the assistant tool_calls
154
+ * without answers, and the next request to the model would be rejected until a restart.
155
+ */
156
+ private runTool;
157
+ private executeTool;
158
+ }
@@ -0,0 +1,4 @@
1
+ import type { AgentDef } from './extensions';
2
+ export declare const EXPLORE_PROMPT = "You are a read-only research agent working for another agent. Search and read the codebase to answer the request. You cannot edit files or run commands. Reply with a concise report: the answer, the relevant file paths with line numbers, and nothing else.";
3
+ export declare const GENERAL_PROMPT = "You are an agent handling a delegated task for another agent. Complete the task end to end with the tools you have, verify the result, and reply with a short report of what you did and what you found.";
4
+ export declare const BUILTIN_AGENTS: AgentDef[];