@shuyhere/bb-agent 0.0.10 → 0.0.13

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,55 @@ 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.13] - 2026-04-09
9
+
10
+ ### Added
11
+
12
+ - fullscreen screenshot and image clipboard paste now works on the normal paste path, with macOS clipboard fallbacks and Codex image preservation so pasted images reach image-capable models correctly
13
+ - model registry metadata now tracks image input capability, making `/models` truthful about image support and allowing runtime warnings when users attach images to text-only models
14
+
15
+ ### Fixed
16
+
17
+ - fullscreen clipboard image attach no longer leaks helper `true` / `false` output or stray follow-up paste text into the input block
18
+ - attached image chips can now be removed with `Backspace`, image-only prompts can be submitted, and optimistic user messages keep attachment chip previews in the transcript
19
+ - rebuilt fullscreen session transcripts now preserve user image attachment markers instead of silently dropping image blocks
20
+ - managed `bb-clipboard-*.png` temp files are now cleaned up after removal or ingestion instead of lingering in `/tmp`
21
+ - the fullscreen input block now hides raw `@file` tokens when the corresponding attachment chip is already shown, preventing duplicated `@file` text in the editor
22
+ - fullscreen tool-header regression tests now match the intended live bash-header rendering and running-dot animation behavior
23
+
24
+ ## [0.0.12] - 2026-04-06
25
+
26
+ ### Fixed
27
+
28
+ - direct `@image` references in print mode and fullscreen now attach real image inputs instead of falling back to UTF-8 read warnings
29
+ - `@path with spaces` parsing now correctly keeps the full file path before trailing prompt text, including whole-message forms
30
+ - image tool results are now preserved through provider conversion so models can actually see images returned by tools instead of responding as if no image was provided
31
+ - fullscreen `@` folder navigation now keeps the completion menu open when you select a directory and immediately shows the next level, including directories with spaces
32
+ - the fullscreen input block now shows attached files as `[name, sizeKB]`, keeps those chips visible, and places the cursor below them so typing starts after the attachments
33
+
34
+ ### Changed
35
+
36
+ - binary office/document inputs (`pdf`, `docx`, `pptx`, `xlsx`) now degrade to format-aware metadata notes instead of misleading invalid-UTF-8 errors
37
+
38
+ ## [0.0.11] - 2026-04-07
39
+
40
+ ### Added
41
+
42
+ - startup update notices in the fullscreen transcript are now highlighted so available updates stand out clearly during startup
43
+ - read-tool line ranges in fullscreen tool activity now highlight the requested span, so values like `2148-2267/5006` stand out while the model is using tools
44
+ - fullscreen footer and `/session` info now show the active execution posture so safety vs yolo is visible during a run
45
+
46
+ ### Improved
47
+
48
+ - npm install now caches verified native binaries by version/platform and reuses them on reinstall instead of re-downloading every time
49
+ - npm install now shows more frequent download progress with transfer rate information to make slow installs easier to understand
50
+ - npm install now avoids unnecessary re-verification on cache hits, making repeat installs faster
51
+ - safety mode now restricts built-in `write` and `edit` to the active workspace, while `yolo` keeps unrestricted file mutation behavior
52
+
53
+ ### Migration
54
+
55
+ - `execution_mode` now defaults to `safety`; set `"execution_mode": "yolo"` if your workflow intentionally edits files outside the current workspace
56
+
8
57
  ## [0.0.10] - 2026-04-07
9
58
 
10
59
  ### Fixed
@@ -58,6 +107,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
58
107
 
59
108
  - latest published package includes the post-0.0.7 startup, auth, model-default, and update-notice improvements
60
109
 
110
+ [0.0.13]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.13
111
+ [0.0.12]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.12
112
+ [0.0.11]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.11
61
113
  [0.0.10]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.10
62
114
  [0.0.9]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.9
63
115
  [0.0.8]: https://github.com/shuyhere/bb-agent/releases/tag/v0.0.8
package/README.md CHANGED
@@ -101,6 +101,7 @@ bb --list-models # List all available models
101
101
  - **Fullscreen TUI** — rich terminal interface with streaming output, markdown rendering, syntax highlighting
102
102
  - **Multi-provider** — Anthropic (Claude), OpenAI, Google (Gemini), Groq, xAI, OpenRouter, and custom OpenAI-compatible endpoints
103
103
  - **Built-in tools** — `read`, `write`, `edit`, `bash`, `find`, `grep`, `ls`, `web_search`, `web_fetch`, `browser_fetch`
104
+ - **Safety and yolo execution modes** — default safety posture restricts built-in `write` and `edit` to the active workspace; yolo removes that guard
104
105
  - **Session persistence** — SQLite-backed sessions with branching, forking, and tree navigation
105
106
  - **Extensions** — JS/TS plugin system for custom tools, commands, and hooks
106
107
  - **Skills** — markdown-based instruction files that auto-load contextual knowledge
@@ -144,9 +145,11 @@ BB-Agent uses layered configuration:
144
145
 
145
146
  ```json
146
147
  {
148
+ "execution_mode": "safety",
147
149
  "default_model": "claude-sonnet-4-20250514",
148
150
  "default_provider": "anthropic",
149
151
  "default_thinking": "medium",
152
+ "execution_mode": "safety",
150
153
  "models": [
151
154
  {
152
155
  "id": "my-local-model",
@@ -160,6 +163,21 @@ BB-Agent uses layered configuration:
160
163
  }
161
164
  ```
162
165
 
166
+ ### Execution Modes
167
+
168
+ BB-Agent exposes the active permission posture in fullscreen and `/session`.
169
+
170
+ - `safety` is the default. Built-in `write` and `edit` stay inside the current workspace, and bash commands use the safer approval/sandboxed posture.
171
+ - `yolo` is the opt-in less-restrictive mode.
172
+
173
+ Example:
174
+
175
+ ```json
176
+ {
177
+ "execution_mode": "yolo"
178
+ }
179
+ ```
180
+
163
181
  ## Keyboard Shortcuts
164
182
 
165
183
  | Key | Action |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shuyhere/bb-agent",
3
- "version": "0.0.10",
3
+ "version": "0.0.13",
4
4
  "description": "BB-Agent — a Rust-native AI coding agent for the terminal",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -2,7 +2,7 @@
2
2
 
3
3
  "use strict";
4
4
 
5
- const { execSync } = require("child_process");
5
+ const { execFileSync } = require("child_process");
6
6
  const fs = require("fs");
7
7
  const path = require("path");
8
8
  const os = require("os");
@@ -14,6 +14,7 @@ const BINARY_RELEASE_TAG = `v${packageJson.version}`;
14
14
  const REPO = "shuyhere/bb-agent";
15
15
  const NATIVE_DIR = path.join(__dirname, "..", "native");
16
16
  const DOWNLOAD_TIMEOUT_MS = 120_000;
17
+ const DOWNLOAD_PROGRESS_INTERVAL_MS = 1_000;
17
18
  const MAX_REDIRECTS = 8;
18
19
  const MAX_DOWNLOAD_ATTEMPTS = 3;
19
20
 
@@ -50,15 +51,10 @@ function assetNameForTarget(target) {
50
51
  return isWindows() ? `bb-${target}.exe` : `bb-${target}`;
51
52
  }
52
53
 
53
- function hasBundledNativeBinary() {
54
- const dest = nativeBinaryPath();
55
- if (!fs.existsSync(dest)) return false;
54
+ function logLine(message = "") {
56
55
  try {
57
- execSync(`"${dest}" --version`, { stdio: "pipe", timeout: 5000 });
58
- return true;
59
- } catch {
60
- return false;
61
- }
56
+ process.stderr.write(`${message}\n`);
57
+ } catch (_) {}
62
58
  }
63
59
 
