Skip to content

Install bb's CLI skills onto machines from Settings - #884

Merged
SawyerHood merged 2 commits into
mainfrom
bb/add-install-button-thr_2thxj6anbr
Jul 28, 2026
Merged

Install bb's CLI skills onto machines from Settings#884
SawyerHood merged 2 commits into
mainfrom
bb/add-install-button-thr_2thxj6anbr

Conversation

@SawyerHood

Copy link
Copy Markdown
Collaborator

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-in bb-cli skill into ~/.agents/skills and ~/.claude/skills on the machines you choose.

How it works

The server owns policy (which skills to publish); each daemon owns the destinations.

  • host.install_global_skills pulls 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.
  • Machines install concurrently and independently, so one offline machine doesn't cost the others. The response carries each machine's outcome, and the UI toasts successes and failures separately.
  • host.global_skills_status hashes 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 decides installed / outdated / missing / unknown. Status reads time out at 5s per machine so a wedged machine can't hang the settings page.

HOST_DAEMON_PROTOCOL_VERSION68. 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 }) and sdk.system.cliSkillsStatus().
  • Guide chapter and bb-cli/SKILL.md updated; 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.md was detected as drift.

Note: apps/server/test/internal/internal-skill-trees.test.ts fails on my machine both before and after this branch — it expects file mode 0o644 under a 0o664 umask. Unrelated to this change.

🤖 Generated with Claude Code

SawyerHood and others added 2 commits July 28, 2026 22:50
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
SawyerHood force-pushed the bb/add-install-button-thr_2thxj6anbr branch from 601e25a to b2da93d Compare July 28, 2026 22:51
@SawyerHood
SawyerHood merged commit d6fd3b8 into main Jul 28, 2026
10 checks passed
@SawyerHood
SawyerHood deleted the bb/add-install-button-thr_2thxj6anbr branch July 28, 2026 22:57
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant