Install bb's CLI skills onto machines from Settings - #884
Merged
Conversation
Agents running outside bb (Claude Code, Codex, anything reading ~/.agents/skills) had no way to learn the bb CLI. Settings -> Skills now installs bb's built-in bb-cli skill into ~/.agents/skills and ~/.claude/skills on the machines you choose. The server picks which skills to publish and each daemon owns the destinations: `host.install_global_skills` pulls the tree through the existing skill store, stages the copy beside its destination, and swaps it in, so a failed copy never leaves a half-written skill where an agent would read it. Machines install concurrently and independently, so one offline machine does not cost the others; the response reports each machine's outcome. `host.global_skills_status` hashes each installed copy with the same recipe used for skill trees, and the server compares those raw hashes to what it would install to decide installed / out of date / missing / unknown. The settings row badges that summary and the confirm dialog shows it per machine. HOST_DAEMON_PROTOCOL_VERSION goes to 68: both commands are new, so an older daemon would reject them and must update first. The same feature ships on the CLI and SDK: `bb skill install-cli-skills` (repeatable `--machine`, defaulting to every connected machine) and `bb skill cli-skills-status`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The new host RPC commands and system route schemas flow into the plugin SDK's bundled .d.ts, which the templates package embeds. Both are checked-in generated files, so CI's --check runs failed until they were regenerated. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SawyerHood
force-pushed
the
bb/add-install-button-thr_2thxj6anbr
branch
from
July 28, 2026 22:51
601e25a to
b2da93d
Compare
SawyerHood
added a commit
that referenced
this pull request
Jul 29, 2026
CI on `main` was failing intermittently from two independent races. Both are ~2% flakes, so they broke main sporadically rather than outright — main run [30406401925](https://github.com/ymichael/bb/actions/runs/30406401925) (#884) failed `Tests (app)`, and run [30403074723](https://github.com/ymichael/bb/actions/runs/30403074723) (#883) failed `Tests (packages)`. ## 1. `useResourcePagination` dropped clicks `useResourcePagination` mirrored the derived `page` back into state from a `useEffect`. That write-back captured the pre-interaction page, so when a viewport measurement changed `pageSize`, the queued effect flushed *after* a click and overwrote it: 1. The measurement raises `pageSize` 10 → 15. That commit renders `page=0` at `pageSize=15` while the stored `pageSize` is still `10`, and schedules the write-back with `page=0` captured. 2. Under load the effect hasn't flushed when the click lands, so `setPage(1)` writes `{page: 1, pageSize: 15}`. 3. React then flushes the stale effect, which sees `current.page (1) !== page (0)` and resets state to `{page: 0, pageSize: 15}` — discarding the click. Instrumenting `setPage` printed exactly this, and only in the failing runs: ``` [DIAG] setPage(1) closurePageSize=15 statePageSize=10 statePage=0 renderPageSize=15 ``` This is a real product bug, not just a test flake: a user clicking **Next** in the instant after a list first measures its row height has the click silently ignored. It affects every paginated resource list — plugins, skills, browse, and automations. State is now an **anchor** (the page plus the page size it was chosen under), written only by `setPage` or a new projection, with the rendered page derived from it. The projection reset moved from an effect to a render-time adjustment so it can't land after an interaction. A side benefit falls out: the anchor survives page-size changes, so remeasuring 15 → 10 rows returns you to the page you picked instead of stranding you on page 1. ## 2. Cross-package fixture race `plugins/workflows/src/cli.test.ts` walks the entire checkout, and the `packages` CI shard runs every other package's tests concurrently in one turbo invocation. It enumerated `packages/plugin-registry/.vendor-fixture-*/app.tsx` and then read it after the plugin registry's `afterAll` deleted it → `ENOENT`. The walk now skips dot-directories — covering VCS internals, tool caches, and the scratch trees siblings create in their own package roots (`.vendor-fixture-*`, and agent-runtime's `.bb-codex-outside-*`) — and tolerates a path deleted mid-walk. `.git`/`.turbo` dropped out of the explicit ignore set since the dot rule subsumes them. ## Verification - The stress harness that reproduced the pagination flake at ~2/128 under 8× contention now runs **160/160 clean**. - New `apps/app/src/components/ui/resource-pagination.test.tsx` pins the anchor semantics; its first test **fails on the original hook** (`expected +0 to be 1`) and passes on the fix. It lives in `apps/app` because `shared-ui` has no test runner, matching the existing `apps/app/src/components/ui/theme.test.ts` convention. - With a `.vendor-fixture-PROOF/app.tsx` containing the removed command in place, the workflows test passes (fixture ignored); with the same string in `docs/cli-guide-and-skill.md` it still fails as intended, so coverage is unchanged. - `turbo run typecheck lint` on app/shared-ui/workflows/automations: 0 errors. - `turbo run test`: app 292 files, workflows 13, automations 3, plugin-registry 1 — all pass. - Full CI `packages` shard: 47/48, with the two previously-racing packages passing concurrently. Two failures left untouched, both pre-existing and local-only (they pass in CI): `@bb/desktop#test` needs an Electron binary that was never downloaded in my sandbox, and `bb-plugin-tasks#test` fails on `window.localStorage` being undefined — confirmed failing identically on a pristine HEAD. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Agents running outside bb — Claude Code, Codex, anything that reads
~/.agents/skills— had no way to learn the bb CLI. Settings → Skills now installs bb's built-inbb-cliskill into~/.agents/skillsand~/.claude/skillson the machines you choose.How it works
The server owns policy (which skills to publish); each daemon owns the destinations.
host.install_global_skillspulls each tree through the existing skill store, stages the copy in a sibling temp dir, then swaps it in — a failed copy never leaves a half-written skill where an agent would read it. Existing copies of the same name are replaced; unrelated skills in those roots are untouched.host.global_skills_statushashes each installed copy with the same recipe used for skill trees and returns the raw hashes; the server compares them to what it would install and decidesinstalled/outdated/missing/unknown. Status reads time out at 5s per machine so a wedged machine can't hang the settings page.HOST_DAEMON_PROTOCOL_VERSION→ 68. Both commands are new, so an older daemon would reject them and must update first.UI
The row carries a status badge ("Installed", "Out of date", "Installed on 1 of 3 machines"). Install always confirms first: with several machines the dialog doubles as the picker, showing each machine's status and leaving disconnected ones unselectable; with a single machine there's nothing to choose, so the list is dropped and the machine is named in the description.
Gating is on a connected machine, not on
useHostDaemon().hasDaemon— that reports browser→local-daemon reachability, which is false whenever bb is open remotely even though the install itself runs server-side.Agent surfaces
bb skill install-cli-skills— repeatable--machine <id-or-name>, defaults to every connected machine, reports per-machine outcomes, exits non-zero if any failed.bb skill cli-skills-status— per-machine install state.sdk.system.installCliSkills({ hostIds })andsdk.system.cliSkillsStatus().bb-cli/SKILL.mdupdated; templates regenerated.Testing
~660 lines of tests: daemon install (both roots written, stale copy replaced with no orphaned files, install survives a fetch failure) and a status round-trip (missing → matching hashes → hand-edit detected in one root only); server route (multi-machine success, partial failure, unknown machine, empty list, hash→status mapping, no-session machine); dialog and badge-summary tests.
Verified end to end against a dev instance: a stale copy correctly reported Out of date, installing flipped it to Installed, and hand-editing an installed
SKILL.mdwas detected as drift.Note:
apps/server/test/internal/internal-skill-trees.test.tsfails on my machine both before and after this branch — it expects file mode0o644under a0o664umask. Unrelated to this change.🤖 Generated with Claude Code