feat(hooks): user-scope agent sync hooks, one-command setup, --only scoping (#71)

* feat(hooks): register agent sync hooks per machine, not per project

Agent platforms read hook config only from the directory a session starts
in — never a parent, never a subfolder. Project-level hooks therefore fired
only for sessions that happened to start at the mount, and, living inside a
synced folder, they replicated one machine's agent config to the whole team
(a second writer of a file bdrive already owns). Claude Code additionally
ignores project hooks until the folder is trusted, so in practice they were
often inert without any visible sign.

Hooks now go to each platform's user config, once per machine, covering
every session in every folder; the existing shell guard keeps them a no-op
outside BearDrive projects. Install migrates away blocks older versions
wrote into projects, and `bdrive hooks uninstall` removes ours while leaving
foreign hooks untouched.

Setup is also one command now. init absorbs the skill install, prints the
hub link, and takes --server, so connecting to a named hub no longer needs a
separate login; the runbook forbids preflight and command chaining, since
each distinct command costs the user a permission prompt. For plugin users a
PreToolUse hook auto-approves bdrive's own setup subcommands — narrowly: any
shell operator in the command disqualifies it.

Also drops --shared in favor of `init . --only wiki,docs`, which writes a
managed block of .bdriveignore rules instead of a second scope mechanism.
Because those rules sync, `sync --prune` now refuses on a scoped project
rather than stripping everything outside the scope from the hub for everyone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016aYntCWwdUhpzUfEk3ddyJ

* docs: fix stale claims an audit found against the new CLI

An audit of every doc surface against the code turned up claims that the
user-scope hook move and the one-command init made false: project-level
hooks "riding the repo", the Claude trust prompt, Codex's //hooks project
layer, `--no-hooks` skipping the skill (it does not), prune reconciling
against a per-device scope (it now refuses on a scoped project), and
`--scan-interval`/`--remote-interval` documented as init flags when they
only exist on `bdrive daemon run`.

Also documents the surface added today — `--server`, `bdrive hooks
uninstall`, and the plugin's PreToolUse auto-approval — refreshes the two
sample `init` transcripts to the real output, and corrects hook matchers
that had drifted from agenthooks.go.

`bdrive scope` told users to narrow an existing mount with `bdrive init .
--only <dirs>`, which resume then ignored — a dead end. Init now applies
--only on resume, writing the scope block, so the advice works.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016aYntCWwdUhpzUfEk3ddyJ

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Snow W. Lee (Sungwon)
2026-07-29 10:08:45 +09:00
committed by GitHub
co-authored by Claude Opus 5
parent 22461a3b4f
commit 4e34d03e14
52 changed files with 2601 additions and 691 deletions
@@ -54,7 +54,8 @@ overlap. Nothing is silently dropped.
A per-mount daemon scans the folder every few seconds and exchanges with the
remote every ~10s — or immediately after local edits. Tune with
`--scan-interval` and `--remote-interval` on `bdrive init`.
`--scan-interval` and `--remote-interval` on `bdrive daemon run` (init always
uses the defaults).
It re-reads `.bdrive/config.json` each tick. If that file vanishes because the
folder was moved, renamed, or deleted, the daemon **exits cleanly without
@@ -29,7 +29,7 @@ bdrive url --sync wiki/report.html
With no argument, `bdrive url` gives the project home.
:::tip[Agents do this automatically]
The sync hook installed by `bdrive hooks install` injects the project's
The sync hook `bdrive init` registers injects the project's
gated-link formula into the agent's context, so a connected agent appends
`path` [🔗](link) to every synced path it mentions — without being asked. See
[Set up with your agent](/start/setup/).
+76 -27
View File
@@ -7,39 +7,75 @@ Shared agent memory works better when it's curated. A folder holding
`node_modules/` and build output costs sync bandwidth, buries the documents that
matter, and gives agents thousands of irrelevant paths to wander into.
Two mechanisms control it: an **include list** that narrows the project to a
subfolder, and **`.bdriveignore`** that opts individual paths out. Both are
applied symmetrically — the same filter governs what's read from disk and what's
written back to it.
One mechanism controls it: **`.bdriveignore`**, a gitignore-style rule file at
the mount root. It opts individual paths out, and — as a bdrive-managed block of
"only these folders" rules — it narrows the project to chosen subfolders. Rules
are applied symmetrically: the same filter governs what's read from disk and
what's written back to it.
## Sync only subfolders
A mount is always exactly the folder you name:
```sh
bdrive init --shared wiki
bdrive init --shared wiki,docs # several subfolders, one project
bdrive init wiki # ./wiki is the project
```
This is the right shape inside a code repository: sync `wiki/` or `docs/` and
leave the source tree alone. The agent gets a knowledge folder; the code stays
in git where it belongs. `--shared` takes one folder or several — comma-separated
or repeated — and they all join the same project, with one membership and one
permission set. The interactive `bdrive init` asks the same question.
Its contents *are* the project's contents — no `wiki/` prefix on the hub — and
nothing outside it is ever scanned. This is the right shape inside a code
repository: sync `wiki/` or `docs/` and leave the source tree alone. The agent
gets a knowledge folder; the code stays in git where it belongs.
The result lands in `.bdrive/config.json` as an include list:
When the project has to be the enclosing folder — several subfolders belonging
to one project, or agents that work from the repo root — narrow it with
`--only`:
```jsonc
{ "id": "m-5a10b713", "volume": "notes",
"remote": "https://drive.example.com/p/p-7f3a2c91", "include": ["/wiki/"] }
```sh
bdrive init . --only wiki,docs
```
The leading slash anchors each entry to the mount root: `/wiki/` means the
`wiki` folder at the top of this project and nothing else, so a nested
directory that happens to share the name never syncs.
Both folders join the same project, with one membership and one permission set.
The interactive `bdrive init` asks the same question.
`--only` writes ordinary `.bdriveignore` rules, in a managed block at the top of
the file:
```gitignore
# bdrive scope — only these folders sync (managed by bdrive; change with `bdrive scope add/rm`)
/*
!/wiki/
!/docs/
# end bdrive scope
```
`/*` excludes everything at the mount root; each `!` line re-includes one
folder, anchored to that root — so a nested directory that happens to share the
name never syncs. The block goes first because matching is last-match-wins:
ordinary rules below it still apply, which keeps `node_modules/` excluded
*inside* a scoped folder.
There is no separate scope setting to keep in step.
:::note[Legacy include lists]
Projects created before the scope moved into `.bdriveignore` carry an `include`
list in `.bdrive/config.json`. It is still honored, but never written any more —
`bdrive scope` reports it and points you at `bdrive init . --only <dirs>` to
move it into `.bdriveignore`.
:::
## The scope is the team's
Because the rules live in `.bdriveignore`, and that file syncs, a narrow scope
is the whole team's scope: every device that syncs the project picks it up.
Widening or narrowing it is a change everyone sees, not a local preference.
(Legacy include lists are the exception — they sit in the never-synced
`.bdrive/config.json` and apply to one device only.)
## Change the scope later
`bdrive scope` shows the include list; `scope add` / `scope rm` edit it — no
JSON editing, and the running daemon applies the change within seconds:
`bdrive scope` shows what syncs; `scope add` / `scope rm` edit the managed block
— no hand-written negation syntax, and the running daemon applies the change
within seconds:
```sh
bdrive scope # what syncs now
@@ -47,14 +83,19 @@ bdrive scope add notes # also sync ./notes
bdrive scope rm docs # stop syncing ./docs
```
Both act on an already-narrowed project; on a whole-folder mount `scope add`
points you at `bdrive init . --only <dirs>` and `scope rm` at a plain
`.bdriveignore` rule.
Removing a folder stops syncing it but deletes nothing — local files stay, and
the hub keeps everything already synced (the same
[non-destructive rule](#opting-out-is-non-destructive) as `.bdriveignore`).
Removing the *last* entry is refused, because an empty include list means the
whole folder syncs; if you want to stop syncing entirely, that's `bdrive stop`.
[non-destructive rule](#opting-out-is-non-destructive) as any other
`.bdriveignore` change). Removing the *last* entry is refused, because an empty
block means the whole folder syncs; if you want to stop syncing entirely, that's
`bdrive stop`.
:::tip
A `--shared` mount is also where the two-file
A scoped mount is also where the two-file
[`AGENTS.md` pattern](/guides/shared-agent-memory/) earns its keep — the synced
map lives in `wiki/`, and the repo root gets a pointer to it.
:::
@@ -75,9 +116,9 @@ build/
Supported: `#` comments, `*`, `**`, `?`, a trailing `/` for directories, a
leading (or any) `/` for root-anchoring, and `!` to re-include.
It always syncs — even on an include-list mount where it sits outside the
scope, and even if a pattern matches it — so every device shares the same
rules: one person excluding `*.tmp` fixes it for the whole team.
It always syncs — even when the scope block excludes everything around it, and
even if a pattern matches it — so every device shares the same rules: one
person excluding `*.tmp` fixes it for the whole team.
`bdrive init` seeds a starter one covering `node_modules`, build directories,
caches, and `.env*`.
@@ -89,6 +130,14 @@ but is **deleted nowhere**. The path is dropped from the local cache without a
delete op, so opting out on your machine never removes the file from anyone
else's.
To take something off the hub as well, use `bdrive forget <path>`: it writes the
rule and removes what already synced from the hub, keeping the local copy on
every device. `bdrive sync --prune` does the same reconciliation for rules you
added by hand — but it **refuses on a scoped project**, because "only these
folders" rules exclude everything else the project holds, and pruning them would
strip all of it from the hub for every teammate, not just this device. Name the
specific paths with `bdrive forget` instead, or widen the rules first.
## What never syncs
Regardless of configuration:
@@ -40,7 +40,7 @@ conventions on day one.
**Per machine, never synced.**
For `--shared` mounts inside a repository, append two or three lines to the
For a synced subfolder inside a repository, append two or three lines to the
repo root's `AGENTS.md` and/or `CLAUDE.md` (both, if both exist):
```markdown
@@ -20,8 +20,10 @@ Hubs track it.
Sync replication never counts as a read, and neither does viewing a blob in
history. Only genuine consumption.
Agent reads require the hooks from [Set up with your agent](/start/setup/).
Without them you'll see human traffic only.
Agent reads require the hooks from [Set up with your agent](/start/setup/)
registered once per machine, in the agent's own user config, so every device
whose agents you want counted needs its own `bdrive init` (or `bdrive hooks
install`). Without them you'll see human traffic only.
## In the file browser
+1 -1
View File
@@ -69,7 +69,7 @@ page.
(last-writer-wins), and the losing version is preserved as a
`name.bdrive-conflict-<device>-<time>` file.
- **Selective sync** — a gitignore-style `.bdriveignore` opts files out, and
`bdrive init --shared <dir>` narrows sync to one subfolder.
`bdrive init . --only <dirs>` narrows sync to chosen subfolders.
- **macOS and Linux.**
## Hub options
+4 -3
View File
@@ -49,9 +49,10 @@ brew upgrade beardrive
Clients and hub are the same binary — keep them roughly in step. The sync
protocol is append-only journals plus blobs, which old clients read forward.
After upgrading a client, re-run `bdrive hooks install` once per project to pick
up hook improvements, and `bdrive skill install` once per machine to refresh the
agent skill.
After upgrading a client, re-run `bdrive hooks install` to pick up hook
improvements and `bdrive skill install` to refresh the agent skill — both once
per machine, since both write to your user config. (Re-running `bdrive init` in a
synced folder refreshes both.)
## Next
@@ -7,9 +7,10 @@ This is [what your agent does for you](/start/setup/), one command at a time.
Useful on a machine with no agent, when scripting a fleet, or when you simply
want to see the moving parts.
Three steps: sign the device in, start syncing a folder, work normally. Then
[register the hooks](/manual/skills-and-hooks/) — that last step is what keeps
an agent's files fresh, and it is the one hand-setups forget.
Three steps: sign the device in, start syncing a folder, work normally. `init`
installs the agent skill and [registers the hooks](/manual/skills-and-hooks/)
along the way — that is what keeps an agent's files fresh, and it used to be the
step hand-setups forgot.
## 1. Sign this device in
@@ -41,7 +42,10 @@ Once per project.
```console
$ cd ~/workspace && bdrive init
initialized /Users/snow/workspace
server: https://your-hub
project: workspace (p-7f3a2c91)
skill: installed for claude, codex
claude hooks registered → /Users/snow/.claude/settings.json
daemon: running (pid 55434, scan 3s, remote sync 10s)
```
@@ -49,20 +53,26 @@ On a terminal, `init` walks you through two questions: **create a new project or
connect an existing one** (picked from the server's list), and **sync the whole
folder or only a subfolder** such as `./shared`.
Every question has a flag — `--name`, `--project`, `--shared`, `--yes` — and
Every question has a flag — `--name`, `--project`, `--only`, `--yes` — and
without a TTY init never prompts. It creates-or-joins a project named after the
folder and syncs everything.
Init writes `.bdrive/config.json`, seeds a starter `.bdriveignore`
(node_modules, build dirs, caches, `.env*`), and starts the daemon. Not signed
in yet? It runs the login flow first.
(node_modules, build dirs, caches, `.env*`), starts the daemon, and prints the
project's hub link. It also installs the `beardrive` skill and registers the sync
hooks for any agent platform it detects — in that platform's **user** config, once
per machine, so nothing lands inside the project. `--no-hooks` skips the hooks
(the skill is installed either way). Not
signed in yet? It runs the login flow first.
:::tip[Working inside a repository]
Sync subfolders rather than the repo root: `bdrive init --shared docs` (or
several at once: `--shared wiki,docs`). Git directories are never synced
(per-file last-writer-wins would corrupt a repository), but a narrower scope
keeps the sync surface honest. Adjust it later with `bdrive scope add`/`rm`
see [Scoping the folder](/guides/scoping/).
Sync a subfolder rather than the repo root: `bdrive init docs` makes `./docs`
the project and scans nothing else. For several subfolders in one project, mount
the repo and narrow it: `bdrive init . --only wiki,docs`, which writes the
narrowing as `.bdriveignore` rules. Git directories are never synced (per-file
last-writer-wins would corrupt a repository), but a narrower scope keeps the
sync surface honest. Adjust it later with `bdrive scope add`/`rm` — see
[Scoping the folder](/guides/scoping/).
:::
## 3. Work normally
@@ -96,8 +106,8 @@ changes.
## Next
- [Skills and hooks in detail](/manual/skills-and-hooks/) — the two commands
that make an agent read fresh files every turn. Don't stop before this one.
- [Skills and hooks in detail](/manual/skills-and-hooks/) — what `init` wrote to
make an agent read fresh files every turn, and how to inspect or remove it.
- [Shared agent memory](/guides/shared-agent-memory/) — orient agents in the
folder so they know where to read and write.
- [Artifacts and links](/guides/agent-artifacts/) — internal links for
@@ -1,11 +1,11 @@
---
title: Skills and hooks in detail
description: What bdrive skill install and bdrive hooks install actually write — per-platform paths, hook events, idempotency, and when to re-run them.
description: What bdrive skill install and bdrive hooks install actually write — the user-level config paths, hook events, idempotency, and when to re-run them.
---
Setting up through your agent runs these two commands for you. This is what
they do, for when you want to run them yourself, review what changed, or debug a
folder that isn't syncing.
`bdrive init` runs both of these for you — that is why setup is one command. This
is what they do, for when you want to run them yourself, review what changed, or
debug a folder that isn't syncing.
## The skill
@@ -35,19 +35,21 @@ beardrive@beardrive` covers Claude without this command.
## The hooks
```sh
bdrive hooks install # every detected platform, this project
bdrive hooks install # every detected platform, this machine
bdrive hooks install --agent claude,codex,gemini,hermes
bdrive hooks # status table
bdrive hooks uninstall # remove them again
```
Each platform gets the same three hooks written into its own config format:
Each platform gets the same three hooks written into its own user-level config,
in its own format:
| Platform | Config it writes | Pull / push / read events |
|---|---|---|
| Claude Code | `<project>/.claude/settings.json` | `UserPromptSubmit` / `PostToolUse` (Write\|Edit) / `PostToolUse` (Read\|Grep\|Bash) |
| Codex | `<project>/.codex/hooks.json` | `UserPromptSubmit` / `PostToolUse` (apply_patch) / `PostToolUse` (read_file\|shell) |
| Gemini CLI | `<project>/.gemini/settings.json` | `BeforeAgent` / `AfterTool` (write_file\|replace) / `AfterTool` (read tools) |
| Hermes | `~/.hermes/config.yaml` (per user) | `pre_llm_call` / `post_tool_call` (write_file\|patch) / `post_tool_call` (read_file\|grep\|bash) |
| Claude Code | `~/.claude/settings.json` | `UserPromptSubmit` / `PostToolUse` (Write\|Edit\|MultiEdit) / `PostToolUse` (Read\|Grep\|Bash) |
| Codex | `~/.codex/hooks.json` | `UserPromptSubmit` / `PostToolUse` (apply_patch) / `PostToolUse` (read_file\|shell) |
| Gemini CLI | `~/.gemini/settings.json` | `BeforeAgent` / `AfterTool` (write_file\|replace\|edit) / `AfterTool` (read tools) |
| Hermes | `~/.hermes/config.yaml` | `pre_llm_call` / `post_tool_call` (write_file\|patch) / `post_tool_call` (read_file\|grep\|bash) |
Three hooks, three jobs:
@@ -64,8 +66,15 @@ Every platform pipes hook JSON with a session id, so one hook command serves all
four, and changes are stamped with `<agent> session <id>` — visible in
`bdrive log` and the hub's history.
Codex asks once to trust the project's `.codex` layer. Answer yes, or run
`/hooks` inside Codex.
Codex hooks are experimental and off by default. Turn them on in
`~/.codex/config.toml`:
```toml
[features]
codex_hooks = true
```
Codex then asks once to trust the hook definition. Answer yes.
## Both are safe to re-run
@@ -73,15 +82,26 @@ Merging is idempotent and preserves hooks you already have. Each hook carries
its own marker, so a config written before a hook existed gains just the missing
one, and a registered hook's matcher is upgraded in place when coverage grows.
Re-run after a CLI upgrade: `bdrive hooks install` once per project, `bdrive
skill install` once per machine.
Re-run after a CLI upgrade: `bdrive hooks install` and `bdrive skill install`,
once per machine each.
## Where they live matters
Claude Code, Codex, and Gemini CLI hooks are **project-level** — they ride the
repository, so a teammate who clones it syncs whether or not they installed
anything. Hermes hooks are **per-user** (`~/.hermes/config.yaml`), outside the
repo, so each person registers their own.
Hooks are registered **once per machine**, in each platform's own user config —
never inside a project. Agent platforms read hook config only from the directory
a session starts in: never a parent, never a subfolder. A file in the project
would fire only for the sessions that happened to start exactly there, and — if
the project is synced — would travel to the whole team. A user-level
registration covers every session in every folder instead.
The hook is a fast no-op in any folder without a `.bdrive/` directory, which is
what makes registering it globally safe.
So BearDrive writes no agent-config directory into your project, and teammates
don't inherit your hooks: each device registers its own the first time it runs
`bdrive init`. Earlier versions did write project-level hooks; installing strips
those out as it goes, so nothing ends up running twice.
The hook opens with a shell guard that makes it a fast no-op in any folder
without a `.bdrive/` directory, which is what makes registering it globally safe.
`bdrive hooks uninstall` takes them back out — it removes only BearDrive's own
entries and leaves every other hook in those files untouched. Syncing itself is
unaffected; only turn-boundary sync stops.
+40 -18
View File
@@ -11,15 +11,16 @@ One binary, `bdrive` — the CLI, the sync daemon, and the web server.
|---|---|
| `bdrive login [server-url]` | Sign this device in. Browser flow; `--device` forces the code flow, and shells without a TTY (agents, CI, SSH) fall back to it automatically. Default server is beardrive.ai — the managed cloud, free personal workspace on signup; pass your hub URL to self-host. Switch hubs with `bdrive login <new-url>`. `--status` shows the current server and account |
| `bdrive logout` | Sign this device out — clear the saved token and account. `--forget` also drops the remembered server |
| `bdrive init [folder]` | Create or connect a project and start syncing. Interactive on a TTY; flags (`--name`, `--project`, `--shared`, `--yes`) for scripts. Re-run to resume |
| `bdrive init [folder]` | Create or connect a project and start syncing — the mount is always exactly the folder named. Interactive on a TTY; flags (`--name`, `--project`, `--server`, `--only`, `--yes`) for scripts. Also installs the agent skill, registers agent sync hooks for detected platforms (`--no-hooks` skips the hooks only), and prints the project's hub link. Re-run to resume |
| `bdrive stop [folder]` | Stop syncing — daemon and agent sync hooks both pause. Files stay on disk; `bdrive init` resumes |
| `bdrive scope [add\|rm <dirs...>]` | Show or change which subfolders sync — the include list set by `init --shared`. Run from the mount root; the daemon picks changes up in seconds. `rm` stops syncing a folder but deletes nothing, locally or on the hub |
| `bdrive scope [add\|rm <dirs...>]` | Show or change which subfolders sync — edits the managed block of `.bdriveignore` rules that `init --only` writes. Run from the mount root; the daemon picks changes up in seconds. `rm` stops syncing a folder but deletes nothing, locally or on the hub |
| `bdrive forget <path>...` | Stop syncing a path and remove it from the hub. Adds the rule to `.bdriveignore` (which syncs) and prunes in one step. Local files are never touched, here or on teammates' devices |
| `bdrive url [path]` | Internal hub link for a file or folder — sign-in and membership required. `--sync` pushes first; no argument gives the project home. Computed locally |
| `bdrive share <file>` | Public URL for a synced file. `--list`, `--revoke`, `--expires` |
| `bdrive sync [folder]` | Run one sync cycle now. Refuses folders this device never `init`ed and folders paused by `bdrive stop`. `--note <text>` stamps session context onto changes; `--note-ttl` (default 30m) bounds it. `--prune` also removes from the hub what `.bdriveignore` now excludes (files stay on disk everywhere). `--hook <label>` is agent-hook plumbing |
| `bdrive hooks [install]` | Register turn-boundary sync hooks with detected agent platforms. Idempotent; `--agent` overrides detection |
| `bdrive skill [install]` | Install the `beardrive` skill into detected agent platforms so the agent can do setup itself. Idempotent; `--agent` overrides detection |
| `bdrive hooks [install\|uninstall]` | Register turn-boundary sync hooks in each detected agent platform's user config — once per machine, covering every folder. Run automatically by `bdrive init`; idempotent; `--agent` overrides detection. `uninstall` removes only BearDrive's own hook entries |
| `bdrive skill [install]` | Install the `beardrive` skill into detected agent platforms so the agent can do setup itself. Run automatically by `bdrive init`; idempotent; `--agent` overrides detection |
| `bdrive hook-approve` | Hook plumbing: answers the beardrive plugin's `PreToolUse` hook, auto-approving bare `bdrive init\|login\|hooks\|status\|sync\|url` so setup costs no permission prompts. Anything with a shell operator is left to the normal prompt |
| `bdrive read-log [folder]` | Hook plumbing: queue agent file reads for the hub's read heatmap. Registered by `bdrive hooks install` |
| `bdrive status [folder]` | Projects, daemon state, pending changes |
| `bdrive log [folder] [-p path] [-n N]` | Change history: account, device, time, file |
@@ -33,18 +34,33 @@ One binary, `bdrive` — the CLI, the sync daemon, and the web server.
### `bdrive init`
The front door. Interactive on a TTY, with survey menus for create-new versus
connect-existing (showing a project list) and whole-folder versus
`--shared <dirs>` (one or more subfolders, repeatable or comma-separated —
`--shared wiki,docs` — which become the include list). Full flag bypass with
`--name`, `--project`, `--shared`, `--yes`, and it never prompts without a TTY.
The front door. **The mount is always exactly the folder you name**
`bdrive init wiki` makes `./wiki` the project, and its contents are the
project's contents. Nothing re-roots a mount somewhere else.
Interactive on a TTY, with survey menus for create-new versus
connect-existing (showing a project list) and whole-folder versus only some
subfolders. To sync part of a folder without moving the mount, use
`--only <dirs>` (comma-separated — `bdrive init . --only wiki,docs`), which
writes a managed block of `.bdriveignore` rules rather than a separate scope
setting. Full flag bypass with `--name`, `--project`, `--only`, `--yes`, and
it never prompts without a TTY.
It runs the login flow first when there is no session, writes
`.bdrive/config.json`, seeds `.bdriveignore`, and starts sync. Re-running it
resumes — including after a folder move.
`.bdrive/config.json`, seeds `.bdriveignore`, installs the `beardrive` skill and
registers agent sync hooks for every detected platform (Claude Code, Codex,
Gemini CLI, Hermes — `--no-hooks` skips the hooks; the skill is installed either way), starts sync, and prints the
project's hub link. That is deliberate: one command means one permission prompt
for an agent, instead of four. Re-running it resumes — including after a folder
move.
Daemon intervals are tunable here: `--scan-interval` (default 3s) and
`--remote-interval` (default 10s).
The hooks land in each platform's **user** config (`~/.claude/settings.json` and
friends), once per machine, so they cover every session in every folder; nothing
is written inside the project. See
[Skills and hooks in detail](/manual/skills-and-hooks/).
The daemon scans every 3s and talks to the hub every 10s; those intervals are
tunable on `bdrive daemon run`, not on init.
### `bdrive sync --note`
@@ -79,11 +95,17 @@ and on every teammate's machine. Nothing is destroyed either: blobs are
retained forever, so the removal shows in `bdrive log` and every past version
stays in the hub's history.
Prune reconciles against `.bdriveignore` only, never against this device's own
sync scope (`bdrive scope` / `init --shared`). Ignore rules are shared; the
scope is per-device, and a narrow scope means "not on my disk", not "not on the
hub". To clean up something your scope excludes, `bdrive forget` it — that
writes the exclusion into the shared rules first, which is what makes it safe.
Prune reconciles against `.bdriveignore`, which is shared — so it refuses
outright when those rules contain `!` scope rules (the "only these folders"
block that `init --only` and `bdrive scope` write). With such a scope, pruning
would mean deleting everything outside it from the hub for every teammate. To
remove a specific path, `bdrive forget` it — that writes the exclusion into the
shared rules first, which is what makes it safe.
Mounts created before the scope moved into `.bdriveignore` may still carry a
per-device `include` list in `.bdrive/config.json`; prune never reconciles
against that, so a narrow legacy scope still means "not on my disk", not "not
on the hub".
If a teammate edits the file between your prune and their next sync, their
version wins and the path comes back. Run `--prune` again once they have synced.
@@ -9,18 +9,20 @@ project.
## `.bdrive/`
The folder's settings directory. `config.json` holds the **stable mount id**
plus project, remote, and include settings.
plus the project and remote (older mounts may also carry a legacy `include`
list — still honored, never written now).
```jsonc
// .bdrive/config.json
{ "id": "m-5a10b713", "volume": "notes",
"remote": "https://drive.example.com/p/p-7f3a2c91", "include": ["/shared/"] }
"remote": "https://drive.example.com/p/p-7f3a2c91" }
```
Written by `bdrive init` and safe to hand-edit — a running daemon picks changes
up automatically. The `include` list (which subfolders sync, set by
`init --shared`) has a friendlier editor: `bdrive scope add`/`rm` from the
mount root.
up automatically. Which subfolders sync is *not* stored here: that lives in
`.bdriveignore` (see below), edited with `bdrive scope add`/`rm`. Mounts created
before that change may still carry an `include` list here; it is still honored,
but nothing writes it any more.
It is **never synced** and holds **no credentials**; the session token stays in
`~/.bdrive`.
@@ -34,6 +36,15 @@ A gitignore-style opt-out list at the mount root. It syncs like a normal file,
so every device shares the same rules. See
[Scoping the folder](/guides/scoping/).
## And nothing else
Those two are all BearDrive puts in a project: `.bdrive/config.json`,
`.bdriveignore`, and your own files. No agent-config directory is ever created
here — the sync hooks live in each platform's user config
(`~/.claude/settings.json`, `~/.codex/hooks.json`, `~/.gemini/settings.json`,
`~/.hermes/config.yaml`), written once per machine. See
[Skills and hooks in detail](/manual/skills-and-hooks/).
## Global state
Everything else lives under `$BDRIVE_HOME` (default `~/.bdrive`):
@@ -75,8 +75,8 @@ container platform works the same way.
1. Open `https://your-hub/` and create your account. Use the signup posture you
configured; hub admins are the `admins` emails.
2. On any machine, `bdrive login https://your-hub`, then in the folder you want
synced: `bdrive init --name wiki --yes` — or `--shared docs` inside a
repository to sync only that subfolder.
synced: `bdrive init --name wiki --yes` — or, inside a repository,
`bdrive init docs --yes` to make that subfolder the project.
3. Invite a teammate: sidebar footer → **Manage****New invite**. The join
link both creates their account and adds them to your org.
4. Connect agents: the project's home page shows one-paste setup for Claude
+12 -10
View File
@@ -35,10 +35,11 @@ Two more commands come with the plugin: **`/beardrive:init`** to start syncing
without the full setup conversation, and **`/beardrive:status`** to diagnose a
sync problem.
:::tip[Project-level hooks reach the whole team]
`/beardrive:install` writes hooks into `.claude/settings.json`, which is
committed with the repository — so **teammates sync whether or not they
installed the plugin**.
:::tip[Hooks are registered once per machine]
`/beardrive:install` writes hooks into your user config (`~/.claude/settings.json`
and friends), so **every session in every folder is covered** — not just the ones
started where you ran setup. Nothing is written into the project, and each
teammate registers their own the first time they set up.
:::
## Codex, Gemini CLI, and Hermes
@@ -49,7 +50,7 @@ and paste:
```
Follow https://raw.githubusercontent.com/runbear-io/beardrive/main/INSTALL_FOR_AGENTS.md
to connect this folder to BearDrive project <project-id> on <hub-url>.
to set up BearDrive project <project-id> on <hub-url>. Ask me which folder to sync.
```
The agent fetches that page and works through it — install the CLI, sign in
@@ -76,11 +77,12 @@ Two things, worth knowing by name:
Codex, Gemini CLI, and Hermes.
- **The hooks** — a blocking pull when you send a message, so the agent always
reads the team's current files, and an async push when the turn ends, so what
it writes reaches everyone else within seconds.
it writes reaches everyone else within seconds. They go in the agent's user
config, once per machine, and are a no-op outside BearDrive folders.
The hooks are what make syncing automatic, and they are the step people skip
when they set up by hand. [Skills and hooks in detail](/manual/skills-and-hooks/)
covers what gets written where.
The hooks are what make syncing automatic. `bdrive init` registers them for you,
so there is nothing extra to run — [skills and hooks in
detail](/manual/skills-and-hooks/) covers what gets written where.
## Check it worked
@@ -89,7 +91,7 @@ Ask the agent — "is BearDrive set up in this folder?" — or look yourself:
```sh
bdrive status # projects, daemon state, pending changes
bdrive skill # which agents know the CLI on this machine
bdrive hooks # which agents sync this project automatically
bdrive hooks # which agents on this machine sync automatically
```
## Next
@@ -27,9 +27,10 @@ Ask your agent, in the repo:
> Set up BearDrive here, syncing only `context/`.
That runs `bdrive init --shared context`, which writes `.bdrive/config.json`,
creates the folder, and starts the daemon. Nothing outside `context/` is
scanned or sent — `.git/` and `.bdrive/` never sync at all.
That runs `bdrive init context`, which makes `./context` the project — creating
the folder if it doesn't exist, writing `context/.bdrive/config.json`, and
starting the daemon. Nothing outside `context/` is scanned or sent — `.git/` and
`.bdrive/` never sync at all.
### Then tell git to ignore it
@@ -123,8 +124,8 @@ and it is also the thing to be deliberate about.
[Turn a personal brain into a company brain](/use-cases/company-brain/) for
how that model works.
- **Opt things out** with `.bdriveignore` — see
[Scoping the folder](/guides/scoping/). It always syncs, even on a
`--shared` mount, so one person's rules apply for the whole team.
[Scoping the folder](/guides/scoping/). It always syncs, so one person's rules
apply for the whole team.
- **Watch what actually gets read.** [Read heat](/guides/what-agents-read/)
shows which context pages agents consume. Pages nothing has read in a month
are candidates for deletion, and deleting them makes the rest work better.
@@ -32,8 +32,9 @@ Same as any project — [ask your agent](/start/setup/), in the brain folder:
> Set up BearDrive here and invite my team.
If the brain is a subfolder of something larger, sync just that subfolder:
`bdrive init --shared brain/`. Teammates mount the same project on their own
If the brain is a subfolder of something larger, name it and only it:
`bdrive init brain` — the folder you name is the project, and nothing outside it
is scanned. Teammates mount the same project on their own
machines, wherever they like it on disk, and keep running their own local brain
against the synced files.
@@ -26,13 +26,15 @@ The folder does not have to match across machines. State is keyed by a stable
project id, never a path, so `~/work` on one and `~/Documents/work` on another
are the same project. Moving or renaming a folder later is free.
Install the skill and hooks **once per machine**, and the hooks once per project
— that is what makes each agent pull before it answers. See
The skill and the hooks are installed **once per machine**`bdrive init` does
both on each new device — and from then on they cover every folder that machine
syncs. That is what makes each agent pull before it answers. See
[skills and hooks in detail](/manual/skills-and-hooks/).
## More than one agent per machine
The skill is a cross-agent format and the hooks are written per platform, so
The skill is a cross-agent format and the hooks are written into each platform's
own user config, so
Claude Code, Codex, Gemini CLI, and Hermes can all be wired into the same folder
at once. They read the same files and their writes are stamped with which agent
session made them, so `bdrive log` and the hub's history stay legible even when
@@ -51,8 +53,8 @@ mechanism, just with a longer window.
## What matters for this case
- **[Skills and hooks in detail](/manual/skills-and-hooks/)** — what to run on
each new machine, and why the pull hook is the one that matters.
- **[Skills and hooks in detail](/manual/skills-and-hooks/)** — what each new
machine registers, and why the pull hook is the one that matters.
- **[How sync works](/concepts/how-it-works/)** — journals, blobs, and
deterministic replay, if you want to know why this converges rather than
hoping it does.