Native plugin between ByteRover and Claude Code. Enriches Claude's auto-memory with ByteRover's full multi-tier retrieval — BM25 search, importance scoring, performance correlation, and LLM synthesis — giving Claude persistent, cross-referenced context that improves over time.
Claude Code has a built-in auto-memory system — it saves observations across sessions as flat markdown files. ByteRover has a richer context tree with multi-tier retrieval: BM25 text search, importance/recency scoring, maturity tier boosting, performance-memory correlation, and LLM-powered synthesis. This plugin connects the two:
- Ingesting memories — when Claude's extraction agent writes a memory file, a hook fires and sends the content to
brv curate, which enriches it with tags, keywords, scoring metadata, and stores it in the context tree - Syncing context — after each turn, a hook queries ByteRover for ranked knowledge and writes a cross-reference file that Claude's recall system can pick up
- Visible activation — every prompt that retrieves curated knowledge gets a one-line
🧠 ByteRover returns …summary in the chat showing which memories were used. An opt-in status line at the bottom of Claude Code surfaces daemon activity (idle / curating / dreaming) so you can see when the context tree is being updated in the background - Zero workflow change — you use
claudeexactly as before. The plugin runs in the background via Claude Code's hook system
The result: Claude's memories are indexed, scored, and searchable alongside the rest of your project knowledge in ByteRover — and you can see the system working as you go.
- Claude Code installed
- Node.js 20+
- brv CLI installed and available on your
PATH
npm install -g @byterover/claude-pluginbrv-claude-plugin installThis adds four hooks to ~/.claude/settings.json:
- PostToolUse(Write) — triggers
brv-claude-plugin ingestwhen Claude writes a memory file - PostToolUse(Edit) — triggers
brv-claude-plugin ingestwhen Claude edits a memory file - Stop — triggers
brv-claude-plugin syncafter each turn to refresh cross-references - UserPromptSubmit — triggers
brv-claude-plugin recallto query ByteRover with the user's prompt and inject relevant context before the model call
Your existing settings and hooks are preserved. A backup is saved to settings.json.brv-backup.
brv-claude-plugin install-statuslineAdds a statusLine entry to ~/.claude/settings.json that renders one of three states at the bottom of Claude Code:
🧠 ByteRover · idle (dim gray — no daemon work in progress)
🧠 ByteRover · 📝 curating (yellow — a curate task is running)
🧠 ByteRover · 💭 dreaming (cyan — a dream consolidation cycle is running)
The line is hidden when you're not in a brv-initialized project. Refreshes every 5 seconds in addition to Claude Code's event-driven triggers, so it picks up daemon state changes that happen while the assistant is mid-tool-call.
If you already have a custom statusLine configured, the installer prompts you (in a TTY) to keep it, replace it, or abort. Pass --force to overwrite without prompting; non-interactive shells fail-fast with a hint to use --force.
The status line is opt-in and removable via brv-claude-plugin uninstall-statusline (leaves foreign user statuslines alone) or as part of the full brv-claude-plugin uninstall.
brv-claude-plugin doctorYou should see all checks passing:
brv-claude-plugin doctor
✓ brv CLI — byterover-cli/3.10.3 darwin-arm64 node-v22.21.1
✓ Context tree — /path/to/project/.brv/context-tree
✓ Claude settings — ~/.claude/settings.json
✓ Bridge hooks — PostToolUse + Stop + UserPromptSubmit hooks found
✓ Bridge executable — /usr/local/bin/brv-claude-plugin
✓ Status line — registered, points to our binary
✓ Memory directory — ~/.claude/projects/-path-to-project/memory/
All checks passed.
The Status line check is informational — it shows ✓ when our status line is registered and ✗ otherwise (with a hint to run install-statusline), but a missing status line does not flip the overall outcome since it's opt-in.
claude "fix the auth bug"Memories are ingested into .brv/context-tree/ automatically. No changes to your workflow.
brv-claude-plugin uninstallRemoves plugin hooks and our status line entry (if installed) in a single pass. Your other hooks, settings, and any foreign statusLine entry are untouched. To remove only the status line and keep the hooks, run brv-claude-plugin uninstall-statusline instead.
Installs hooks into Claude Code's ~/.claude/settings.json.
| Option | Description |
|---|---|
--dry-run |
Show what would be written without saving |
--settings-path |
Override path to Claude Code settings.json |
Idempotent — safe to run multiple times. Deduplicates by checking for the #brv-claude-plugin marker in existing hooks.
Removes plugin hooks and the plugin's status line entry from settings. Per-hook removal — if a matcher entry contains both a plugin hook and your own hook, only the plugin hook is deleted. Foreign user statusLine entries are left intact.
| Option | Description |
|---|---|
--settings-path |
Override path to Claude Code settings.json |
Installs an opt-in statusLine entry into ~/.claude/settings.json that surfaces daemon activity at the bottom of Claude Code. Idempotent. Backs up settings.json before writing.
If a statusLine is already configured:
- Carries our
#brv-claude-pluginmarker: idempotent — re-runs to upgrade fields if our shape has drifted (e.g., newrefreshIntervalvalue) - Foreign (someone else's): prompts on TTY (keep / replace / abort, default abort); fails fast on non-TTY with instructions to use
--force
| Option | Description |
|---|---|
--dry-run |
Show what would be written without saving |
--force |
Overwrite an existing foreign statusLine without prompting |
--settings-path |
Override path to Claude Code settings.json |
Removes only the plugin's statusLine entry. Marker-gated — leaves foreign user statuslines alone.
| Option | Description |
|---|---|
--settings-path |
Override path to Claude Code settings.json |
Called by the statusLine command in Claude Code's settings. Reads CC's status payload from stdin, walks up from the current directory to find .brv/, classifies daemon state via filesystem inspection, and prints one ANSI-colored line.
- Outputs nothing (line hidden) when no
.brv/is reachable - Inspects
.brv/dream-log/,.brv/dream.lock, and the daemon's per-project storage directory under the platform's user-data dir forcurate-log/ - Always exits 0; runs in well under 100ms in normal cases
Called by the PostToolUse hook. Reads hook JSON from stdin, parses the memory file, and sends it to brv curate --detach.
- Write tool: reads content from
tool_input.content - Edit tool: reads the file from disk (Edit only carries
old_string/new_string) - Skips non-memory files,
MEMORY.md, and_brv_context.md - Always exits 0 — never blocks Claude
| Option | Description |
|---|---|
--json |
Output result as JSON |
Called by the Stop hook. Copies the context tree index from .brv/context-tree/_index.md into _brv_context.md in Claude's memory directory with a single pointer in MEMORY.md.
- On success: writes context tree index with a guide note pointing to the full tree
- If
_index.mdunavailable but_brv_context.mdexists: preserves existing content - If
_index.mdunavailable and no existing file: writes a stub with "(sync pending)" - Runs async in the background — never blocks Claude
| Option | Description |
|---|---|
--json |
Output result as JSON |
--memory-dir |
Override path to Claude memory directory |
Called by the UserPromptSubmit hook. Reads the user's prompt from stdin, queries ByteRover with it, and emits two things to Claude Code:
additionalContext— the retrieved context wrapped in<byterover-context>tags, injected before the model call so the response can use itsystemMessage— a one-line🧠 ByteRover returns N memories: <path1> (3d ago), <path2> (1w ago), …summary visible in the chat above Claude's response (capped at 3 paths, with humanized age suffixes)
Behavior:
- Runs synchronously — delays the model call until ByteRover responds (6s internal timeout, 8s hook timeout)
- Skips trivially short prompts (< 5 chars)
- Per-entry score filter (≥0.5) trims low-relevance hits from the visible summary; if no qualifying entries exist, the summary line is suppressed entirely
- On cache hits or older
brvversions that don't return structuredmatchedDocs, falls back to regex-parsing the**Sources**:block at the end of the response prose - If ByteRover query fails or times out, exits silently — prompt proceeds without context
Runs 7 diagnostic checks: brv CLI, context tree, settings file, installed hooks, plugin executable, status line registration, and memory directory resolution. The status line entry is informational — its absence does not flip the overall outcome since it's opt-in.
The plugin uses Claude Code's hook system to intercept memory operations without modifying Claude Code's source.
User types: "fix the combo scoring bug"
│
▼ UserPromptSubmit hook fires (before model call)
brv-claude-plugin recall
│ brv query "fix the combo scoring bug" → targeted results
▼
• Claude sees <byterover-context> as system reminder
(live, relevant to this specific prompt)
• A one-line `🧠 ByteRover returns N memories: <path>(3d ago), …`
summary is shown in the chat above the response (capped at 3 paths)
The visible summary is built from the structured matchedDocs returned by brv query --format json when available; on cache hits or older brv versions the plugin falls back to parsing the **Sources**: block at the end of the response prose. Per-entry score filtering (≥0.5) keeps the line concise — if no qualifying entries exist, the line is suppressed entirely.
Claude writes ~/.claude/projects/.../memory/feedback_testing.md
│
▼ PostToolUse hook fires, pipes JSON to stdin
brv-claude-plugin ingest
│ Parse cc-ts frontmatter (name, description, type)
│ Map type → domain (/user, /feedback, /project, /reference)
▼
brv curate --detach --format json -- "context string"
│ Daemon queues curation (async, non-blocking)
▼
.brv/context-tree/feedback/testing.md
(enriched with tags, keywords, importance scoring)
Turn ends
│
▼ Stop hook fires
brv-claude-plugin sync
│ read .brv/context-tree/_index.md → copy to memory dir
▼
~/.claude/projects/.../memory/_brv_context.md
(context tree index + guide to full tree)
The status line uses the same statusLine mechanism Claude Code documents — it runs the configured command after each assistant message, after /compact, on permission/vim-mode changes, AND every 5 seconds (refreshInterval: 5). The 5-second tick is what catches daemon state changes during long tool calls when no event-driven trigger fires.
Claude Code refresh tick (event or every 5s)
│
▼ runs statusLine command (with stdin JSON payload)
brv-claude-plugin status
│ Walk up from cwd → find `.brv/`
│ (none → empty stdout → line hidden)
│ Inspect filesystem signals:
│ • <brvDir>/dream-log/*.json (project-local)
│ • <brvDir>/dream.lock (project-local)
│ • <getProjectDataDir(cwd)>/curate-log/*.json
│ (per-project storage under the OS user-data dir —
│ mirrors the daemon's path resolution)
▼
Print one ANSI-colored line:
🧠 ByteRover · idle / 📝 curating / 💭 dreaming
State classification follows a confident-signals-first order: an active dream-log entry wins; an active curate-log entry wins over the dream.lock fallback; the lock alone is honored only if it's fresh (within 15 minutes) and the latest dream-log entry isn't already marked completed. This combination catches stale locks left behind by interrupted daemons and avoids masking active curate work. A residual edge case remains for daemons that crash mid-dream within the 15-minute window — the proper fix lives daemon-side and is tracked separately.
The plugin resolves Claude's memory directory using the same logic as Claude Code:
CLAUDE_COWORK_MEMORY_PATH_OVERRIDEenv var (full override)autoMemoryDirectoryfrom settings (local → user, excluding project settings for security)~/.claude/projects/<sanitized-canonical-git-root>/memory/
Git worktrees are resolved to their canonical root so all worktrees share one memory directory.
The plugin naturally supports multiple projects. Claude Code scopes memory per git repository, and ByteRover scopes its context tree per .brv/ directory. Both use the same cwd from the hook input as their anchor:
/Users/x/project-a/ ← claude session here
├── .git/ ← Claude memory: ~/.claude/projects/-Users-x-project-a/memory/
├── .brv/ ← ByteRover tree: project-a/.brv/context-tree/
└── src/
/Users/x/project-b/ ← separate claude session here
├── .git/ ← Claude memory: ~/.claude/projects/-Users-x-project-b/memory/
├── .brv/ ← ByteRover tree: project-b/.brv/context-tree/
└── src/
Each project's memories are ingested into its own context tree and synced back to its own memory directory. No cross-project leakage.
Important: initialize .brv/ at the git root. Claude Code resolves memory directories from the canonical git root. ByteRover walks up from cwd to find .brv/. When both are at the same level, everything maps correctly.
If you initialize .brv/ in a subdirectory instead of the git root, the plugin will encounter mismatches:
/monorepo/ ← git root (Claude memory lives here)
├── .git/
├── frontend/
│ └── .brv/ ← brv initialized here, not at git root
└── backend/ ← sessions from here won't find .brv/
In this layout, Claude sessions from /monorepo/backend/ would fail to reach the .brv/ in frontend/. To fix this, initialize ByteRover at the git root:
cd /monorepo
brv init # .brv/ at git root — matches Claude's project boundaryIf you need separate context trees for subdirectories in a monorepo, initialize .brv/ in each subdirectory and run claude from within that subdirectory (not from the monorepo root).
Every stored hook command includes a #brv-claude-plugin shell comment marker. This marker — not the path text — is used by install (dedupe), uninstall (removal), and doctor (detection). A clone in any directory will work correctly.
# Install dependencies
npm install
# Type check
npm run typecheck
# Build
npm run build
# Run in dev mode
npm run dev -- doctor
# Run built CLI
node dist/cli.js doctor- Initialize a brv project:
cd /your/project && brv init - Build the plugin:
npm run build - Install hooks (dev mode):
node dist/cli.js install - Start a Claude session and make a few changes
- Check
.brv/context-tree/for ingested memories - Check
~/.claude/projects/.../memory/_brv_context.mdfor synced context
src/
cli.ts # Commander.js entry point
cc-frontmatter.ts # Claude Code memory YAML frontmatter parser
memory-path.ts # Full cc-ts memory path resolution (git root, worktrees, env vars)
stdin.ts # Read + validate JSON from stdin
bridge-command.ts # Executable resolution + #brv-claude-plugin marker
build-recall-output.ts # Recall orchestrator — combines query result, score filter, fallback
build-visible-summary.ts # Recall preamble formatter — `🧠 ByteRover returns N memories…` line
parse-sources.ts # Regex fallback over **Sources**: blocks for cache-hit recovery
resolve-context-tree-age.ts # Frontmatter-based age resolution (updatedAt → mtime → undefined)
state-detector.ts # Filesystem state classification for the status line
format-status-line.ts # ANSI-colored status line formatter (idle / curating / dreaming)
project-data-dir.ts # Mirrors daemon's getGlobalDataDir + sanitizeProjectPath
schemas/
cc-hook-input.ts # Zod schemas for PostToolUse and Stop hook input
cc-settings.ts # Raw JSON read/write for settings.json (lossless)
commands/
install.ts # Install hooks into settings.json (per-hook dedupe, backup)
uninstall.ts # Remove plugin hooks AND status line (per-hook removal)
install-statusline.ts # Opt-in statusLine installer (prompt / --force / upgrade-in-place)
uninstall-statusline.ts # Marker-gated statusLine removal
ingest.ts # PostToolUse handler — Write/Edit split, brv curate
sync.ts # Stop handler — _index.md copy, _brv_context.md, MEMORY.md pointer
recall.ts # UserPromptSubmit handler — live brv query, visible summary
status.ts # statusLine handler — daemon state classification → one-line output
doctor.ts # 7 diagnostic checks (incl. status line registration)