bb is an agentic IDE that builds itself. It can control, customize, and automate itself, laying the groundwork for your own software factory.
Every surface — the desktop app, web app, CLI, and HTTP API — is a first-class way to drive bb. Work runs in threads you can follow live, steer at any point, or hand off to another agent.
Note
bb is in active development. Core architecture is stable, but workflows and surfaces are still evolving.
The recommended way to start using bb is the desktop app:
Download the latest desktop app
The desktop app supports macOS on Apple Silicon (arm64). The Linux x64 AppImage
and the Windows x64 installer are alpha: expect problems, and please report
them. Intel Mac users should run bb with npx instead. Native Windows needs
Git for Windows, which bb uses for Git and
for environment setup scripts. You can also run bb inside
WSL2 (Windows Subsystem for Linux):
install WSL2 first, then run the npx command below from your WSL2 (Linux)
shell.
Early adopters can install bb Nightly alongside the stable desktop app. It has a separate application identity, yellow icon, and auto-update feed.
npx bb-app@latestThen open http://localhost:38886.
To control bb from outside bb with the bb CLI, open Settings → General →
Command line and choose Install. See
The bb Command On PATH.
To run the newest automated build instead:
npx bb-app@nightlynpm 12 and later block dependency install scripts by default. bb needs those scripts to build its native add-ons. If your npm version is 12 or later, allow the scripts for the install:
npx --allow-scripts=better-sqlite3,node-pty,@parcel/watcher bb-app@latestOr set the policy once for all global installs:
npm config set allow-scripts=better-sqlite3,node-pty,@parcel/watcher --location=userbb uses the provider CLI you already have authenticated.
For install requirements, provider setup, configuration, and package-focused
docs, start with
packages/bb-app.
Production runs (the desktop app, npx bb-app, and pnpm start checkouts) send
anonymous usage telemetry (app starts, thread creation counts, user message
counts, and plugin installs) to help us understand adoption. Each event carries
the app version, how bb was installed (desktop, npm, or source checkout), and
the OS release, CPU architecture, and Node version. Source checkouts report whether
origin points to github.com/get-bb/bb, another repository, or is unavailable.
Telemetry never includes Git commit hashes or remote URLs. Identification is a random per-install
id stored in your data dir — no user, host, project, workspace, or message
content is ever attached. Plugin install events name only public plugins
(bundled plugins and bb-community marketplace entries); installs from a local
path, a private git or npm source, or a third-party marketplace report no name. Development runs (pnpm dev, pnpm start:worktree) never send. Opt out any run with
BB_TELEMETRY=false. See
apps/server/src/services/system/telemetry.ts.
Use the development loop when working on bb itself:
pnpm devThat starts the Vite app and proxies API and WebSocket traffic to a separate
dev server. The launcher prints the actual ports at startup. Each checkout gets
a data directory under
~/.bb-dev/<checkout-instance>/ and deterministic high ports derived from the
checkout path. The checkout instance id is the sanitized path to the checkout,
relative to your home directory, plus a short hash suffix. Separate worktrees
can run alongside each other and the packaged npx bb-app@latest instance.
To test the production bundle and serving path without switching to production data or ports, use:
pnpm start:worktreeThis builds the same optimized frontend and runtime artifacts as pnpm start,
then serves the app from the BB server on the checkout-specific dev server port.
It keeps the normal checkout-specific dev data directory and host-daemon port.
There is no Vite dev server or hot reload in this mode; rerun the command after
source changes. As with pnpm dev, worktree starts do not send telemetry.
For the Electron desktop shell, keep pnpm dev running and start the desktop
package in a second terminal:
pnpm exec turbo run dev --filter=@bb/desktopThe desktop shell connects to this checkout's running dev app. Stop each command with Ctrl-C in its terminal.
To package a private fork beside the official Homebrew-managed desktop app, use
pnpm desktop:local:package. The resulting bb Local.app has the distinct
bundle identifier dev.bb.desktop.local and does not use the official update
feeds. Updating it is therefore an explicit fetch/rebase/build operation, while
Homebrew can continue updating the official bb.app independently.
Run pnpm desktop:local:update from Terminal or iTerm on a clean local-fork
branch (--current also accepts uncommitted changes); it refuses to run inside bb because it quits bb Local itself. It first
fetches upstream main and reports the upstream and patch counts, before
touching bb Local. It then makes bb Local the running server (stopping the
official app if it holds the port), recovers a leftover update pause, and
checks the plugin collection. Only then does it pause all work through a sealed
bb maintenance lease and quit bb Local. After the quit it rebases the private
commit stack, installs dependencies, packages and smoke-tests the local desktop
app, ad-hoc signs it, and installs it beside the official app. The new bb Local
starts under the sealed lease, the script syncs the private plugins from the
lettland/bb-plugins Git collection while bb Local is open but paused, waits
for every host daemon that was paused to reconnect, releases the lease, and
pushes the result to the fork's main with a force-with-lease. Automatic and
queued work is held while paused and resumed on release; turns interrupted by
the quit are retried where provider-retry applies, and turns already running
on remote hosts continue. If the new app fails its health check, the previous
app is restored from ~/.Trash/bb-local-backup-*.app (the failed app is moved
to ~/.Trash/bb-local-failed-*.app) before the pause is released.
--check only fetches and reports the upstream and patch counts.
--skip-install, --skip-plugins, --skip-other-plugins, and --skip-push
selectively omit those final operations; --skip-install never quits bb Local or pauses work and
still starts the official app when plugin synchronization has no server.
--plugins-only syncs just the lettland/bb-plugins collection and does
nothing else: no git, build, install, or push. It needs no clean worktree, and
bb Local may be running. It logs the resolved bb CLI and syncs through it
against whichever server answers, starting nothing. The CLI is BB_CLI when
set, then bb Local's CLI, then the first bb on PATH or in an installed
bb.app. It refuses to run while a desktop update is in progress or after one
was interrupted, which may have left bb Local paused; if no update is running,
run --recover first. It cannot be combined with other flags except
--skip-other-plugins. Plugins that the
collection manifest no longer lists are removed, and their settings, secrets,
and schedules are dropped.
After the collection sync, --plugins-only and a full update also update the
other installed plugins (git and npm plugins installed from a tracking ref or
range). This pass runs last, after the update has completed and the fork push is
done, so it cannot roll back a release or lengthen a pause. It runs
bb plugin outdated and applies each compatible update with
bb plugin update --yes, which means it auto-applies third-party full-trust
plugin code without a prompt. Failures only print warnings and never change the
exit code. The outdated read times out after 2 minutes and each update after
10 minutes; a timed-out update warns that the server-side update may still
finish. --skip-other-plugins skips just this pass and keeps the collection
sync; --skip-plugins skips both. Once the update is applied, Ctrl-C aborts the
fork push if it is still running, or stops the third-party pass, and the
updater then exits non-zero even though the bb update itself already finished.
The pause covers every host that was connected when it was sealed, including
remote hosts such as a VPS. Release waits for a daemon with each of those host
IDs to reconnect; keep those machines awake until the update finishes. A paused
host that is gone for good blocks the release: the updater cannot release the
pause until some daemon with that host ID reconnects, so reconnect one (or
restart its daemon) and run --recover.
If the update was interrupted (a closed terminal, a crash, or a host daemon that
never reconnected), the pause may still be held and the state is recorded in
~/.bb/admin/desktop-update.json. Run pnpm desktop:local:update --recover
to start bb Local if needed, wait for the paused hosts, and release the pause.
--recover stops the official bb.app if it is running, so run it from
Terminal or from inside bb Local, never from inside the official app. If a
remote host stays offline, reconnect it or restart its daemon first. If the
installed app will not start, restore the backup the updater printed over
/Applications/bb Local.app, then run --recover. If the state file is
unreadable, or another maintenance lease is held, --recover prints the raw
bb maintenance commands for the installed bb Local CLI (skip the transition
when the phase is already releasing) and points to bb maintenance status and
docs/linux-arm64-vps.md.
Use pnpm desktop:local:update -- --current to run the same test, package,
signing, installation, activation, plugin synchronization, and health workflow
against the checkout exactly as it is on disk, including uncommitted and
untracked changes, which it lists before building and marks as <sha>-dirty.
Current-checkout mode skips every remote fetch and the rebase. If pushing is
enabled, it protects the fork with the existing remote-tracking head as a
force-with-lease; fetch the fork first if that ref is missing. Pushing requires
the checked-out branch to match the configured fork branch and a checkout
without uncommitted changes. Pass --skip-push to use another branch or a
dirty checkout and avoid all Git remote operations; dependency installation can
still access package registries.
The updater identifies the two remotes by URL, not by name: whichever remote
points at get-bb/bb is upstream, and the remaining one is the fork. Either
remote layout therefore works, whether upstream is origin and the fork is
fork, or upstream is a named remote such as _get-bb and the fork is
origin. Set BB_LOCAL_UPSTREAM_REMOTE or BB_LOCAL_FORK_REMOTE to pin a
name explicitly, BB_LOCAL_UPSTREAM_SLUG to track a different upstream
repository, and BB_LOCAL_UPSTREAM_BRANCH or BB_LOCAL_FORK_BRANCH to use
branches other than main.
The updater installs the complete lettland/bb-plugins collection. It updates
plugins already installed from the collection's own GitHub repository, replaces
plugins installed from another source or another GitHub repository, and removes
collection-managed plugins no longer present in the manifest.
To use the dev app from another machine over Tailscale, start it with the Tailscale browser origin configured:
BB_APP_URL=https://<machine>.<tailnet>.ts.net pnpm devNote the printed app port, then publish the loopback Vite listener in another terminal:
tailscale serve --bg --https=443 http://127.0.0.1:<app-port>Then open https://<machine>.<tailnet>.ts.net. Source dev binds both the Vite
app and main server to loopback by default; Vite continues to proxy API and
WebSocket traffic.
For direct access at http://<tailscale-ip>:<app-port> instead, run:
pnpm dev:remoteThis binds the Vite app and main server to all IPv4 interfaces. The remote browser must be able to reach both the printed app and server ports for realtime updates. The server API is unauthenticated and permits command execution and file reads, so use this only behind a trusted network boundary and restrict the ports to Tailscale traffic with the host firewall when the LAN is not trusted.
To access the production-style worktree server directly from another machine, run:
pnpm start:worktree-remoteThis uses the same checkout-specific data directory and ports as
pnpm start:worktree, but binds its single server listener to all IPv4
interfaces. The server API is unauthenticated and permits command execution and
file reads, so use it only behind a trusted network boundary and restrict the
port to Tailscale traffic with the host firewall when the LAN is not trusted.
To use the component storybook from another machine, run:
pnpm storybookLadle binds to all interfaces and configures its HMR WebSocket to use the
browser's current host instead of localhost. Do not run pnpm storybook on an
untrusted network.
Development behavior is intentionally split:
- the app hot reloads itself
- the server does not hot reload
- the host daemon does not hot reload
When you want the server and host daemon to pick up the latest build output, use:
pnpm dev:restart
pnpm dev:restart-server
pnpm dev:restart-host-daemonThese rebuild first, then restart only the targeted stateful services.
To run a production-mode build from a source checkout:
pnpm startThat builds only the app, server, and host-daemon runtime artifacts, then runs
the launcher directly against those workspace outputs. Use the bb-app
tarball smoke task when validating the published npx bb-app@latest package
layout.
pnpm bb --help # built CLI, targets the default/prod instance
pnpm reset # clear production state
pnpm bb:dev --help # source CLI, targets this checkout's dev instance
pnpm reset:dev # clear this checkout's dev state
pnpm reset:all # clear both production and dev statesThese reset commands prompt for confirmation before deleting anything.
See Repository overview for the monorepo package and app map.
See System overview for runtime architecture, data model, and component boundaries.
See CONTRIBUTING.md for contribution guidelines.
bb uses native add-ons, for example better-sqlite3 and @parcel/watcher. npm
downloads or builds those binaries in a package install script. If npm does not
run install scripts, the binaries are absent. bb then stops at startup with this
error:
Error: Could not locate the bindings file. Tried:
→ .../node_modules/better-sqlite3/build/better_sqlite3.node
There are two usual causes.
The first cause is npm 12 or later. Since npm 12, npm blocks dependency install
scripts by default and prints
npm warn install-scripts N packages had install scripts blocked. Name bb's
native add-ons in --allow-scripts to let this one command run their install
scripts:
npx --allow-scripts=better-sqlite3,node-pty,@parcel/watcher bb-app@latestFor a permanent install with the same setting, use:
npm install -g --allow-scripts=better-sqlite3,node-pty,@parcel/watcher bb-app
bb-appTo allow them for all global installs on this machine, run
npm config set allow-scripts=better-sqlite3,node-pty,@parcel/watcher --location=user.
npm 10 and 11 accept or ignore the flag, so it is safe on every supported Node.
The second cause is ignore-scripts=true in your ~/.npmrc. Set the
npm_config_ignore_scripts environment variable to let this one command run its
install scripts:
npm_config_ignore_scripts=false npx bb-app@latestFor a permanent install with the same setting, use:
npm_config_ignore_scripts=false npm install -g bb-app
bb-appThe environment variable applies to that one command only. Keep
ignore-scripts=true in your ~/.npmrc if you want it for security.
The same error has other causes. A Node.js major-version change after the
install causes it. A copy of node_modules from a different operating system,
CPU architecture, or libc variant also causes it. To recover, install the
package again, or run npm rebuild better-sqlite3.

