@shuyhere/bb-agent 0.0.8 → 0.0.10

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.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,41 @@ All notable changes to BB-Agent will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.0.10] - 2026-04-07
9
+
10
+ ### Fixed
11
+
12
+ - npm install now uses a longer timeout, retries release-binary downloads, and reports real download errors instead of incorrectly saying no matching prebuilt binary exists
13
+ - npm install now shows progress logs during native binary download and verification so first-time installs on macOS/Linux are less confusing
14
+ - fullscreen `/login` provider-family status now correctly shows OpenAI OAuth state after ChatGPT login instead of incorrectly showing the API key path as not authenticated
15
+
16
+ ### Changed
17
+
18
+ - README install docs now lead with `npm install -g @shuyhere/bb-agent`, move terminal/font guidance into Troubleshooting, and clearly separate npm install from building from source for development
19
+
20
+ ## [0.0.9] - 2026-04-07
21
+
22
+ ### Added
23
+
24
+ - `@folder/` expansion now sends a directory tree summary to the model instead of treating folders like text files
25
+ - large `@file` expansions now send a structural outline first for long files instead of dumping the entire file immediately
26
+ - non-UTF-8 and binary `@file` references now send metadata instead of a misleading UTF-8 read error
27
+
28
+ ### Fixed
29
+
30
+ - fullscreen paste in iTerm2/SSH no longer corrupts the input area after paste
31
+ - pasted file and image paths are normalized more reliably, including quoted paths and `file://` URLs
32
+ - fullscreen prompt submission now expands `@file` references consistently
33
+ - running tool timers continue updating after `TurnEnd` while tools are still executing
34
+ - sub-second tool durations now display as `ms` instead of `0.0s`
35
+ - startup Skills/Prompts/Extensions note now only appears at startup or explicit `/reload`
36
+ - remote SSH clipboard copy no longer leaks Wayland/XDG clipboard helper warnings into the TUI
37
+
38
+ ### Changed
39
+
40
+ - fullscreen `Ctrl+V` now falls back to clipboard text when no clipboard image is available
41
+ - `@` autocomplete now inserts quoted file references when paths contain spaces
42
+
8
43
  ## [0.0.8] - 2026-04-06
9
44
 
10
45
  ### Fixed
@@ -23,4 +58,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
23
58
 
24
59
  - latest published package includes the post-0.0.7 startup, auth, model-default, and update-notice improvements
25
60
 
61
+ [0.0.10]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.10
62
+ [0.0.9]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.9
26
63
  [0.0.8]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.8
package/README.md CHANGED
@@ -1,10 +1,37 @@
1
1
  # BB-Agent
2
2
 
3
+ ![BB-Agent title figure](assets/title-figure.png)
4
+
5
+ > BB means Bridge Baby in Death Stranding. I named this project that way because while building it, I was also enjoying Death Stranding and loved the idea of connecting everyone together.
6
+
3
7
  A Rust-native AI coding agent for the terminal — featuring a fullscreen TUI, multi-provider support, tool use, session persistence, branching, extensions, and skills.
4
8
 
5
9
  ## Install
6
10
 
7
- ### From source (all platforms — macOS, Linux, Windows)
11
+ ```bash
12
+ npm install -g @shuyhere/bb-agent
13
+ ```
14
+
15
+ ### 1. Install with npm
16
+
17
+ npm install downloads a small wrapper package first, then fetches the matching native BB-Agent binary from the GitHub release for your platform.
18
+
19
+ What to expect:
20
+ - first install can take a bit because npm downloads and verifies the native binary
21
+ - the installer now prints progress and retry information while downloading
22
+ - after install, run `bb`
23
+
24
+ Current GitHub release binaries are published for:
25
+ - Linux x86_64
26
+ - macOS x86_64
27
+ - macOS arm64 (Apple Silicon)
28
+ - Windows x86_64
29
+
30
+ If no matching prebuilt binary is available, or the download fails, npm install will print source-build instructions instead.
31
+
32
+ ### 2. Build from source for development
33
+
34
+ Use this if you want to develop BB-Agent itself, work on the Rust codebase, or install directly without the npm downloader.
8
35
 
