Field report follow-up: an agent with a stale skill copy couldn't find
the gated URL after creating a file. Instructions rot; hook output is
computed fresh from the binary every turn. Claude Code's turn-start
pull hook now runs 'bdrive sync --hook claude-code', which:
- pulls as before, and stamps the session note from the event JSON
(replacing the sh/sed pipeline for the pull leg)
- emits the project's gated-link formula as UserPromptSubmit
additionalContext: whenever the agent mentions a synced file path in
prose, it appends the hub link on an emoji — `<path>` [🔗](<url>) —
path plain (it's the local path), hyperlink on the emoji only; code
blocks stay plain; bdrive share stays explicit-opt-in-public
Blind-tested: an agent given only the injected context decorated every
path mention correctly, kept the code-block command plain, and checked
files were synced before linking.
- hooks install now CONVERGES marker-identified groups to the current
shape (command/matcher/flags), so improvements reach existing
projects on reinstall instead of being frozen by the idempotency
marker; hermes same
- plugin: UserPromptSubmit → beardrive-pull.sh (stdout passes through);
version 0.3.0
- SKILL 'Share what you make' generalized to 'Link what you mention'
(URL formula documented for non-Claude platforms); install.md pointer
template updated
Never fails the turn: every error path in --hook mode is a silent
successful exit; offline still emits (links serve online teammates).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt
Field report: an agent session read a dozen-plus files via Bash
(grep/cat/tail/find) and Grep, but read heat showed almost nothing —
the read-log hook only matched the native Read tool.
- matchers broadened per platform: claude Read|Grep|Bash, codex
read_file|shell, gemini +search_file_content|run_shell_command,
hermes read_file|grep|bash; plugin hooks.json matches Read|Grep|Bash
- read-log is now tool-aware: shell events mine the command line for
existing files it names (redirect targets and flags excluded), grep
events mine the response for the files the matches came from
(content lines and filename lists), and listing tools (Glob, ls) are
deliberately ignored — seeing a file's name is not reading it
- hooks install upgrades a registered hook's stale matcher in place, so
re-running it after a binary upgrade rolls coverage out to existing
projects instead of being skipped by the idempotency marker
- docs: SKILL.md platform table + read-heat wording, install/init
commands, README command table
Note from the same report, verified not a bug: read-log resolves the
mount via the folder's own .bdrive/config.json (config.ResolveMount),
so a stale duplicate registry entry cannot swallow reads.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt
Admin/org-owner Insights view (⋯ menu): dependency-free SVG scatter of
every file by 30-day reads × days since last change, log scales, with the
hot-but-stale danger quadrant shaded and a ranked fix-these-first list;
lens toggle for all/human/agent reads. The Claude Code plugin gains a
PostToolUse(Read) hook so plugin users feed agent-read telemetry without
project-level hook registration. Docs synced: README, SKILL.md,
plugin install/init commands, CLAUDE.md, design doc marked implemented.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt
`bdrive sync --note <text>` stamps session context onto every op the cycle
commits and persists it in the volume store (note.json, --note-ttl default
30m) so the daemon's own scans stamp it too — winning the race between
one-shot hook syncs and the 3s daemon scan. The plugin sync hook extracts
session_id from hook stdin JSON and passes it automatically, so history
links every change to the Claude Code session that made it. Conflict-copy
ops keep their own note; expired/cleared notes stop applying.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P5cxPQdSGJnjXCYY9GeWXt
Share links (bdrive share <file>, or the web UI's Share button):
- Mint an unguessable public URL (/s/<token>) anyone can open — no
account. HTML renders as a page, markdown gets a standalone shell,
PDFs open inline; ?download=1 attaches.
- Sandboxed: /s/* responses carry CSP `sandbox allow-scripts` + nosniff
and never see auth cookies, so shared content's scripts run in an
opaque origin and can't touch hub sessions.
- Links serve the file's LATEST synced content and live until revoked;
--expires makes self-destructing ones; --list/--revoke manage them.
Re-sharing a file returns the same link. File-backed shares.json.
- CLI resolves the project by walking up to .bdrive/ from the shared
file, warns when the hub address is private (LAN-only links), and
hints when the file hasn't synced yet.
/beardrive:install (plugin command) — team onboarding driven by Claude:
- Ensures the bdrive binary, signs in (bdrive login), runs bdrive init
(whole folder or a shared subfolder like wiki/).
- Asks before appending a CLAUDE.md section that teaches agents to put
shareable artifacts in the shared folder and mint URLs with bdrive
share; asks before registering project-level hooks in
.claude/settings.json: blocking pull at UserPromptSubmit, async push
on PostToolUse Write|Edit — teammates sync with or without the plugin.
- Fix: the plugin hook script still checked for the old `.bdrive` file
and was a silent no-op since the directory change; now checks -d.
Tests: share creation gating, public access + sandbox headers, dedupe,
latest-content semantics, revoke, expiry, markdown/download variants,
list filtering, registry persistence. Docs updated (README sharing +
Claude Code sections, SKILL.md, CLAUDE.md).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7Q9ZKSZRTdvrSJkYLUmYs
The web server (bdrive web) becomes a full sync hub, and client devices
get one-command onboarding — without ever seeing storage info or holding
cloud credentials:
- bdrive web -c config.json: server configurable from a JSON file
(remote/addr/upload/upload_ttl/projects_db); explicit flags win.
- Hub mode: pointing bdrive web at a storage root hosts many projects,
each under <root>/<project-id>/ (remote.Prefixed). Projects live in a
file-backed registry (projects.json — loaded at open, rewritten
atomically per change) with create-or-join-by-name semantics.
- Per-project APIs: /api/projects (list/create/get) and
/api/p/<id>/{tree,file,render,download,upload/*,store/*}. The web UI
grows a project list with per-project browsing and hash deep links.
- Browser uploads and a store proxy for syncing devices: presigned
direct-to-storage PUTs when the backend can sign (S3 presign, GCS V4
signed URLs; expiring, credential-free), relayed through the server
otherwise. Journals are never presigned — only immutable blobs.
Blobs-before-journal and one-writer-per-journal invariants hold.
- https:// remote backend: a device syncs one hub project through
/api/p/<id>/store/* — mnt/sync/daemon/log all work unchanged.
- bdrive login <url>: verify a hub and remember it as the device default
(settings.json). bdrive init: create-or-join a project named after the
folder (--name/--project override), write .bdrive, seed a starter
.bdriveignore, mount, and start the daemon — one command per project.
- Hard-break rename: .beardrive->.bdrive, .beardriveignore->.bdriveignore,
~/.beardrive->~/.bdrive, BEARDRIVE_HOME->BDRIVE_HOME, temp/conflict
prefixes; old names are no longer read.
- Tests: presigning, project registry persistence, store API validation
and gating, project isolation over live HTTP, browser upload flows, and
two-device convergence through a hub (incl. read-only pull-only mode).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01R7Q9ZKSZRTdvrSJkYLUmYs
Product and project are BearDrive; the CLI binary is bdrive (bdrive mnt,
bdrive sync, ...), the web viewer is bdrive-web. All conventions follow
the full name: .beardrive settings file, .beardriveignore, ~/.beardrive
home, BEARDRIVE_HOME, .beardrive-conflict-* / .beardrive-tmp-* files.
Plugin/skill/marketplace renamed to beardrive (/beardrive:mount).
Module path is now github.com/runbear-io/beardrive — merge only after
renaming the GitHub repo, or go install breaks.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHEUaYfFHhmDvqLYw74Ehz
- sfs-web (cmd/sfs-web, internal/webapp): read-only Obsidian-style web UI
serving a local folder (default) or an sfs remote; markdown rendering
with [[wikilinks]], task lists and tables, file downloads with ETags,
per-file provenance from the journals; added to goreleaser builds
- .sfs project file (internal/config): per-folder volume/remote/include
settings that travel with the folder, win over the global registry, and
never sync; daemon picks up edits live
- .sfsignore + include lists (internal/syncer): gitignore-style selective
sync with ! re-includes, applied symmetrically in scan and materialize;
newly ignored files stop syncing without being deleted anywhere
- Claude Code plugin (plugin/, .claude-plugin/): sfs skill, /sfs:mount and
/sfs:status commands, turn-boundary sync hooks (blocking pull on prompt,
async push on stop); installable via the repo's marketplace manifest
- CLAUDE.md and .claude project settings for Claude Code development
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HHEUaYfFHhmDvqLYw74Ehz