Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,41 @@ All notable changes to **tre-mem** are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and this project
adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.10.0] — 2026-06-07

**The agent shares for you, and memory crosses your clones.** Two workflow gaps
close: the assistant can now publish curated memory itself (no terminal hop), and
multiple local clones of the same repo finally share one memory.

### Added

- **`export_memory` MCP tool** — the assistant writes `.tre-mem/` and makes a
**local git commit** (never pushes; you push when ready). The result carries the
exact `git push…` command. Reuses the same `exportSync` + `shareToGit` pipeline
as `tre share`, so behavior never drifts. Fail-closed on secrets: a blocked
export returns the matched secret _categories_ and a fix instruction, never the
secret values.
- **`get_share_status` MCP tool** — lets the assistant see unshared pins / shared
pins / graduated counts so it can proactively offer to export.
- **Cross-clone memory by git remote** — clones that share an identical
`remote.origin.url` (e.g. `app`, `app-2`, `app-3`) now **union** their memory
(branch tags, pins, graduated facts, and claude-mem observations). Identity is a
read-time alias set, so the on-disk `.tre-mem/` format is unchanged and teammates
are unaffected. Default-on; disable with `TRE_MEM_CROSS_CLONE=0`.
- **`tre status`** now prints the canonical `remote:` and, when more than one clone
shares it, a `linked clones (N): …` line. The dashboard topbar shows a
`🔗 N clones` chip.
- **`graduate_fact`** result now includes a `hint` nudging the assistant to call
`export_memory` to publish.

### Changed

- Sidecar schema **v3** (additive): a nullable `remote` column on `branch_state`,
recorded on every session-start / git-watch. No backfill; existing rows behave
exactly as before until their clone is next opened.
- Retrieval and the claude-mem adapter now query an `IN (…)` set of project labels
internally; a single-element set is identical to prior behavior.

## [0.9.0] — 2026-06-07

**The Grove — a second-brain contributor graph, plus a Vietnamese dashboard.** A new
Expand Down
86 changes: 86 additions & 0 deletions PLAN-PHASE7.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# PLAN — Phase 7 (v0.10.x): Agent-driven export + cross-clone memory

> Phase SSOT, chained from [`PLAN.md`](./PLAN.md). Status: **done (v0.10.0)**.

## Why

Two gaps surfaced from daily use:

1. **The assistant couldn't close the share loop.** `pin_fact` / `graduate_fact`
only write the sidecar; publishing required dropping to a terminal for
`tre share`. That one manual hop broke the "let the agent do it" flow.
2. **Memory didn't cross clones of one repo.** Identity was `basename(cwd)`, so
`app`, `app-2`, `app-3` (parallel clones) were three isolated projects.

User decisions: MCP tool = **export + local commit, no push**; cross-clone =
**default-on + `TRE_MEM_CROSS_CLONE=0` kill-switch**; plus three small UX extras
(pending-share status tool, `tre status` clone visibility, `graduate_fact` hint).

## A — Agent-driven export (MCP)

- **`export_memory`** — reuses `exportSync` (`src/sync/export.ts`) +
`shareToGit({commit:true, push:false})` (`src/sync/share.ts`). Writes `.tre-mem/`
and makes a **local commit**; never pushes. Returns `{ files, total_added,
graduated_added, committed, pushed:false, commit_hint, note }`. Fail-closed on
secrets: a `RedactionError` returns `redaction_blocked { categories, count,
instruction }` — never the secret values, no files written.
- **`get_share_status`** — `{ pending_export, shared_pins, total_pins, graduated,
has_sync_dir }` (mirrors `/api/share-status`). Lets the agent nudge the user.
- **`graduate_fact`** — result gains a `hint` field pointing at `export_memory`.

Both new tools use the **write key** (`basename(cwd)` / `--project`), since
publishing and pending-share are per-clone.

## B — Cross-clone memory by git remote