64
60
  function makeDownloadError(kind, message, statusCode) {
@@ -80,6 +76,172 @@ function formatBytes(bytes) {
80
76
  return `${value.toFixed(value >= 10 || unit === 0 ? 0 : 1)} ${units[unit]}`;
81
77
  }
82
78
 
79
+ function formatRate(bytesPerSecond) {
80
+ if (!Number.isFinite(bytesPerSecond) || bytesPerSecond <= 0) return "0 B/s";
81
+ return `${formatBytes(bytesPerSecond)}/s`;
82
+ }
83
+
84
+ function ensureParentDir(filePath) {
85
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
86
+ }
87
+
88
+ function removeIfExists(filePath) {
89
+ try {
90
+ fs.unlinkSync(filePath);
91
+ } catch (_) {}
92
+ }
93
+
94
+ function binaryVersion(binaryPath) {
95
+ try {
96
+ const out = execFileSync(binaryPath, ["--version"], {
97
+ stdio: ["ignore", "pipe", "pipe"],
98
+ timeout: 2500,
99
+ encoding: "utf8",
100
+ });
101
+ return (out || "").trim();
102
+ } catch (err) {
103
+ return null;
104
+ }
105
+ }
106
+
107
+ function binaryMatchesCurrentVersion(binaryPath) {
108
+ const version = binaryVersion(binaryPath);
109
+ if (!version) return false;
110
+ return version.includes(packageJson.version);
111
+ }
112
+
113
+ function hasBundledNativeBinary() {
114
+ const dest = nativeBinaryPath();
115
+ if (!fs.existsSync(dest)) return false;
116
+ if (!binaryMatchesCurrentVersion(dest)) return false;
117
+ try {
118
+ fs.accessSync(dest, fs.constants.X_OK);
119
+ return true;
120
+ } catch {
121
+ return false;
122
+ }
123
+ }
124
+
125
+ function cacheRootDir() {
126
+ if (process.env.BB_INSTALL_CACHE_DIR && process.env.BB_INSTALL_CACHE_DIR.trim()) {
127
+ return process.env.BB_INSTALL_CACHE_DIR;
128
+ }
129
+
130
+ const home = os.homedir();
131
+ if (isWindows()) {
132
+ return path.join(
133
+ process.env.LOCALAPPDATA || process.env.APPDATA || path.join(home, "AppData", "Local"),
134
+ "bb-agent"
135
+ );
136
+ }
137
+ if (os.platform() === "darwin") {
138
+ return path.join(home, "Library", "Caches", "bb-agent");
139
+ }
140
+ return path.join(process.env.XDG_CACHE_HOME || path.join(home, ".cache"), "bb-agent");
141
+ }
142
+
143
+ function cacheBinaryPath(target) {
144
+ return path.join(cacheRootDir(), "prebuilt", packageJson.version, assetNameForTarget(target));
145
+ }
146
+
147
+ function cacheMetadataPath(target) {
148
+ return `${cacheBinaryPath(target)}.json`;
149
+ }
150
+
151
+ function loadCacheMetadata(target) {
152
+ try {
153
+ return JSON.parse(fs.readFileSync(cacheMetadataPath(target), "utf8"));
154
+ } catch (_) {
155
+ return null;
156
+ }
157
+ }
158
+
159
+ function storeCacheMetadata(target, binaryPath) {
160
+ try {
161
+ const stat = fs.statSync(binaryPath);
162
+ ensureParentDir(cacheMetadataPath(target));
163
+ fs.writeFileSync(
164
+ cacheMetadataPath(target),
165
+ JSON.stringify(
166
+ {
167
+ version: packageJson.version,
168
+ target,
169
+ assetName: assetNameForTarget(target),
170
+ binaryName: nativeBinaryName(),
171
+ size: stat.size,
172
+ verifiedAt: new Date().toISOString(),
173
+ },
174
+ null,
175
+ 2
176
+ )
177
+ );
178
+ } catch (_) {}
179
+ }
180
+
181
+ function copyBinary(src, dest) {
182
+ ensureParentDir(dest);
183
+ fs.copyFileSync(src, dest);
184
+ if (!isWindows()) {
185
+ fs.chmodSync(dest, 0o755);
186
+ }
187
+ }
188
+
189
+ function installFromVerifiedCache(target) {
190
+ const cached = cacheBinaryPath(target);
191
+ const meta = loadCacheMetadata(target);
192
+ if (!fs.existsSync(cached) || !meta) return false;
193
+ if (meta.version !== packageJson.version || meta.target !== target) return false;
194
+
195
+ let stat;
196
+ try {
197
+ stat = fs.statSync(cached);
198
+ } catch (_) {
199
+ return false;
200
+ }
201
+ if (!stat.isFile() || stat.size <= 0) return false;
202
+ if (meta.size && stat.size !== meta.size) return false;
203
+
204
+ logLine(`Using cached BB-Agent binary for ${target} (${formatBytes(stat.size)}).`);
205
+ copyBinary(cached, nativeBinaryPath());
206
+ return true;
207
+ }
208
+
209
+ function refreshCacheFromExistingBinary(target, sourcePath) {
210
+ if (!binaryMatchesCurrentVersion(sourcePath)) return false;
211
+ const cached = cacheBinaryPath(target);
212
+ copyBinary(sourcePath, cached);
213
+ storeCacheMetadata(target, cached);
214
+ return true;
215
+ }
216
+
217
+ function maybeRepairCache(target) {
218
+ const cached = cacheBinaryPath(target);
219
+ if (!fs.existsSync(cached)) return false;
220
+
221
+ const meta = loadCacheMetadata(target);
222
+ if (meta && meta.version === packageJson.version && meta.target === target && meta.size) {
223
+ try {
224
+ const stat = fs.statSync(cached);
225
+ if (stat.isFile() && stat.size === meta.size) {
226
+ return false;
227
+ }
228
+ } catch (_) {
229
+ return false;
230
+ }
231
+ }
232
+
233
+ logLine(`Checking cached BB-Agent binary for ${target}...`);
234
+ if (!binaryMatchesCurrentVersion(cached)) {
235
+ removeIfExists(cached);
236
+ removeIfExists(cacheMetadataPath(target));
237
+ return false;
238
+ }
239
+
240
+ storeCacheMetadata(target, cached);
241
+ logLine("Verified cached BB-Agent binary for reuse.");
242
+ return true;
243
+ }
244
+
83
245
  function requestBinary(url, dest, redirects = 0) {
84
246
  return new Promise((resolve, reject) => {
85
247
  if (redirects > MAX_REDIRECTS) {
@@ -120,14 +282,17 @@ function requestBinary(url, dest, redirects = 0) {
120
282
  }
121
283
 
122
284
  const totalBytes = Number(res.headers["content-length"] || 0);
285
+ const startedAt = Date.now();
123
286
  let downloadedBytes = 0;
124
- let lastLoggedAt = Date.now();
287
+ let lastLoggedAt = 0;
288
+
125
289
  if (totalBytes > 0) {
126
- console.log(`Release asset size: ${formatBytes(totalBytes)}.`);
290
+ logLine(`Release asset size: ${formatBytes(totalBytes)}.`);
127
291
  } else {
128
- console.log("Release asset size: unknown (streaming download).");
292
+ logLine("Release asset size: unknown (streaming download).");
129
293
  }
130
294
 
295
+ ensureParentDir(dest);
131
296
  const file = fs.createWriteStream(dest);
132
297
  let settled = false;
133
298
 
@@ -141,15 +306,17 @@ function requestBinary(url, dest, redirects = 0) {
141
306
  res.on("data", (chunk) => {
142
307
  downloadedBytes += chunk.length;
143
308
  const now = Date.now();
144
- if (now - lastLoggedAt >= 5000) {
309
+ if (now - lastLoggedAt >= DOWNLOAD_PROGRESS_INTERVAL_MS) {
145
310
  lastLoggedAt = now;
311
+ const elapsedSeconds = Math.max((now - startedAt) / 1000, 0.001);
312
+ const rate = downloadedBytes / elapsedSeconds;
146
313
  if (totalBytes > 0) {
147
314
  const percent = Math.min(100, Math.round((downloadedBytes / totalBytes) * 100));
148
- console.log(
149
- `Download progress: ${percent}% (${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)})`
315
+ logLine(
316
+ `Download progress: ${percent}% (${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)}, ${formatRate(rate)})`
150
317
  );
151
318
  } else {
152
- console.log(`Downloaded ${formatBytes(downloadedBytes)} so far...`);
319
+ logLine(`Downloaded ${formatBytes(downloadedBytes)} so far (${formatRate(rate)})...`);
153
320
  }
154
321
  }
155
322
  });
@@ -159,12 +326,16 @@ function requestBinary(url, dest, redirects = 0) {
159
326
  if (closeErr) {
160
327
  finish(reject, makeDownloadError("write", closeErr.message));
161
328
  } else {
329
+ const elapsedSeconds = Math.max((Date.now() - startedAt) / 1000, 0.001);
330
+ const rate = downloadedBytes / elapsedSeconds;
162
331
  if (totalBytes > 0) {
163
- console.log(
164
- `Download complete: ${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)}.`
332
+ logLine(
333
+ `Download complete: ${formatBytes(downloadedBytes)} / ${formatBytes(totalBytes)} in ${elapsedSeconds.toFixed(1)}s (${formatRate(rate)}).`
165
334
  );
166
335
  } else {
167
- console.log(`Download complete: ${formatBytes(downloadedBytes)}.`);
336
+ logLine(
337
+ `Download complete: ${formatBytes(downloadedBytes)} in ${elapsedSeconds.toFixed(1)}s (${formatRate(rate)}).`
338
+ );
168
339
  }
169
340
  finish(resolve);
170
341
  }
@@ -172,14 +343,14 @@ function requestBinary(url, dest, redirects = 0) {
172
343
  });
173
344
 
174
345
  file.on("error", (err) => {
175
- try { file.close(() => {}); } catch {}
176
- try { fs.unlinkSync(dest); } catch {}
346
+ try { file.close(() => {}); } catch (_) {}
347
+ removeIfExists(dest);
177
348
  finish(reject, makeDownloadError("write", err.message));
178
349
  });
179
350
 
180
351
  res.on("error", (err) => {
181
- try { file.close(() => {}); } catch {}
182
- try { fs.unlinkSync(dest); } catch {}
352
+ try { file.close(() => {}); } catch (_) {}
353
+ removeIfExists(dest);
183
354
  finish(reject, makeDownloadError("network", err.message));
184
355
  });
185
356
 
@@ -198,16 +369,22 @@ function requestBinary(url, dest, redirects = 0) {
198
369
  });
199
370
  }
200
371
 
201
- function verifyBinary(dest) {
202
- try {
203
- execSync(`"${dest}" --version`, { stdio: "pipe", timeout: 5000 });
204
- return { ok: true };
205
- } catch (err) {
372
+ function verifyBinary(binaryPath) {
373
+ logLine("Verifying downloaded binary...");
374
+ const version = binaryVersion(binaryPath);
375
+ if (!version) {
376
+ return {
377
+ ok: false,
378
+ message: "binary verification failed",
379
+ };
380
+ }
381
+ if (!version.includes(packageJson.version)) {
206
382
  return {
207
383
  ok: false,
208
- message: err && err.message ? err.message : "binary verification failed",
384
+ message: `expected version ${packageJson.version}, got '${version}'`,
209
385
  };
210
386
  }
387
+ return { ok: true, version };
211
388
  }
212
389
 
213
390
  async function tryDownloadPrebuilt(target) {
@@ -216,21 +393,34 @@ async function tryDownloadPrebuilt(target) {
216
393
 
217
394
  fs.mkdirSync(NATIVE_DIR, { recursive: true });
218
395
  const dest = nativeBinaryPath();
396
+ const tmpDest = `${dest}.tmp`;
397
+
398
+ if (installFromVerifiedCache(target)) {
399
+ logLine("✓ BB-Agent binary installed successfully from cache.");
400
+ return { ok: true, source: "cache" };
401
+ }
402
+
403
+ if (maybeRepairCache(target) && installFromVerifiedCache(target)) {
404
+ logLine("✓ BB-Agent binary installed successfully from cache.");
405
+ return { ok: true, source: "cache" };
406
+ }
219
407
 
220
408
  let lastError = null;
221
409
  for (let attempt = 1; attempt <= MAX_DOWNLOAD_ATTEMPTS; attempt += 1) {
222
410
  try {
223
- console.log(
411
+ logLine(
224
412
  `Downloading BB-Agent ${BINARY_RELEASE_TAG} for ${target} (attempt ${attempt}/${MAX_DOWNLOAD_ATTEMPTS})...`
225
413
  );
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);
414
+ logLine("This may take a little while on first install because npm downloads the native binary from the GitHub release.");
415
+ removeIfExists(tmpDest);
416
+ await requestBinary(url, tmpDest, 0);
417
+ if (!isWindows()) {
418
+ fs.chmodSync(tmpDest, 0o755);
419
+ }
230
420
 
231
- const verified = verifyBinary(dest);
421
+ const verified = verifyBinary(tmpDest);
232
422
  if (!verified.ok) {
233
- try { fs.unlinkSync(dest); } catch {}
423
+ removeIfExists(tmpDest);
234
424
  return {
235
425
  ok: false,
236
426
  kind: "verify",
@@ -238,12 +428,14 @@ async function tryDownloadPrebuilt(target) {
238
428
  };
239
429
  }
240
430
 
241
- console.log("Verifying downloaded binary...");
242
- console.log("✓ BB-Agent binary installed successfully.");
243
- return { ok: true };
431
+ fs.renameSync(tmpDest, dest);
432
+ refreshCacheFromExistingBinary(target, dest);
433
+ logLine("Cached verified BB-Agent binary for future installs.");
434
+ logLine("✓ BB-Agent binary installed successfully.");
435
+ return { ok: true, source: "download" };
244
436
  } catch (err) {
245
437
  lastError = err;
246
- try { fs.unlinkSync(dest); } catch {}
438
+ removeIfExists(tmpDest);
247
439
  if (err.kind === "not-found") {
248
440
  return {
249
441
  ok: false,
@@ -252,6 +444,7 @@ async function tryDownloadPrebuilt(target) {
252
444
  };
253
445
  }
254
446
  if (attempt < MAX_DOWNLOAD_ATTEMPTS) {
447
+ logLine(`Download failed (${err.message}). Retrying...`);
255
448
  await new Promise((resolve) => setTimeout(resolve, 1000 * attempt));
256
449
  }
257
450
  }
@@ -265,40 +458,34 @@ async function tryDownloadPrebuilt(target) {
265
458
  }
266
459
 
267
460
  function printFallbackHelp(platform, reason) {
268
- console.log("");
461
+ logLine("");
269
462
  if (reason && reason.kind === "not-found") {
270
- console.log(
271
- `BB-Agent ${packageJson.version}: matching prebuilt binary is not published for ${platform}.`
272
- );
463
+ logLine(`BB-Agent ${packageJson.version}: matching prebuilt binary is not published for ${platform}.`);
273
464
  } 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}`);
465
+ logLine(`BB-Agent ${packageJson.version}: failed to download the prebuilt binary for ${platform}.`);
466
+ logLine(`Reason: ${reason.message}`);
278
467
  } else {
279
- console.log(
280
- `BB-Agent ${packageJson.version}: matching prebuilt binary not available yet for ${platform}.`
281
- );
468
+ logLine(`BB-Agent ${packageJson.version}: matching prebuilt binary not available yet for ${platform}.`);
282
469
  }
283
- console.log("");
284
- console.log("╔══════════════════════════════════════════════════════════════╗");
285
- console.log(
470
+ logLine("");
471
+ logLine("╔══════════════════════════════════════════════════════════════╗");
472
+ logLine(
286
473
  "║ BB-Agent: npm could not install native binary for " +
287
474
  platform.padEnd(16) +
288
475
  " ║"
289
476
  );
290
- console.log("║ ║");
291
- console.log("║ Install Rust (if needed): ║");
292
- console.log("║ https://rustup.rs ║");
293
- console.log("║ Then install with rustup for your platform ║");
294
- console.log("║ ║");
295
- console.log("║ Then build BB-Agent: ║");
296
- console.log("║ git clone https://github.com/shuyhere/bb-agent.git ║");
297
- console.log("║ cd bb-agent && cargo install --path crates/cli ║");
298
- console.log("║ ║");
299
- console.log("║ Then run: bb ║");
300
- console.log("╚══════════════════════════════════════════════════════════════╝");
301
- console.log("");
477
+ logLine("║ ║");
478
+ logLine("║ Install Rust (if needed): ║");
479
+ logLine("║ https://rustup.rs ║");
480
+ logLine("║ Then install with rustup for your platform ║");
481
+ logLine("║ ║");
482
+ logLine("║ Then build BB-Agent: ║");
483
+ logLine("║ git clone https://github.com/shuyhere/bb-agent.git ║");
484
+ logLine("║ cd bb-agent && cargo install --path crates/cli ║");
485
+ logLine("║ ║");
486
+ logLine("║ Then run: bb ║");
487
+ logLine("╚══════════════════════════════════════════════════════════════╝");
488
+ logLine("");
302
489
  }
303
490
 
304
491
  async function main() {
@@ -307,6 +494,7 @@ async function main() {
307
494
  }
308
495
 
309
496
  if (hasBundledNativeBinary()) {
497
+ logLine(`BB-Agent ${packageJson.version} native binary already present; skipping download.`);
310
498
  return;
311
499
  }
312
500
 
@@ -330,8 +518,8 @@ async function main() {
330
518
 
331
519
  main()
332
520
  .catch((err) => {
333
- console.error("BB-Agent postinstall notice:", err && err.message ? err.message : String(err));
334
- console.log(
521
+ logLine(`BB-Agent postinstall notice: ${err && err.message ? err.message : String(err)}`);
522
+ logLine(
335
523
  "Install manually: git clone https://github.com/shuyhere/bb-agent.git && cd bb-agent && cargo install --path crates/cli"
336
524
  );
337
525
  })