diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 4c6fe24..55bd1e8 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,14 +1,24 @@ { "name": "beardrive", - "owner": { "name": "runbear", "email": "snow@runbear.io" }, - "description": "BearDrive (beardrive) — a synced file system for AI agents", + "owner": { + "name": "runbear", + "email": "snow@runbear.io" + }, + "description": "BearDrive — the open-source Google Drive for AI agents: one folder your team and their agents share, with per-change attribution and read analytics.", "plugins": [ { "name": "beardrive", "source": "./plugin", - "description": "BearDrive: folders that stay in sync across your team and agents through a BearDrive server (or S3/GCS directly). Installs the beardrive skill, /beardrive:install, /beardrive:init and /beardrive:status commands, and turn-boundary sync hooks in one step.", + "description": "BearDrive: folders that stay in sync across your team and agents through a self-hostable BearDrive hub, with per-file history and public share links. Installs the beardrive skill, /beardrive:install, /beardrive:init and /beardrive:status commands, and turn-boundary sync hooks in one step.", "category": "workflow", - "tags": ["sync", "files", "workspace", "s3", "gcs", "agents"] + "tags": [ + "sync", + "files", + "workspace", + "agents", + "memory", + "claude-code" + ] } ] } diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..6a85838 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,47 @@ +name: Bug report +description: Something misbehaved — a sync, the CLI, the hub, or the web UI +labels: [bug] +body: + - type: input + id: version + attributes: + label: bdrive version + description: Output of `bdrive version` + placeholder: beardrive v0.7.0 + validations: + required: true + - type: input + id: os + attributes: + label: OS / platform + placeholder: macOS 15.2 (arm64) / Ubuntu 24.04 + validations: + required: true + - type: dropdown + id: mode + attributes: + label: Where does it happen? + options: + - Syncing client (bdrive CLI / daemon) + - Self-hosted hub (bdrive web) + - Web UI in the browser + - Claude Code plugin / agent hooks + - Not sure + validations: + required: true + - type: textarea + id: repro + attributes: + label: What happened, and how do we reproduce it? + description: | + Steps, expected vs actual. For sync issues, `bdrive status` and + `bdrive log -n 20` output help a lot. Never paste tokens or the + contents of settings.json. + validations: + required: true + - type: textarea + id: logs + attributes: + label: Logs (optional) + description: Daemon log lives at `~/.bdrive/volumes//daemon.log`. + render: text diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..a533d06 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,27 @@ +name: Feature request +description: An idea for BearDrive — check ROADMAP.md first, it might already be planned +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: What are you trying to do? + description: The workflow or problem, not just the feature — context makes better designs. + validations: + required: true + - type: textarea + id: proposal + attributes: + label: What would you like BearDrive to do? + validations: + required: true + - type: dropdown + id: area + attributes: + label: Area + options: + - Sync engine / CLI + - Hub / web UI + - Agent integration (hooks, plugin, skills) + - Sharing / permissions + - Other diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..de45369 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,10 @@ +## What & why + + + +## Checklist + +- [ ] `go build ./... && go vet ./... && go test ./...` green +- [ ] Sync behavior changes have a multi-device test in `internal/syncer` +- [ ] Frontend changes: `npm run build` re-committed `internal/webapp/static` and `npm run e2e` is green +- [ ] CLI behavior changes updated both `README.md` and `plugin/skills/beardrive/SKILL.md` diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..10525af --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,69 @@ +# Changelog + +Notable changes per release. Format loosely follows +[Keep a Changelog](https://keepachangelog.com/); BearDrive is pre-1.0, so +minor versions may ship breaking changes (see [SemVer §4](https://semver.org/#spec-item-4)). + +## v0.7.0 — 2026-07-14 + +- **`bdrive url `** — internal, permission-walled links (sign-in + + project membership required) that agents share when they create files; + the plugin now teaches agents to include the link in their reply. +- Mobile layout overhaul: responsive chrome now covers tablet and phone + landscape, 44px touch targets throughout, five designer-review rounds. +- Read-heat hooks re-registered on upgrade pick up broader matchers. + +## v0.6.0 — 2026-07-13 + +- **Web UI rewritten in React + TypeScript** (same URLs, same design): + committed build output keeps `go build`/`go install` Node-free. +- **Read-heat coverage fix**: agent reads via shell commands (`cat`, + `grep`, `tail`) and Grep matches now count, not just native file reads; + `bdrive hooks install` upgrades existing hook matchers in place. +- Content-hashed assets served immutable; committed e2e harness + + 42-spec Playwright suite. + +## v0.5.0 — 2026-07-12 + +- **Project home page**: connect-an-agent guide (Claude Code & Cowork + plugin flow, Hermes/Codex CLI) with real hub URL + project id filled + in; Insights embedded for admins/org owners. +- Two-file AGENTS.md orientation for shared folders in the plugin flows. +- Expandable history notes; RESTful `/insights` and `/history` routes. + +## v0.4.0 — 2026-07-12 + +- **Read heat / Insights**: per-file read telemetry (human vs agent vs + share), heat dots in listings, and the Insights dashboard — treemap, + reads×staleness scatter with the hot-but-stale danger quadrant, hot + path, per-agent coverage matrix. +- **Agent read reporting**: `bdrive read-log` + hooks spool agent file + reads locally and report on next sync. + +## v0.3.1 — 2026-07-10 + +- Parallel blob upload + progress bar for large initial imports. + +## v0.3.0 — 2026-07-10 + +- **Hub-only architecture**: clients sync exclusively through a + `bdrive web` hub over HTTPS (the `remote` command and direct + client-to-bucket sync were removed); `bdrive logout` added. +- **SQL metadata backends**: hub accounts/projects/orgs/shares can live + in SQLite or Postgres (incl. Supabase) instead of JSON files. +- Dockerfile + Cloud Run deployment recipe. + +## v0.2.2 — 2026-07-08 + +- **BearDrive**: the project (formerly `sfs`) got its name; CLI became + `bdrive`. +- Multi-project sync hub with accounts and orgs; interactive + `bdrive init` / browser `bdrive login` onboarding; public share links; + web viewer folded into the CLI as `bdrive web`; per-file history in + the web UI; `/beardrive:install` team onboarding for Claude Code. + +## v0.1.0 — 2026-06-12 + +- First release: per-device append-only journals, last-writer-wins + replay, content-addressed blobs, offline-first sync through S3/GCS/ + file remotes, conflict copies, daemon with turn-boundary agent hooks. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fa55ae9 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# Contributing to BearDrive + +Thanks for wanting to help. BearDrive is early (pre-1.0) and moving fast — +small, focused PRs land quickest, and an issue or discussion before a big +change saves everyone time. + +## Build & test + +```sh +go build ./... # everything, no CGO, no Node needed +go vet ./... +go test ./... # full suite +go test ./internal/syncer -run TestConflict -v # one test +``` + +Set `BDRIVE_HOME=/some/tmp/dir` when testing the CLI by hand so you never +touch your real `~/.bdrive`. + +## Run a local hub + +```sh +go build -o bdrive ./cmd/bdrive +mkdir -p /tmp/hub-storage +./bdrive web /tmp/hub-storage --addr :8080 --upload # plain-folder viewer +``` + +For hub mode with accounts, see the self-hosting guide +([docs/self-hosting.md](docs/self-hosting.md)). The e2e test harness +(`BDRIVE_E2E_SERVE=1 go test -run TestE2EServe ./internal/webapp`) starts a +seeded hub on :8993 with test accounts — handy for frontend work. + +## The rules that matter here + +- **Sync changes need multi-device tests.** The real coverage lives in + `internal/syncer/syncer_test.go`: simulated devices syncing through a + shared remote, driven cycle by cycle. A new sync behavior without a + multi-device test is untested where it matters. +- **Never break sync.** Errors degrade to offline and retry next cycle; + a cycle must not fail because a side feature (telemetry, hooks) did. + Read the invariants section in [CLAUDE.md](CLAUDE.md) before touching + `internal/syncer`, `internal/journal`, or `internal/store` — replay + determinism and journal ownership are the whole concurrency story. +- **Frontend changes rebuild the committed assets.** The web UI lives in + `internal/webapp/frontend` (React + TS, Vite); its build output is + committed at `internal/webapp/static` so `go build` needs no Node. + After changing `frontend/src`: `npm run build`, commit the new + `static/`, and keep `npm run e2e` green. `frontend/check-dist.sh` + verifies freshness. +- **Docs travel with behavior.** Changing CLI commands, flags, or output + means updating both `README.md` and `plugin/skills/beardrive/SKILL.md` + — the skill is what makes agents beardrive-aware and must match the + binary. + +## Where to start + +[ROADMAP.md](ROADMAP.md) marks items we'd love help with, and issues +labeled `good first issue` / `help wanted` are curated to be approachable. +Bug reports with a reproduction (the issue form asks for `bdrive version`, +OS, and hub vs plain-folder mode) are gold. + +## Conduct + +Be kind, be direct, assume good intent. Maintainers reserve the right to +moderate. Security issues: email snow@runbear.io rather than opening a +public issue. diff --git a/README.md b/README.md index 5b4b57b..182f4ed 100644 --- a/README.md +++ b/README.md @@ -7,25 +7,38 @@ everything keeps working offline. The CLI is `bdrive`; a hub is a `bdrive web` server you (or we) run on an object store — clients sync through it over HTTPS and never touch the storage directly. -Two things it's for: **sharing files with people** — any synced file -becomes a public URL that renders as a page — and **sharing context across -AI agents**: give every agent on the team the same folder as memory, and -your agent knows what their agent knows. Notes, plans, findings, and -artifacts follow the team everywhere, with a full audit trail of which -agent or human changed what. +What it's for, first and foremost: **sharing context across AI agents** — +give every agent on the team the same folder as memory, and your agent +knows what their agent knows. (People are covered too: any synced file +becomes a public URL that renders as a page.) Notes, plans, findings, and +artifacts follow the team everywhere — and unlike a memory API, they stay +**real files with provenance**: every change is attributed to the human, +agent, and device that made it, and the hub's Insights show what your +agents actually read (and which hot-but-stale docs nobody maintains). + +

