Tre — shared roots for your codebase.
🇻🇳 Phiên bản tiếng Việt: README.vi.md
In Vietnamese, tre means bamboo — an enduring symbol of resilience, kinship, and shared roots. A bamboo grove is many stalks rising from one root system, swaying separately in the wind but standing together through every storm. The many branches of a codebase grow the same way: each its own feature, all from one shared history.
That's what tre-mem gives your AI assistant: every branch gets its own
voice, while the shared roots of the codebase stay intact. Branch-aware
memory layered on claude-mem — so
Claude Code / Cursor / Gemini CLI understand the feature you're working
on, not just the repo you're in.
tre-mem is a sidecar to claude-mem. It does not fork or modify claude-mem —
it adds a read-only adapter, tags every observation with the git branch it was
authored on, and serves a 3-signal retrieval API (semantic + branch + recency)
over MCP so Claude Code / Cursor / Gemini CLI all see branch-scoped context
instead of flat per-repo memory.
The headline: pin a decision, run one command, and it's in your team's git. Your teammate runs
git pulland their AI already knows — no server, no API keys, no GitHub required. Jump straight to Team memory.
Why · Install · Everyday use · Team memory · Dashboard · Cross-tool · MCP tools · CLI · License
claude-mem ingests sessions beautifully but indexes them flat per project.
Switch from feature/payment to fix/auth-jwt-expiry and the assistant still
sees Stripe webhook chatter alongside JWT context. tre-mem fixes that:
- Live branch tagging via a chokidar watcher on
.git/HEAD. - History backfill via
git reflogso existing observations get a branch. - 3-signal rerank: semantic (FTS5/BM25), branch locality, recency-in-branch,
plus a
pinboost for facts you want pinned to a branch. - MCP server exposing 9 tools so Claude Code can call branch-aware retrieval — and curate, publish, and retract memory — directly.
On the tre-mem repo itself the rerank lifts precision@10 from 0.19 (raw FTS5 baseline) to 0.97. See BENCHMARK.md for the harness.
# 1. Install
npm i -g tre-mem # or: pnpm add -g tre-mem
# 2. Initialize the sidecar DB at ~/.tre-mem/
tre init
# 3. Backfill branch tags for existing claude-mem observations (per repo)
cd /path/to/your/repo
tre backfillRequirements:
- Node 20+
- claude-mem already installed and ingesting (we read from
~/.claude-mem/claude-mem.db) giton PATH
The recommended way:
claude mcp add -s user tre-mem -- tre mcp(or, if you cloned from source and tre is not on PATH, point at the built
CLI directly: claude mcp add -s user tre-mem -- node /abs/path/to/tre-mem/dist/cli.js mcp)
Verify inside Claude Code with /mcp. You should see:
tre-mem · connected · 9 tools
Refreshes branch_state whenever Claude Code starts a session, so retrieval
always knows which branch is active even between watcher cycles. See
docs/HOOKS.md for the registration snippet and
troubleshooting matrix. Short version: add to ~/.claude/settings.json:
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [{ "type": "command", "command": "tre hook session-start" }]
}
]
}
}You rarely type tre by hand. tre-mem registers an MCP server and a
SessionStart hook, so the real loop is: Claude shows you branch context when a
session starts, and you curate by just talking to it. The CLI further down is
the manual escape hatch.
With the hook registered, every Claude Code session opens with a branch-scoped
digest (the hook's systemMessage, rendered in color in your terminal). When
this shows up, it's working:
[tre-mem] recent context · feature/payment · shop · 4:12pm
Legend: 🎯session 🔴bugfix 🟣feature 🔄refactor ✅change 🔵discovery ⚖️decision 🚨security_alert 🔐security_note
tagged: 38 on branch · 232 on project · imported: 1pin/0grad · source: startup
📌 Pinned on this branch
#411 ⚖️ Simulator Mock Features Strategy: File Download and MQTT Playgrounds [shared]
↳ why we mock MQTT + downloads instead of hitting staging
#412 🔵 CardApi Endpoints for Student, Deny, and Blackcard List Synchronization
#410 🔵 Simulator Web Infrastructure and API Routing for New Features
The 📌 Pinned block floats curated decisions — and the note explaining why —
to the top. [shared] means the pin arrived from a teammate's git push and was
auto-imported on this session. imported: 1pin/0grad in the stats line confirms
the import happened.
Pinning is deliberate by design — a pin gets a 1.0 search boost (always top),
so if everything were auto-pinned, nothing would stand out. But you steer it in
plain language via the pin_fact / graduate_fact MCP tools; Claude can even
write the note for you:
| You say to Claude… | Claude calls… |
|---|---|
| "Pin this decision for the team: we use Stripe webhook v3." | pin_fact (note on the current branch) |
| "Remember why we chose MQTT over polling — share it with the team." | pin_fact on the key ⚖️ decision |
| "What's the context on this branch?" / "What did we decide about X?" | get_branch_context → ranked, pins on top |
| "Show me the timeline of this feature so far." | get_branch_timeline |
| "This branch is merged — promote its decisions repo-wide." | graduate_fact (or the merge Action does it) |
Then run tre share (one command: export + commit + push) and your teammate
inherits the pin automatically on their next session — surfaced in their digest
with the [shared] tag shown above.
tre init # create ~/.tre-mem/ + run migrations
tre status [path] # project / branch / tag counts for cwd
tre backfill [path] [--project SLUG] # tag history via git reflog
tre search "<query>" [--branch B] [--k 10]
tre pin <observation_id> [--note "..."]
tre graduate <observation_id> # promote branch fact → project-wide
tre list-branches [--project SLUG]
tre logs [--tail 50 | --all] [--level warn] [--path] # local diagnostics log
tre mcp # start MCP server (stdio)
# --- team sharing ---
tre share [--branch B | --all] [--message M] [--no-push] [--no-commit] # push memory to git (one step)
tre export [--branch B | --all] [--force] [--dry-run] # low-level: write pins → .tre-mem/ only
tre import [--from .tre-mem] [--force] # pull a teammate's pins
tre graduate-pr <PR# | branch> [--dry-run] # graduate a merged branch (any provider)
tre graduate-merge # graduate the just-merged branch (post-merge hook)
tre setup claude-code [--auto-inject] [--with-hook] # wire hooks (+ local graduate-on-merge hook)
tre hook session-start | user-prompt-submit # invoked by Claude Codetre search prints top-K with a score breakdown so you can see why each hit
ranked:
tre-mem search "stripe webhook"
project: shop
branch: feature/payment
k: 10 (returned 4)
[1.800] #938 feat(stripe): retry handler for failed charges
sem 0.40 branch 0.40 rec 0.20 pin 1.00
[0.600] #1034 BMOtpTextView keyboard handling
sem 0.40 branch 0.00 rec 0.20 pin 0.00
...
| Tool | Input | Output |
|---|---|---|
get_branch_context |
query, project?, branch?, k? |
Top-K observations, rerank breakdown included |
get_branch_timeline |
branch, project?, limit? |
Chronological feed for a branch |
list_branches |
project? |
Branches with tag counts |
pin_fact |
observation_id, branch?, note? |
Pin a fact to a branch (boost = 1.0) |
graduate_fact |
observation_id |
Promote a branch fact to project scope |
unpin_fact |
observation_id, branch? |
Remove a pin (tombstones it if shared) |
ungraduate_fact |
observation_id |
Remove a graduated fact (tombstones if shared) |
export_memory |
branch?, all?, force? |
Write .tre-mem/ + local commit (no push) |
get_share_status |
project? |
Pending / shared / graduated counts |
After the assistant pins or graduates a fact, it can call export_memory to
publish it: this writes the .tre-mem/ files and makes a local git commit — it
never pushes, so you review and git push when ready (the tool returns the exact
command). It's fail-closed on secrets: a blocked export reports the matched secret
categories, never the values.
When a fact goes stale — a leader's PR feedback, a bug found in QC — the assistant
calls unpin_fact or ungraduate_fact to take it back. If the fact was
already shared, this writes a tombstone to .tre-mem/; on the next git pull
every teammate's clone drops it automatically. To correct a fact, ungraduate the
old one and graduate_fact the fixed observation.
If you clone the same repo into several directories (e.g. app, app-2, app-3)
to work branches in parallel, tre-mem unions their memory — they're recognized
as one project by a shared remote.origin.url. tre status shows the canonical
remote: and the linked clones. This is on by default; set
TRE_MEM_CROSS_CLONE=0 to keep each directory isolated. The committed .tre-mem/
format is unchanged, so teammates are unaffected.
This is what tre-mem is for. Pin a decision, run one command, and it's in
your team's git. Your teammate runs git pull and their AI already knows — Claude
Code, Codex, Gemini, Cursor, whatever they use. No server, no API keys, and no
GitHub required: GitHub, GitLab, Bitbucket, or a plain bare remote — if it's
git, it works.
alice@laptop bob@laptop
~/.tre-mem/ (private sidecar) ~/.tre-mem/ (private sidecar)
│ tre share ▲ tre import (auto on SessionStart)
▼ │
repo/.tre-mem/ ── git push ── git pull ── repo/.tre-mem/
(merge=union: two sharers never conflict — git keeps both)
# alice, on feature/payment
tre pin 1234 --note "use Stripe webhook v3"
tre share # export + git add + commit + push — one step
# bob
git pull # tre import runs automatically on his next session
# → his AI now surfaces alice's pin, tagged [shared]That's the whole loop. tre share writes your curated facts to .tre-mem/,
commits, and pushes; if the branch has no upstream it prints the exact
git push -u …. (tre export is still the low-level "write files only"
primitive if you'd rather drive git yourself.)
Key properties:
- One command, any git host.
tre shareis plain git underneath — works with GitHub, GitLab, Bitbucket, or a bare remote. Nothing is provider-locked. - Only pins + graduated facts are shared. Raw observations stay private in
each developer's
~/.claude-mem/. - Fail-closed redaction. Sharing refuses to write detected secrets
(private keys, API tokens, JWTs…) unless you pass
--force(which replaces them with[REDACTED:*]). Per-repo.tre-mem/.shareignoreadds globs. - "Keep both" conflicts.
.tre-mem/.gitattributessets*.jsonl merge=union, so two teammates sharing at once never hit a merge conflict — git keeps both sides andtre importde-dupes on read. - Graduate on merge, no CI required.
tre setup … --with-hookinstalls a localpost-mergegit hook that promotes a merged branch's pins to repo-wide facts — on any provider. Prefer CI? A few lines of GitLab/Bitbucket/any-runner YAML callingtre graduate-prdoes the same. No vendor-locked GitHub Action.
Full guide: docs/TEAM-WORKFLOW.md. Upgrading from v0.1 is automatic — see docs/MIGRATION-v1-v2.md.
Phase 2 made memory travel through git; v0.5 lets your team see it. Run
tre web for a local, read-only dashboard of the branch graph, pinned decisions,
graduated facts, and what's not shared yet — updating live as the repo and sidecar
change. No account, no cloud; binds 127.0.0.1 only.
tre web # start + open the browser (Ctrl-C to stop)
tre web --background # run detached; manage with `tre web status` / `tre web stop`- Branch graph — every branch with tagged-count, pins, last-active, current
HEAD. - Team memory — who pinned what and why, plus graduated facts, with a
shared via git ✓/not shared yetmarker. - Search — branch-aware, with a per-signal score breakdown.
- Live — reacts to branch switches, teammate
git pull/tre import, and pins written from another terminal (SSE).
Works even without claude-mem (shared-memory-only mode: pins + graduated from
the sidecar/.tre-mem/, substring search). Full guide:
docs/WEB-UI.md.
tre means bamboo, so as of v0.8 tre-mem looks like bamboo. The dashboard is
re-themed around a jade-green primary on green-tinted rice paper, with a
coordinated grove palette for the branch graph (light + dark both intentional);
the SessionStart digest and CLI read green too — 🎋 tre-mem · recent context ….
The whole identity — OKLCH palette with its terminal-ANSI mapping, the semantic
spine legend, typography, and motion — is documented as a single source of truth
in docs/BRAND.md.
tre-mem speaks MCP, so the git-shared team memory travels to every major harness. Wire them all at once:
tre setup --all # claude-code (repo) + every installed harness
tre setup --all --auto-inject # also wire per-prompt context injectionOr one at a time: tre setup codex · codex-desktop · gemini · cursor ·
antigravity. Each registers tre-mem's MCP server (and, for Codex/Gemini,
SessionStart + opt-in prompt hooks) — idempotent and non-clobbering.
Two layers: consume (team memory + branch ranking) works on every harness
via tre-mem's MCP; ingest (recording observations) is claude-mem's job, and
claude-mem v13+ ingests from Claude Code, Codex, Gemini, Cursor, and Antigravity
into one shared DB. So full branch-aware search is available wherever claude-mem
is installed + ingesting — run tre doctor to see this machine's mode
(full / shared-only) and ingest health. Full guide:
docs/CROSS-TOOL.md.
tre-mem writes a small append-only JSONL log to ~/.tre-mem/tre-mem.log so you can
see what it did on a machine and share it for troubleshooting. It records counts and
metadata only — branch/project names, event counts, ids, durations, and error
class+message. It never logs raw query text, prompt text, or pin/note bodies, so the
file is safe to hand off.
tre logs # last 50 lines of JSONL
tre logs --all # whole file (good for collecting end-of-day)
tre logs --level warn # only warnings + errors
tre logs --component mcp # only MCP events
tre logs --path # print the file path (e.g. to cat / copy it)
tre logs --clear # truncate the log (and remove the rotated .1 backup)Each line looks like:
{
"ts": "2026-06-04T09:12:03.144Z",
"t": 1780989123144,
"level": "info",
"component": "hook",
"event": "session_start",
"fields": {
"project": "shop",
"branch": "feature/payment",
"tagged_branch": 12,
"imported_pins": 2,
"ms": 31
}
}| Env var | Default | Meaning |
|---|---|---|
TRE_MEM_LOG |
enabled | set 0/false/off to disable logging |
TRE_MEM_LOG_LEVEL |
info |
min level: debug/info/warn/error |
TRE_MEM_LOG_FILE |
<TRE_MEM_HOME>/tre-mem.log |
absolute path override |
The file rotates to tre-mem.log.1 once it passes 5 MB, so it never grows unbounded.
Claude Code / Cursor / Gemini CLI
│ (MCP stdio)
▼
tre-mem MCP server (TS)
│
┌──────────┴──────────────┐
│ Retrieval engine │ 3-signal rerank
│ (semantic + branch + recency)
└──────┬──────────────┬────┘
│ │
┌──────▼─────┐ ┌─────▼────────────┐
│ tre-mem.db │ │ claude-mem.db │ ← READ-ONLY (better-sqlite3)
│ (sidecar) │ │ observations, FTS5│
│ branch_tag │ │ session_summaries │
│ branch_pin │ └───────────────────┘
│ graduated │
└────────────┘
▲
┌──────┴───────────┐
│ Git watcher │ chokidar on .git/HEAD per repo
│ + reflog backfill│
└───────────────────┘
Five modules: adapter/ (claude-mem reader), git/ (watcher + resolver +
reflog), store/ (sidecar DB + repo), retrieval/ (3-signal + rerank),
mcp/ (server + tools). See CLAUDE.md and PLAN.md
for the full design.
Current: v0.10.0 — agent-driven export + cross-clone memory. Covered by 366 tests, green on Node 20 + 22. CHANGELOG.md tracks every release; PLAN.md is the roadmap index.
Shipped so far:
| Version | Theme |
|---|---|
| v0.10 | Agent-driven export_memory + cross-clone memory union (by git remote) |
| v0.9 | "The Grove" — contributor graph + leaderboard + full Vietnamese i18n |
| v0.8 | Bamboo-green design identity (web + terminal), docs/BRAND.md SSOT |
| v0.7 | "Share, made obvious" — one-command tre share, local graduate-on-merge |
| v0.6 | Cross-tool — Codex / Gemini / Cursor / Antigravity via MCP |
| v0.5 | Local team dashboard (tre web) — branch graph + team memory, live (SSE) |
| v0.2–4 | Git-native team share — export/import, redaction, branch graduation |
| v0.1 | Branch-aware retrieval (3-signal rerank) + MCP server |
Out of scope (for now):
- Hosted / cloud sync (tre-mem stays local-first; git is the transport)
- Encrypted memory for sensitive repos (BYO-key)
- Independent ingest — recording observations is claude-mem's job, not ours
MIT. See LICENSE.
🎋 Made with care, from the bamboo grove. Cảm ơn bạn đã ghé thăm.