9
36
  Requires [Rust](https://rustup.rs). Install Rust first if you don't have it:
10
37
 
@@ -13,7 +40,7 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
13
40
  source ~/.cargo/env
14
41
  ```
15
42
 
16
- Then build and install BB-Agent:
43
+ Then build and install BB-Agent from source:
17
44
 
18
45
  ```bash
19
46
  git clone https://github.com/shuyhere/bb-agent.git
@@ -23,16 +50,6 @@ cargo install --path crates/cli
23
50
 
24
51
  This compiles the `bb` binary and installs it to `~/.cargo/bin/bb` (which Rust adds to your PATH).
25
52
 
26
- ### npm (Linux/macOS/Windows — downloads matching prebuilt binary when available)
27
-
28
- ```bash
29
- npm install -g @shuyhere/bb-agent
30
- ```
31
-
32
- > If no matching prebuilt binary is available for your platform, npm install will print source-build instructions instead. After install, run `bb` to start.
33
- >
34
- > Current GitHub release binaries are published for Linux x86_64, macOS x86_64/arm64, and Windows x86_64.
35
-
36
53
  ## Getting Started
37
54
 
38
55
  ### 1. Start the TUI
@@ -170,6 +187,38 @@ BB-Agent uses layered configuration:
170
187
  | `bb-tui` | Terminal UI components and fullscreen experience |
171
188
  | `bb-cli` | The `bb` command-line application |
172
189
 
190
+ ## Troubleshooting
191
+
192
+ ### Terminal & Font Compatibility
193
+
194
+ BB-Agent uses Unicode glyphs and ANSI color in the fullscreen TUI. For the best visual experience, use a modern terminal and a Unicode-capable monospace font such as:
195
+
196
+ - JetBrains Mono
197
+ - SF Mono / Menlo
198
+ - Fira Code
199
+ - Cascadia Mono
200
+ - Nerd Font variants of the above
201
+
202
+ If some symbols look broken, missing, or too narrow in your terminal:
203
+
204
+ 1. switch to a Unicode-capable monospace font
205
+ 2. make sure your terminal uses UTF-8
206
+ 3. enable BB-Agent compatibility mode
207
+
208
+ Compatibility mode uses safer ASCII-style fallback glyphs for spinner/status/tool markers:
209
+
210
+ ```bash
211
+ BB_TUI_COMPAT=1 bb
212
+ ```
213
+
214
+ Or set this in `~/.bb-agent/settings.json`:
215
+
216
+ ```json
217
+ {
218
+ "compatibility_mode": true
219
+ }
220
+ ```
221
+
173
222
  ## Documentation
174
223
 
175
224
  - [Configuration Reference](docs/configuration.md) — settings.json, AGENTS.md, templates
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shuyhere/bb-agent",
3
- "version": "0.0.8",
3
+ "version": "0.0.10",
4
4
  "description": "BB-Agent — a Rust-native AI coding agent for the terminal",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -7,13 +7,15 @@ const fs = require("fs");
7
7
  const path = require("path");
8
8
  const os = require("os");
9
9
  const https = require("https");
10
+ const http = require("http");
10
11
 
11
12
  const packageJson = require("../package.json");
12
13
  const BINARY_RELEASE_TAG = `v${packageJson.version}`;
13
14
  const REPO = "shuyhere/bb-agent";
14
- const PACKAGE_ROOT = path.resolve(__dirname, "..");
15
15
  const NATIVE_DIR = path.join(__dirname, "..", "native");
16
- const DOWNLOAD_TIMEOUT_MS = 15_000;
16
+ const DOWNLOAD_TIMEOUT_MS = 120_000;
17
+ const MAX_REDIRECTS = 8;
18
+ const MAX_DOWNLOAD_ATTEMPTS = 3;
17
19
 
18
20
  function isWindows() {
19
21
  return os.platform() === "win32";
@@ -44,34 +46,6 @@ function getTarget() {
44
46
  return `${a}-${p}`;
45
47
  }
46
48
 
47
- function downloadBinary(url, dest, timeoutMs) {
48
- return new Promise((resolve, reject) => {
49
- const timer = setTimeout(() => reject(new Error("Download timed out")), timeoutMs);
50
-
51
- const follow = (url, redirects = 0) => {
52
- if (redirects > 5) { clearTimeout(timer); return reject(new Error("Too many redirects")); }
53
-
54
- const mod = url.startsWith("https") ? https : require("http");
55
- const req = mod.get(url, (res) => {
56
- if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
57
- return follow(res.headers.location, redirects + 1);
58
- }
59
- if (res.statusCode !== 200) {
60
- clearTimeout(timer);
61
- return reject(new Error(`HTTP ${res.statusCode}`));
62
- }
63
- const file = fs.createWriteStream(dest);
64
- res.pipe(file);
65
- file.on("finish", () => { clearTimeout(timer); file.close(); resolve(); });
66
- file.on("error", (e) => { clearTimeout(timer); reject(e); });
67
- });
68
- req.on("error", (e) => { clearTimeout(timer); reject(e); });
69
- req.on("timeout", () => { req.destroy(); clearTimeout(timer); reject(new Error("Request timed out")); });
70
- };
71
- follow(url);
72
- });
73
- }
74
-
75
49
  function assetNameForTarget(target) {
76
50
  return isWindows() ? `bb-${target}.exe` : `bb-${target}`;
77
51
  }
@@ -87,60 +61,232 @@ function hasBundledNativeBinary() {
87
61
  }
88
62
  }
89
63
 
90
- async function tryDownloadPrebuilt(target) {
91
- const assetName = assetNameForTarget(target);
92
- const url = `https://github.com/${REPO}/releases/download/${BINARY_RELEASE_TAG}/${assetName}`;
93
-
94
- fs.mkdirSync(NATIVE_DIR, { recursive: true });
95
- const dest = nativeBinaryPath();
64
+ function makeDownloadError(kind, message, statusCode) {
65
+ const err = new Error(message);
66
+ err.kind = kind;
67
+ if (statusCode) err.statusCode = statusCode;
68
+ return err;
69
+ }
96
70
 
97
- try {
98
- console.log(`Downloading BB-Agent ${BINARY_RELEASE_TAG} for ${target}...`);
99
- await downloadBinary(url, dest, DOWNLOAD_TIMEOUT_MS);
100
- fs.chmodSync(dest, 0o755);
71
+ function formatBytes(bytes) {
72
+ if (!Number.isFinite(bytes) || bytes <= 0) return "0 B";
73
+ const units = ["B", "KB", "MB", "GB"];
74
+ let value = bytes;
75
+ let unit = 0;
76
+ while (value >= 1024 && unit < units.length - 1) {
77
+ value /= 1024;
78
+ unit += 1;
79
+ }
80
+ return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`;
81
+ }
101
82
 
102
- // Verify the binary is executable
103
- try {
104
- execSync(`"${dest}" --version`, { stdio: "pipe", timeout: 5000 });
105
- } catch {
106
- // Binary may not run on this platform (e.g. wrong arch) — remove it
107
- fs.unlinkSync(dest);
108
- return false;
83
+ function requestBinary(url, dest, redirects = 0) {
84
+ return new Promise((resolve, reject) => {
85
+ if (redirects > MAX_REDIRECTS) {
86
+ reject(makeDownloadError("redirect", "Too many redirects"));
87
+ return;
109
88
  }
110
89
 
111
- console.log(" BB-Agent binary installed successfully.");
112
- return true;
90
+ const client = url.startsWith("https:") ? https : http;
91
+ const req = client.get(
92
+ url,
93
+ {
94
+ headers: {
95
+ "User-Agent": `${packageJson.name}/${packageJson.version} (postinstall)`,
96
+ Accept: "application/octet-stream,application/octet-stream; q=0.9,*/*;q=0.1",
97
+ },
98
+ },
99
+ (res) => {
100
+ const status = res.statusCode || 0;
101
+
102
+ if (status >= 300 && status < 400 && res.headers.location) {
103
+ res.resume();
104
+ requestBinary(res.headers.location, dest, redirects + 1)
105
+ .then(resolve)
106
+ .catch(reject);
107
+ return;
108
+ }
109
+
110
+ if (status === 404) {
111
+ res.resume();
112
+ reject(makeDownloadError("not-found", `HTTP 404 for ${url}`, 404));
113
+ return;
114
+ }
115
+
116
+ if (status !== 200) {
117
+ res.resume();
118
+ reject(makeDownloadError("http", `HTTP ${status} for ${url}`, status));
119
+ return;
120
+ }
121
+
122
+ const totalBytes = Number(res.headers["content-length"] || 0);
123
+ let downloadedBytes = 0;
124
+ let lastLoggedAt = Date.now();
125
+ if (totalBytes > 0) {
126
+ console.log(`Release asset size: ${formatBytes(totalBytes)}.`);
127
+ } else {
128
+ console.log("Release asset size: unknown (streaming download).");
129
+ }
130
+
131
+ const file = fs.createWriteStream(dest);
132
+ let settled = false;
133
+
134
+ const finish = (fn, value) => {
135
+ if (settled) return;
136
+ settled = true;
137
+ clearTimeout(timeout);
138
+ fn(value);
139
+ };
140
+
141
+ res.on("data", (chunk) => {
142
+ downloadedBytes += chunk.length;
143
+ const now = Date.now();
144
+ if (now - lastLoggedAt >= 5000) {
145
+ lastLoggedAt = now;
146
+ if (totalBytes > 0) {
147
+ const percent = Math.min(100, Math.round((downloadedBytes / totalBytes) * 100));
148
+ console.log(
149
+ `Download progress: ${percent}% (${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)})`
150
+ );
151
+ } else {
152
+ console.log(`Downloaded ${formatBytes(downloadedBytes)} so far...`);
153
+ }
154
+ }
155
+ });
156
+
157
+ file.on("finish", () => {
158
+ file.close((closeErr) => {
159
+ if (closeErr) {
160
+ finish(reject, makeDownloadError("write", closeErr.message));
161
+ } else {
162
+ if (totalBytes > 0) {
163
+ console.log(
164
+ `Download complete: ${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)}.`
165
+ );
166
+ } else {
167
+ console.log(`Download complete: ${formatBytes(downloadedBytes)}.`);
168
+ }
169
+ finish(resolve);
170
+ }
171
+ });
172
+ });
173
+
174
+ file.on("error", (err) => {
175
+ try { file.close(() => {}); } catch {}
176
+ try { fs.unlinkSync(dest); } catch {}
177
+ finish(reject, makeDownloadError("write", err.message));
178
+ });
179
+
180
+ res.on("error", (err) => {
181
+ try { file.close(() => {}); } catch {}
182
+ try { fs.unlinkSync(dest); } catch {}
183
+ finish(reject, makeDownloadError("network", err.message));
184
+ });
185
+
186
+ res.pipe(file);
187
+ }
188
+ );
189
+
190
+ const timeout = setTimeout(() => {
191
+ req.destroy(makeDownloadError("timeout", `Download timed out after ${DOWNLOAD_TIMEOUT_MS}ms`));
192
+ }, DOWNLOAD_TIMEOUT_MS);
193
+
194
+ req.on("error", (err) => {
195
+ clearTimeout(timeout);
196
+ reject(makeDownloadError(err.kind || "network", err.message));
197
+ });
198
+ });
199
+ }
200
+
201
+ function verifyBinary(dest) {
202
+ try {
203
+ execSync(`"${dest}" --version`, { stdio: "pipe", timeout: 5000 });
204
+ return { ok: true };
113
205
  } catch (err) {
114
- // Clean up partial download
115
- try { fs.unlinkSync(dest); } catch {}
116
- return false;
206
+ return {
207
+ ok: false,
208
+ message: err && err.message ? err.message : "binary verification failed",
209
+ };
117
210
  }
118
211
  }
119
212
 
213
+ async function tryDownloadPrebuilt(target) {
214
+ const assetName = assetNameForTarget(target);
215
+ const url = `https://github.com/${REPO}/releases/download/${BINARY_RELEASE_TAG}/${assetName}`;
120
216
 
121
- async function main() {
122
- if (process.env.BB_SKIP_POSTINSTALL) {
123
- return;
124
- }
217
+ fs.mkdirSync(NATIVE_DIR, { recursive: true });
218
+ const dest = nativeBinaryPath();
125
219
 
126
- if (hasBundledNativeBinary()) return;
220
+ let lastError = null;
221
+ for (let attempt = 1; attempt <= MAX_DOWNLOAD_ATTEMPTS; attempt += 1) {
222
+ try {
223
+ console.log(
224
+ `Downloading BB-Agent ${BINARY_RELEASE_TAG} for ${target} (attempt ${attempt}/${MAX_DOWNLOAD_ATTEMPTS})...`
225
+ );
226
+ console.log("This may take a little while on first install because npm downloads and verifies the native binary from the GitHub release.");
227
+ try { fs.unlinkSync(dest); } catch {}
228
+ await requestBinary(url, dest, 0);
229
+ fs.chmodSync(dest, 0o755);
127
230
 
128
- const target = getTarget();
231
+ const verified = verifyBinary(dest);
232
+ if (!verified.ok) {
233
+ try { fs.unlinkSync(dest); } catch {}
234
+ return {
235
+ ok: false,
236
+ kind: "verify",
237
+ message: `Downloaded binary could not run: ${verified.message}`,
238
+ };
239
+ }
129
240
 
130
- // Try prebuilt binary
131
- if (target) {
132
- const ok = await tryDownloadPrebuilt(target);
133
- if (ok) return;
241
+ console.log("Verifying downloaded binary...");
242
+ console.log("✓ BB-Agent binary installed successfully.");
243
+ return { ok: true };
244
+ } catch (err) {
245
+ lastError = err;
246
+ try { fs.unlinkSync(dest); } catch {}
247
+ if (err.kind === "not-found") {
248
+ return {
249
+ ok: false,
250
+ kind: "not-found",
251
+ message: `No release asset named ${assetName} was found for ${BINARY_RELEASE_TAG}.`,
252
+ };
253
+ }
254
+ if (attempt < MAX_DOWNLOAD_ATTEMPTS) {
255
+ await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
256
+ }
257
+ }
134
258
  }
135
259
 
136
- // No prebuilt available — print instructions instead of trying cargo build
137
- // (cargo build takes 5+ minutes and would appear to hang)
138
- const platform = `${os.platform()}-${os.arch()}`;
260
+ return {
261
+ ok: false,
262
+ kind: (lastError && lastError.kind) || "download",
263
+ message: (lastError && lastError.message) || "unknown download failure",
264
+ };
265
+ }
266
+
267
+ function printFallbackHelp(platform, reason) {
139
268
  console.log("");
140
- console.log(`BB-Agent ${packageJson.version}: matching prebuilt binary not available yet for ${platform}.`);
269
+ if (reason && reason.kind === "not-found") {
270
+ console.log(
271
+ `BB-Agent ${packageJson.version}: matching prebuilt binary is not published for ${platform}.`
272
+ );
273
+ } else if (reason) {
274
+ console.log(
275
+ `BB-Agent ${packageJson.version}: failed to download the prebuilt binary for ${platform}.`
276
+ );
277
+ console.log(`Reason: ${reason.message}`);
278
+ } else {
279
+ console.log(
280
+ `BB-Agent ${packageJson.version}: matching prebuilt binary not available yet for ${platform}.`
281
+ );
282
+ }
141
283
  console.log("");
142
284
  console.log("╔══════════════════════════════════════════════════════════════╗");
143
- console.log("║ BB-Agent: no prebuilt binary for " + platform.padEnd(19) + " ║");
285
+ console.log(
286
+ "║ BB-Agent: npm could not install native binary for " +
287
+ platform.padEnd(16) +
288
+ " ║"
289
+ );
144
290
  console.log("║ ║");
145
291
  console.log("║ Install Rust (if needed): ║");
146
292
  console.log("║ https://rustup.rs ║");
@@ -155,8 +301,40 @@ async function main() {
155
301
  console.log("");
156
302
  }
157
303
 
158
- main().catch((err) => {
159
- // Never fail npm install — just print instructions
160
- console.error("BB-Agent postinstall notice:", err.message);
161
- console.log("Install manually: git clone https://github.com/shuyhere/bb-agent.git && cd bb-agent && cargo install --path crates/cli");
162
- });
304
+ async function main() {
305
+ if (process.env.BB_SKIP_POSTINSTALL) {
306
+ return;
307
+ }
308
+
309
+ if (hasBundledNativeBinary()) {
310
+ return;
311
+ }
312
+
313
+ const target = getTarget();
314
+ const platform = `${os.platform()}-${os.arch()}`;
315
+
316
+ if (target) {
317
+ const result = await tryDownloadPrebuilt(target);
318
+ if (result.ok) {
319
+ return;
320
+ }
321
+ printFallbackHelp(platform, result);
322
+ return;
323
+ }
324
+
325
+ printFallbackHelp(platform, {
326
+ kind: "unsupported-platform",
327
+ message: `Unsupported target mapping for ${platform}`,
328
+ });
329
+ }
330
+
331
+ main()
332
+ .catch((err) => {
333
+ console.error("BB-Agent postinstall notice:", err && err.message ? err.message : String(err));
334
+ console.log(
335
+ "Install manually: git clone https://github.com/shuyhere/bb-agent.git && cd bb-agent && cargo install --path crates/cli"
336
+ );
337
+ })
338
+ .finally(() => {
339
+ process.exitCode = 0;
340
+ });