**Approach: read-time alias union, never a stored-key rewrite.** Writes keep using
`basename(cwd)`; reads expand to `project IN (alias set)`. Forced by constraints:
claude-mem observations are permanently basename-keyed (read-only upstream),
historical sidecar rows can't be reliably re-keyed, and committed `.tre-mem/` JSONL
carries `project`. Payoff: **on-disk format + `SYNC_SCHEMA_VERSION` unchanged, zero
teammate impact**, and only one additive schema column.

- `src/git/remote.ts` — `canonicalizeRemoteUrl(url)` (pure: ssh scp / ssh-url /
https-with-creds / `git://`, strip `.git` + trailing slash, lowercase →
`host/org/repo`); `remoteSlug(cwd)` reads `remote.origin.url`, never throws.
- `src/store/aliases.ts` — `resolveProjectIdentity(repo, cwd, opts) → { project,
remote, aliases }`; `crossCloneEnabled(env)` (`TRE_MEM_CROSS_CLONE` off-switch).
- Schema **v3** (`src/store/migrate.ts` + `schema.sql`): nullable
`branch_state.remote` + `idx_branch_state_remote`, idempotent self-heal mirroring
v2. No backfill; rows self-heal on next session-start / git-watch write.
- `src/store/repo.ts` — `projectAliases(project, remote)` (= `SELECT DISTINCT
project FROM branch_state WHERE remote = ?` ∪ `{project}`, else `[project]`) and
`*Across(projects[])` readers (`countBranchTagsAcross`,
`listBranchTagsForBranchAcross`, `listPinsForBranchAcross`,
`listPinsForProjectAcross`, `listGraduatedAcross`, `listBranchesForProjectAcross`,
`listPinBranchesAcross`). Single-element array == prior behavior.
- Adapter/retrieval refactored `project: string → projects: string[]`,
`= @project → IN (…)`: `src/adapter/claude-mem.ts`, `src/adapter/types.ts`
(`ListQuery`), `src/retrieval/semantic.ts`, `src/retrieval/search.ts`.
- Entry points resolve identity (writes use `project`, reads use `aliases`):
`src/mcp/tools.ts`, `src/hooks/session-start.ts`,
`src/hooks/user-prompt-submit.ts`, `src/web/server.ts` + `api.ts`, `src/cli.ts`.
`branch_state.remote` is persisted by session-start and the git watcher.

## C — Visibility

- `tre status` prints `remote:` and, when >1 clone shares it,
`linked clones (N): …`.
- `/api/health` carries `remote` + `linked_clones`; dashboard topbar shows a
`🔗 N clones` chip (`web/app.tsx`, `Health` type in `web/lib.tsx`).

## Tests

`test/git-remote.test.ts`, `test/store-aliases.test.ts`,
`test/mcp-export-crossclone.test.ts` (export commit-no-push, redaction fail-closed,
share-status, graduate hint, cross-clone surfacing + isolation), plus v3 migration
cases (`test/migrate.test.ts`), repo alias/`*Across` cases (`test/repo.test.ts`),
and the `projects[]` migration across adapter/retrieval/web/mcp test fixtures.
Full gate green: format · lint · typecheck · 364 tests · build.

## Out of scope (deferred)

- Monorepo sub-path identity (one remote, many logical projects) — kill-switch is
the escape hatch for now.
- Cross-clone for the per-clone "pending share" view (kept per-clone by design).
- Auto-export-on-graduate (the user prefers an explicit `export_memory` call).
4 changes: 3 additions & 1 deletion PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
> [`PLAN-PHASE3.md`](./PLAN-PHASE3.md) — Phase 3 (v0.5.x, team web dashboard) ·
> [`PLAN-PHASE4.md`](./PLAN-PHASE4.md) — Phase 4 (v0.6.x, cross-tool port) ·
> [`PLAN-PHASE5.md`](./PLAN-PHASE5.md) — Phase 5 (v0.7.x, "Share, made obvious") ·
> [`PLAN-PHASE6.md`](./PLAN-PHASE6.md) — Phase 6 (v0.9.x, "The Grove" — contributor graph + VN i18n). Current: **v0.9.0**.
> [`PLAN-PHASE6.md`](./PLAN-PHASE6.md) — Phase 6 (v0.9.x, "The Grove" — contributor graph + VN i18n) ·
> [`PLAN-PHASE7.md`](./PLAN-PHASE7.md) — Phase 7 (v0.10.x, agent-driven export + cross-clone memory). Current: **v0.10.0**.

---

Expand Down Expand Up @@ -388,4 +389,5 @@ Parser: `cac` (nhẹ, đủ).
- **2026-05-31** — T2D10 polish (docs + version) done. Added `README.md` (one-liner + why + 3-step install + MCP registration via `claude mcp add -s user tre-mem -- tre mcp` + SessionStart hook + CLI surface + MCP tool table + architecture diagram + status), `CHANGELOG.md` in Keep-a-Changelog format with a single `[0.1.0] — 2026-05-31` entry covering sidecar store, read-only adapter, branch resolver/watcher, reflog backfill, 3-signal rerank, MCP server, SessionStart hook, CLI, benchmark harness, docs. Added MIT `LICENSE`. Bumped `package.json` version `0.0.0 → 0.1.0` and synced `getPackageVersion()` in `src/cli.ts` so `tre --version` prints `tre/0.1.0`. All quality gates remain green (typecheck + lint + 96/96 tests + build). `npm publish v0.1.0`, demo screen-record, and external-dev validation deliberately deferred to user action — those require credentials / hands / a second human and cannot be automated from this session.
- **2026-06-07** — v0.8.0 "Bamboo — the green identity" done. Established a design-system SSOT (`docs/BRAND.md`) and made tre actually look like bamboo on both surfaces a user sees. **Web** (`web/styles.css`): full re-theme — primary `--bark` → `--bamboo` (amber → jade green), green-tinted rice-paper/ink ramp, semantic spines re-hued into a coordinated grove palette (sky / young-shoot / lacquer-gold / clay-red), both light + dark intentional; token-only change, no component restructure, JS bundle unchanged; added a bamboo-culm `BambooMark` SVG before the wordmark + a 🎋 favicon. **Terminal** (`src/format/`): `colors.ts` gained a `brand()` helper + `BAMBOO` motif; the SessionStart digest header (`🎋 tre-mem · recent context`), live-dashboard link, and pinned-section header now read green; `tre web` / `tre doctor` carry the 🎋 mark; the plain model-facing `context` string stays ANSI-free. Docs synced (README + README.vi "Brand & design", WEB-UI theme note, PLAN-PHASE3 design-direction update); version `0.7.1 → 0.8.0` across `package.json` + `src/version.ts` + CHANGELOG. Decision was user-confirmed: full web re-theme, subtle terminal (no banner), minor bump.
- **2026-05-31** — T2D8 done. `src/mcp/tools.ts` exposes 5 pure tool functions (`getBranchContext`, `getBranchTimeline`, `listBranches`, `pinFact`, `graduateFact`) plus a `callTool(name, args)` dispatcher and a frozen `TOOL_DEFINITIONS` array carrying name/description/JSON-Schema for each. Tools resolve `project`/`branch` defaults via injectable `ToolDeps.defaultCwd` + `ToolDeps.resolveBranch` (defaults to `process.cwd()` + `currentBranch()`), and `now` is injectable for deterministic tests. `src/mcp/server.ts` uses the lower-level `@modelcontextprotocol/sdk` `Server` (not `McpServer`) because we don't want a zod dependency: `ListToolsRequestSchema` returns `TOOL_DEFINITIONS` 1:1; `CallToolRequestSchema` dispatches to `callTool`, JSON-stringifies into `content[0].text` AND mirrors structured into `structuredContent` so both old + new MCP clients work, and converts thrown errors to `{isError: true}`. `runMcpServer()` runs `migrate()`, opens adapter + repo, wires `StdioServerTransport`, and closes handles on SIGINT/SIGTERM. CLI gains `tre mcp` command. **Live MCP handshake smoke (real `~/.tre-mem/tre-mem.db`)**: `initialize` → `notifications/initialized` → `tools/list` returns all 5 tool descriptors; `tools/call list_branches {project:"tre-mem"}` returns `{branches: [{branch:"main", count:22}]}` — i.e. the same 22-tag count `tre status` shows, proving the full stack works through stdio. 10 new tests (`mcp-tools.test.ts` × 9, `mcp-server.test.ts` × 1); suite 96/96 green; lint + typecheck + build clean.
- **2026-06-07** — **Phase 7 (v0.10.0) done — agent-driven export + cross-clone memory.** Two workflow gaps closed (SSOT `PLAN-PHASE7.md`). **(A) Agent-driven sharing:** new MCP tools `export_memory` (reuses `exportSync` + `shareToGit` with `push:false` → writes `.tre-mem/` + a **local commit**, never pushes; returns the exact `git push` command; fail-closed on secrets, returning categories not values) and `get_share_status` (pending/shared/graduated counts); `graduate_fact` now returns a `hint` nudging export. So the assistant closes the share loop without a terminal hop — the user reviews + pushes. **(B) Cross-clone memory:** clones sharing a `remote.origin.url` now union memory. Approach = **read-time alias union, not a stored-key rewrite** (forced by: claude-mem observations are permanently basename-keyed; committed `.tre-mem/` JSONL carries `project`; can't reliably re-key history). New `src/git/remote.ts` (`canonicalizeRemoteUrl` → `host/org/repo`, `remoteSlug`), new `src/store/aliases.ts` (`resolveProjectIdentity` + `crossCloneEnabled`), schema **v3** (additive nullable `branch_state.remote` + index, self-healing like v2), `projectAliases` + `*Across(projects[])` repo readers, and adapter/retrieval refactored to `project IN (…)`. Writes still key on `basename(cwd)` → **on-disk format + `SYNC_SCHEMA_VERSION` unchanged, zero teammate impact**. Default-on; `TRE_MEM_CROSS_CLONE=0` disables. `tre status` shows `remote:` + `linked clones`; dashboard topbar shows a `🔗 N clones` chip (`/api/health` carries `remote` + `linked_clones`). Tests: `git-remote`, `store-aliases`, `mcp-export-crossclone`, plus v3 migration + repo alias cases; 364 tests green; full gate (format/lint/typecheck/test/build) clean. Version `0.9.0 → 0.10.0`.
- **2026-06-07** — **Phase 6 "The Grove" (v0.8.0) done.** New `tre web` Grove tab: an Obsidian-style `d3-force` + canvas graph of the repo's shared memory (root/trunk + branches + contributors + facts) beside a contributor leaderboard. Built on the realization that contributor identity already lives in the committed `.tre-mem/` JSONL `author` field (set at `tre share`/graduate time) — so a new read-only reader (`src/sync/read.ts`) feeds pure aggregators in `src/web/grove.ts` (value score = pins×1 + graduated×3, plus Gardener-of-the-week / Most-rooted / Longest-streak / First-sprout badges) with **no schema migration**. Two read-only endpoints `/api/contributors` + `/api/graph` (project-scoped, `?fallback=git` defaulting on so solo/unshared repos backfill contributors from `git log` authors). Frontend: `Grove.tsx` + `Leaderboard.tsx` + `GraphCanvas.tsx` (theme-token colors, bamboo node sizing, hover/click drill into Branch detail) + `ShareCard.tsx` (canvas→PNG) + growth time-lapse scrubber. `d3-force` (sim only) + hand-rolled canvas chosen over cytoscape/react-force-graph to stay dependency-light (~13 KB add). Also full **Vietnamese i18n** (EN/VI toggle, casual voice) + a VN-friendly type system (Baloo 2 + Be Vietnam Pro). Tests: `test/sync-read.test.ts`, `test/web-grove.test.ts`. Rebased onto v0.8.0 bamboo identity (`--bark` → `--bamboo`); version → 0.9.0; CHANGELOG `[0.9.0]`; SSOT `PLAN-PHASE6.md`.
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ sees Stripe webhook chatter alongside JWT context. tre-mem fixes that:
- **History backfill** via `git reflog` so existing observations get a branch.
- **3-signal rerank**: semantic (FTS5/BM25), branch locality, recency-in-branch,
plus a `pin` boost for facts you want pinned to a branch.
- **MCP server** exposing 5 tools so Claude Code can call branch-aware
retrieval directly.
- **MCP server** exposing 7 tools so Claude Code can call branch-aware
retrieval — and publish 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](./BENCHMARK.md) for the harness.
Expand Down Expand Up @@ -95,7 +95,7 @@ CLI directly: `claude mcp add -s user tre-mem -- node /abs/path/to/tre-mem/dist/
Verify inside Claude Code with `/mcp`. You should see:

```
tre-mem · connected · 5 tools
tre-mem · connected · 7 tools
```

## Register the SessionStart hook (optional but recommended)
Expand Down Expand Up @@ -216,6 +216,23 @@ tre-mem search "stripe webhook"
| `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 |
| `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.

## Cross-clone memory

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.

## Diagnostics log

Expand Down
4 changes: 2 additions & 2 deletions README.vi.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Stripe webhook với JWT context. tre-mem xử lý điều đó:
- **Backfill lịch sử** qua `git reflog` để observation cũ cũng có branch.
- **Rerank 3-signal**: semantic (FTS5/BM25), branch locality, recency-trong-branch,
cộng thêm pin boost cho fact muốn ghim vào branch.
- **MCP server** expose 5 tool để Claude Code gọi retrieval branch-aware trực tiếp.
- **MCP server** expose 7 tool để Claude Code gọi retrieval branch-aware trực tiếp.

Trên chính repo tre-mem, rerank nâng precision@10 từ **0.19** (FTS5 baseline thuần)
lên **0.97**. Xem [BENCHMARK.md](./BENCHMARK.md) để biết chi tiết harness.
Expand Down Expand Up @@ -89,7 +89,7 @@ claude mcp add -s user tre-mem -- tre mcp
Kiểm tra trong Claude Code bằng `/mcp`. Sẽ thấy:

```
tre-mem · connected · 5 tools
tre-mem · connected · 7 tools
```

## Đăng ký SessionStart hook (tùy chọn nhưng nên có)
Expand Down
3 changes: 2 additions & 1 deletion docs/CROSS-TOOL.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@ tre mcp # start by hand; without claude-mem it prints "shared-memory
```

In the tool, list MCP tools — you should see `get_branch_context`,
`get_branch_timeline`, `list_branches`, `pin_fact`, `graduate_fact`. Ask:
`get_branch_timeline`, `list_branches`, `pin_fact`, `graduate_fact`,
`export_memory`, `get_share_status`. Ask:
_"what has the team pinned on this branch?"_

## Notes
Expand Down
2 changes: 1 addition & 1 deletion docs/DEMO.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ Claude Code session in the same repo.

**Expected**: Claude calls the `tre-mem.get_branch_context` MCP tool and
answers in terms of **BMOtpTextView**, not AccountManager. (Check `/mcp` shows
`tre-mem · connected · 5 tools` before recording.)
`tre-mem · connected · 7 tools` before recording.)

Then `git checkout feature/test_tre_mem` in the terminal, ask the same
question again, and watch Claude switch to **AccountManager**.
Expand Down
Loading
Loading