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
45 changes: 45 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,51 @@ 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.9.0] — 2026-06-07

**The Grove — a second-brain contributor graph, plus a Vietnamese dashboard.** A new
`tre web` tab visualizes the team's shared knowledge as an Obsidian-style force
graph (trunk + branches + contributors + facts) and ranks who grew it, and the
whole dashboard now speaks Vietnamese with a Vietnamese-friendly type system.

### Added

- **Grove tab** in the dashboard: an interactive `d3-force` + canvas graph of the
repo's shared memory. The project trunk (`root`), each branch, each contributor,
and each fact (pins = young shoots, graduated facts = mature culms) are nodes;
edges wire authorship, where a fact lives, and how branches graduate into the
trunk. Bamboo palette driven by the existing theme tokens (light/dark aware).
Hover for a tooltip; click a branch/fact to drill into Branch detail, click a
contributor to highlight their shoots.
- **Contributor leaderboard** with a weighted value score (branch-local pins ×1,
graduated/rooted facts ×3) plus playful badges & streaks — Gardener of the week,
Most rooted, Longest streak, First sprout.
- **Shareable grove card**: one click exports a bamboo-framed PNG of your grove's
headline stats — the viral, post-it-anywhere artifact.
- **Growth time-lapse**: scrub or play back how the grove grew over time.
- **Solo/unshared fallback**: when nothing has been shared yet, the grove backfills
contributors from `git log` authors per branch (flagged as `git-fallback`) so a
fresh repo is never empty. Toggle with `?fallback=` on the new endpoints.
- New read-only endpoints `GET /api/contributors` and `GET /api/graph`
(project-scoped), plus a read-only `.tre-mem/` JSONL reader (`src/sync/read.ts`)
— contributor attribution lives in the committed JSONL `author` field, so no
schema migration was needed.
- **Vietnamese (VI) localization** of the entire dashboard with an EN ⇄ VI toggle
(persisted, auto-detects Vietnamese browsers) and a deliberately casual, fun
Vietnamese voice. `timeAgo` is localized too.

### Changed

- **Type system → Vietnamese-friendly fonts.** Display/headings now use Baloo 2
(cute, rounded) and body uses Be Vietnam Pro (a Vietnamese-native typeface),
both with full diacritics and system fallbacks — fixing the diacritic stacking
the previous serif had on Vietnamese text.

### Fixed

- Dashboard shell grid declared two rows for three children, so the nav row
ballooned and pushed content down on low-content pages; now `auto auto 1fr`.

## [0.8.0] — 2026-06-07

**Bamboo — the green identity.** `tre` means _bamboo_ in Vietnamese, but the tool
Expand Down
49 changes: 49 additions & 0 deletions PLAN-PHASE6.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# tre-mem Phase 6 — "The Grove" (contributor graph, leaderboard & viral share)

> Sibling plan to [`PLAN.md`](./PLAN.md) (v0.1), [`PLAN-PHASE2.md`](./PLAN-PHASE2.md) (v0.2–v0.4),
> [`PLAN-PHASE3.md`](./PLAN-PHASE3.md) (v0.5), [`PLAN-PHASE4.md`](./PLAN-PHASE4.md) (v0.6),
> [`PLAN-PHASE5.md`](./PLAN-PHASE5.md) (v0.7.x). This file is the **Phase 6 SSOT** (v0.9.0;
> rebased onto the v0.8.0 bamboo identity).

## Context

Through v0.7, tre-mem makes a team's git-shared memory _legible_ (`tre web`: Overview / Team
memory / Search). Phase 6 makes it **emotional and viral**: a second-brain, Obsidian-style force
graph of the repo — the trunk (graduated repo-wide facts), the branches, and the **contributors**
who grew them — plus a "best contributor" leaderboard with playful bamboo badges and a one-click
shareable grove card.

**Why it was feasible with no migration:** every committed `.tre-mem/` JSONL record
(`PinRecord` / `GraduatedRecord` in `src/sync/format.ts`) already carries `author`, populated from
`git config user.name` at `tre share` / graduate time. The sidecar DB has no author column and the
web layer read only the DB — so the new feature reads the JSONL directly via a new read-only reader.

**Decisions locked with owner:** ship all four surfaces (graph, leaderboard, share card, time-lapse);
solo/unshared repos fall back to `git log` authors so the grove is never empty; full gamification
(value score + badges & streaks).

## Tasks

- [x] **Read-only JSONL reader** — `src/sync/read.ts` (`readSyncRecords` / `readSyncDir`), reusing
`parseSyncLine` + layout helpers. Test: `test/sync-read.test.ts`.
- [x] **Grove core** — `src/web/grove.ts`: `aggregateContributors` (value score = pins×1 +
graduated×3, badges/streaks), `buildGraph`, `buildGitContributors` + `buildGitGraph` (fallback).
`branchAuthors()` git helper in `src/git/identity.ts`. Test: `test/web-grove.test.ts`.
- [x] **Endpoints** — `GET /api/contributors` and `GET /api/graph` added to `ROUTES` in
`src/web/api.ts`; project-scoped; `?fallback=git` (default on) when nothing is attributed.
- [x] **Grove tab + leaderboard** — `web/lib.tsx` types, `web/app.tsx` tab, `web/views/Grove.tsx` +
`Leaderboard.tsx` (badges, drill into Branch detail).
- [x] **Graph canvas** — `web/views/GraphCanvas.tsx`: `d3-force` simulation + HTML canvas, theme
tokens, bamboo sizing, hover tooltip, click-to-drill, `cutoffEpoch` for time-lapse.
- [x] **Share card + time-lapse** — `web/views/ShareCard.tsx` (canvas → PNG) and the time-lapse
scrubber/playback in `Grove.tsx`.
- [x] **Docs + release** — `docs/WEB-UI.md` (Grove tab + endpoints), `CHANGELOG.md` `[0.9.0]`,
version bump `package.json` + `src/version.ts` → `0.9.0`.

## Notes

- Graph rendering uses `d3-force` (force sim only) + a hand-rolled canvas renderer — chosen over
cytoscape (~350 KB) and react-force-graph-2d (~80–120 KB) to honor the dependency-light ethos.
Bundle add ≈ 13 KB; SPA raw ≈ 243 KB.
- Edge kinds: `authored` (contributor→fact), `lives_on` (fact→branch), `graduates_into`
(branch→root), `committed` (contributor→branch, git-fallback only).
4 changes: 3 additions & 1 deletion PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
> [`PLAN-PHASE2.md`](./PLAN-PHASE2.md) — Phase 2 (v0.2–v0.4.x, git-native team share) ·
> [`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"). Current: **v0.7.0**.
> [`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**.

---

Expand Down Expand Up @@ -387,3 +388,4 @@ 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 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`.
11 changes: 11 additions & 0 deletions docs/WEB-UI.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ in `~/.tre-mem/web.pid` (self-healing: a stale pidfile is cleared automatically)
- **Overview** — a branch graph: every branch with its tagged-observation count,
pin count, last-active time, and the current `HEAD` highlighted. Click a branch
to drill in.
- **Grove** — the second-brain view: an Obsidian-style force graph of the repo's
shared memory (the project trunk + branches + contributors + facts) beside a
contributor leaderboard. Facts are sized as young shoots (pins) and mature culms
(graduated facts); contributor node size tracks their value score (pins ×1,
graduated ×3). Earns playful badges (Gardener of the week, Most rooted, Longest
streak, First sprout), exports a shareable PNG card, and replays grove growth
over time. Hover for tooltips; click a branch/fact to drill into **Branch
detail**, click a contributor to highlight their shoots. When nothing has been
shared yet, it falls back to `git log` authors so the grove is never empty.
- **Branch detail** — pinned decisions, facts graduated from that branch, and the
tagged activity timeline.
- **Team memory** — the point of the dashboard: every pinned decision and
Expand Down Expand Up @@ -97,6 +106,8 @@ The SPA is served over a dependency-light `node:http` server. Endpoints, all
| `/api/pins` | all pins for the project |
| `/api/graduated` | all graduated facts for the project |
| `/api/share-status` | pending export / shared / graduated counts |
| `/api/contributors` | contributor leaderboard (value score + badges) |
| `/api/graph` | force-graph nodes + edges for the Grove view |
| `/api/search?q=&branch=` | branch-aware search (full or degraded) |
| `/api/observation/:id` | observation detail (`full` mode only) |
| `/api/events` | SSE live-update stream |
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tre-mem",
"version": "0.8.0",
"version": "0.9.0",
"description": "Branch-aware shared memory layer for AI coding tools. Sidecar on top of claude-mem.",
"keywords": [
"claude-code",
Expand Down Expand Up @@ -53,11 +53,13 @@
"cac": "^7.0.0",
"chokidar": "^4.0.3",
"chromadb": "^3.4.3",
"d3-force": "^3.0.0",
"picocolors": "^1.1.1",
"simple-git": "^3.36.0"
},
"devDependencies": {
"@types/better-sqlite3": "^7.6.13",
"@types/d3-force": "^3.0.10",
"@types/node": "^22.10.0",
"@types/react": "^19.2.16",
"@types/react-dom": "^19.2.3",
Expand Down
39 changes: 39 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions scripts/build-web.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ const html = `<!doctype html>
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Ctext y='20' font-size='20'%3E%F0%9F%8E%8B%3C/text%3E%3C/svg%3E"
/>
<script>${themeBoot}</script>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Baloo+2:wght@500;600;700;800&family=Be+Vietnam+Pro:wght@400;500;600;700&display=swap&subset=vietnamese,latin"
/>
<link rel="stylesheet" href="/app.css" />
</head>
<body>
Expand Down
27 changes: 27 additions & 0 deletions src/git/identity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,30 @@ export async function gitAuthor(cwd: string): Promise<string | null> {
return null;
}
}

/**
* Commit authors on a branch, most-frequent first, with their commit counts.
* Used as the solo/unshared fallback for the Grove view: when nothing has been
* shared yet, contributors are inferred from `git log --format=%an`. Best-effort
* — returns `[]` on any failure (no repo, unknown branch). Never throws.
*/
export async function branchAuthors(
cwd: string,
branch: string,
): Promise<Array<{ name: string; commits: number }>> {
if (!existsSync(cwd)) return [];
try {
const raw = await simpleGit(cwd).raw(['log', '--format=%an', branch]);
const counts = new Map<string, number>();
for (const line of raw.split('\n')) {
const name = line.trim();
if (name === '') continue;
counts.set(name, (counts.get(name) ?? 0) + 1);
}
return [...counts.entries()]
.map(([name, commits]) => ({ name, commits }))
.sort((a, b) => b.commits - a.commits || a.name.localeCompare(b.name));
} catch {
return [];
}
}
Loading
Loading