+ Knowledge Insights — every file plotted by agent/human reads vs staleness; hot-but-stale docs are the danger zone +

+ +| Browse with read heat | Public share pages | +|---|---| +| ![Folder listing with per-file agent read counts and change feed](docs/assets/browse.png) | ![A shared markdown file rendered as a public page](docs/assets/share.png) | ```console -$ bdrive login # once per device (browser sign-in) +$ bdrive login https://your-hub # once per device — self-host a hub in ~10 min (docs/self-hosting.md) $ cd ~/workspace && bdrive init initialized /Users/snow/workspace project: workspace (p-7f3a2c91) daemon: running (pid 55434, scan 3s, remote sync 10s) ``` +> BearDrive Cloud — zero-setup, bare `bdrive login` — is on the waitlist +> at [beardrive.ai](https://beardrive.ai). Self-host to try it today. + On another machine: ```console -$ bdrive login && cd ~/workspace && bdrive init +$ bdrive login https://your-hub && cd ~/workspace && bdrive init # … connect the same project; the files appear and stay in sync ``` @@ -72,9 +85,11 @@ go install github.com/runbear-io/beardrive/cmd/bdrive@latest ## Quick start ```sh -# 1. Sign this device in (once). Default server: beardrive.ai; -# self-hosters pass their own URL. -bdrive login +# 1. Sign this device in against your hub (once per device). +# Self-host a hub in ~10 minutes (docs/self-hosting.md), then: +bdrive login https://your-hub +# (BearDrive Cloud — zero-setup, bare `bdrive login` — is coming; +# join the waitlist at beardrive.ai. Self-host to try it today.) # 2. Start syncing a project — interactive: create or connect a project, # sync the whole folder or just ./shared. Re-run any time to resume. @@ -83,7 +98,7 @@ cd ~/my-project && bdrive init # 3. Work normally — create, edit, delete files with any tool. echo "remember this" > memory.md -# On every other device: bdrive login once, then bdrive init in a folder +# On every other device: `bdrive login https://your-hub` once, then bdrive init in a folder # and connect the same project. # See what changed, who changed it, and from which device @@ -103,7 +118,10 @@ exactly where it left off — zero re-scan, zero spurious changes. ### Credentials -beardrive uses each provider's standard credential chain — nothing beardrive-specific: +beardrive uses each provider's standard credential chain — nothing beardrive-specific. +Note: **client devices always use an `https://` hub remote** — the +`s3`/`gs`/`file` rows below are how the *hub operator* configures the +hub's own storage, never something a syncing client points at directly: | Remote | Credentials | |---|---| @@ -116,7 +134,7 @@ beardrive uses each provider's standard credential chain — nothing beardrive-s | Command | Description | |---|---| -| `bdrive login [server-url]` | Sign this device in (browser flow; `--device` for headless; default server beardrive.ai). Switch hubs with `bdrive login ` | +| `bdrive login [server-url]` | Sign this device in (browser flow; `--device` for headless; default server beardrive.ai — the managed cloud, waitlist-only: pass your hub URL). Switch hubs with `bdrive login ` | | `bdrive logout` | Sign this device out — clear the saved token/account (`--forget` also drops the remembered server) | | `bdrive init [folder]` | Create/connect a project and start syncing — interactive on a TTY, flags (`--name/--project/--shared/--yes`) for scripts; re-run to resume | | `bdrive stop [folder]` | Stop syncing (files stay; `bdrive init` resumes) | @@ -241,7 +259,8 @@ cd ~/some-project && bdrive init # once per project ``` `bdrive login` signs the device in and remembers the server (`settings.json` -under the bdrive home; bare `bdrive login` defaults to beardrive.ai — +under the bdrive home; bare `bdrive login` defaults to beardrive.ai — the +managed cloud, currently waitlist-only, so pass your hub's URL — `--status` shows the current server and account). To move to a **different hub**, run `bdrive login ` and then re-run `bdrive init` in each folder to connect it to a project there; `bdrive logout` signs out entirely. @@ -347,77 +366,18 @@ distinct-reader counts, and last-read times — never who read what; `?by=device` adds the agent-only per-device folder breakdown (device identity is already public via history; human emails never appear). -### Authentication +### Authentication & database -Hubs always require sign-in — every change is attributed to a real account. -The whole API — web UI, uploads, project creation, device sync — needs a -session; only `/api/config` and the auth pages stay open (the plain-folder -viewer, `bdrive web ./folder`, remains auth-free). Accounts are -email + password + name, kept in a file-backed registry (`auth.json`: -bcrypt password hashes and SHA-256 token digests, atomically rewritten — -no plaintext credentials ever touch disk). +Hubs always require sign-in — every change is attributed to a real +account. **Signup is invite-only by default** (the safe posture for a +public URL); self-service signup opens only with a gate (admin approval, +or allowed domains + email verification). Hub metadata (accounts, +projects, orgs, shares) lives in a file-backed store by default, or +SQLite/Postgres (incl. Supabase) via the `database` config block. -**Signup is invite-only by default** — the safe posture for a hub on a -public URL. New people get in only through an expiring invite link an owner -mints; the link lets them create an account (bypassing the gates below) and -join, in one step. To allow self-service signup instead, set -`"allow_signup": true` **with a gate** — the server refuses to start an open -hub that has none, so a fake email can never just walk in. Three postures: +Full reference — the three signup postures, SMTP, admins, CLI device +sign-in, and database selection: **[docs/self-hosting.md](docs/self-hosting.md)**. -- **Invite-only** (default): `allow_signup` unset/false. Only invite links create accounts. -- **Approval-gated**: `allow_signup: true` + `require_approval: true` — anyone can sign up, but a hub admin approves each new account before it works (no SMTP needed). -- **Domain-restricted + verified**: `allow_signup: true` + `allowed_domains: ["you.com"]` + `require_verification: true` (needs `smtp`) — only your company's addresses may sign up, each confirming an emailed link. Verification without SMTP is refused (the link would otherwise only reach the server log). - -Admins tune verification/approval live from the web UI (**Admin → Signup & -access**); `allowed_domains`, the admin list, and `allow_signup` are -server-config-owned so a browser session can never widen who gets in. - -`bdrive login ` on a client device opens the server's sign-in page in -a browser (sign up right there if needed); when the user signs in, the -page bounces a one-time code to the CLI's loopback listener and the -terminal finishes on its own, storing a long-lived per-device token -(revocable server-side). On headless/SSH machines, `bdrive login --device` -prints a short code to approve from any signed-in browser instead. Every -sync and every `bdrive init` then authenticates with that token. - -"Forgot password" emails a one-hour reset link via the `auth.smtp` block — -plain SMTP, so any provider works. With no SMTP configured, the link is -printed to the server log so an admin can hand it over; reset is never -fully broken. - -Two notes: put a hub behind TLS (reverse proxy -or tailscale) — `bdrive login` warns when signing in over plain http to a -non-localhost address. Internally all of this sits behind an -`AuthProvider` interface; the open-source server ships the built-in -email/password provider, and alternative identity backends can be swapped -in without touching the CLI or the API. - -### Choosing a database - -A hub keeps a little **metadata** — accounts, projects, orgs, invites, -shares, devices — separate from your files. (File content and the sync -journals always live in the object store; the database never holds them.) -You choose where that metadata lives with the `database` block: - -```jsonc -"database": { "driver": "file" } // default — JSON under BDRIVE_HOME -"database": { "driver": "sqlite", "dsn": "/var/lib/bdrive/hub.db" } -"database": { "driver": "postgres", "dsn": "postgres://…@…pooler.supabase.com:6543/postgres" } -``` - -- **file** (default): zero dependencies, human-readable JSON, perfect for a - laptop or a small self-hosted hub. -- **sqlite**: one embedded database file — a real DB locally with no server - to run. -- **postgres**: a managed Postgres such as **Supabase** for production — - just point `dsn` at its connection string (use the transaction pooler for - many connections). Since Supabase *is* Postgres, this stays fully - open-source with no managed-only lock-in. - -`file` and `sqlite` are single-writer (run one hub instance); Postgres is -transactional and can back more than one instance. Switching backends -doesn't migrate existing data — pick one when you set the hub up. Both SQL -drivers are pure Go, so the binary stays a CGO-free static build. ### Uploads @@ -507,14 +467,19 @@ deletes for it), and anything excluded by `.bdriveignore` or omitted from an ## Roadmap -- `beardrive restore @