Archived (July 2026). This project is no longer maintained or installed on the original development machine. It is preserved as a reference for a time when models were changing how they read and edit code. The core idea—putting structural context beside source files and keeping a durable project map—may still be useful as model tooling continues to evolve. Treat this repository as historical material, not a supported installation path.
trimmrog gives every project an autonomous knowledge-curator. It runs as a local daemon on the repos you point it at, keeps a precise call graph fresh as sibling shard files (*.calls, *.deps, *.impact next to every source file), and on every real, settled commit spawns its agent — pi-reflect — to maintain an architecture canvas, a prose wiki, and its own evolving memory in a sibling git repo.
Pi-reflect is the agent character inside trimmrog. It reads the watched repo (read-only) and writes only inside its own knowledge dir. It is not a coding agent for the watched repo — it's a librarian. Its mission, verbatim from the seeded IDENTITY.md:
"My artifacts (canvas + wiki + my own memory) are derivative — they exist so a human (or future me) can walk into this project cold and understand it without reading every file. I am explicitly NOT a coding agent. I am a librarian who keeps the index up to date."
Run trimmrog on five repos and you have five pi-reflect instances, each with its own IDENTITY.md, its own MEMORY.md index, its own learnings/, its own playbook/. The agent in trimmrog/ learns trimmrog's quirks; the agent in syke/ learns syke's. Each instance commits to its own git repo, so git log is the audit trail of how each librarian learned its project.
The shards aren't just for the agent. Pi-reflect treats them as ground truth — its seeded ENVIRONMENT.md says verbatim "if the diagram or wiki contradicts the shards, the diagram or wiki is wrong" — and any other AI assistant in the watched repo (Claude Code, Codex, Cursor, vim+LLM, VS Code) reads them on the same Read it would have made for the source anyway. No MCP, no embeddings, no cloud.
Read top-to-bottom: THE BOUNDARY (watched repo read-only; knowledge dir is the agent's only write surface). FILESYSTEM (where each thing lives). WHAT PI MAINTAINS (canvas/scripts/diag_*.py + canvas/*.excalidraw, wiki/*.md, agents/pi-reflect/{IDENTITY,ENVIRONMENT,MEMORY,learnings,playbook}). AGENT-CALLABLE COMMANDS (trimmrog reflect now …). VERSIONING (knowledge dir is its own git repo; git log --oneline is the audit trail). OPERATOR SURFACE (trimmrog tail for the live dashboard; reflect.log TSV for raw data).
This diagram is dogfooded — it's rendered by docs/diagrams/diag_pi_reflect.py, the same kind of Python source pi-reflect writes under knowledge/<repo>/canvas/scripts/ on every run. The script POSTs Excalidraw elements to a self-hosted canvas server (mcp_excalidraw) over HTTP and the snapshot is exported to PNG. Five of these diagrams ship under docs/img/ (overview · freshness · compaction · pi-reflect · modules); sources in docs/diagrams/.
Three milestones. Each ends with a verification command.
brew install go node
npm install -g gitnexus
go install github.com/saxenauts/trimmrog@latest
cd <some-repo> && trimmrog init .Verify:
trimmrog doctor # daemon alive · shards fresh · hooks wired · gitnexus reachable
ls src/*.calls.* # sibling shards now exist next to sourceThe daemon's reflect loop is also polling at this point, but pi-reflect will not actually fire until step 2 — the spawn is gated on pi being on PATH.
Pi-reflect is invoked via pi --print --no-session --mode json --append-system-prompt … (see internal/picaller/picaller.go). Put a Claude-Code-CLI-compatible binary on PATH as pi:
which pi # must resolve to a Claude-Code-CLI-compatible binary
pi --versionIf your local agent CLI has another name, alias it (ln -s "$(which claude)" ~/.local/bin/pi) or edit picaller.go to call yours by name.
git clone https://github.com/yctimlin/mcp_excalidraw && cd mcp_excalidraw
npm ci && npm run build
PORT=3737 npm run canvas &
open http://127.0.0.1:3737/ # leave the browser tab open — the export endpoint needs a connected frontendBack in your watched repo:
trimmrog reflect now "first run — seed canvas + wiki for this repo"
trimmrog tail # live dashboard: daemon, recent runs, knowledge-dir commitsAfter the first cycle, every subsequent commit on the watched repo (after a 2-minute quiet window) spawns the agent automatically. Without the canvas server, wiki and learnings still work — only the canvas surface needs it.
internal/reflect/scaffold.go seeds the agent's full working layout on first run. The agent owns these files after that — re-running the scaffolder never clobbers anything that exists:
agents/pi-reflect/IDENTITY.md— who I am, my mission, my disposition. The agent edits this when its role evolves.agents/pi-reflect/ENVIRONMENT.md— what's around me; where to validate against ground truth (the source code; the trimmrog shards).agents/pi-reflect/MEMORY.md— index oflearnings/.agents/pi-reflect/learnings/<YYYY-MM-DD>-<slug>.md— one mistake or insight per file. What happened (link to reflect.log timestamp + commit SHA), why it was wrong (root cause), what I'll do differently (concrete rule).agents/pi-reflect/playbook/<slug>.md— promoted patterns. A learning gets promoted here once it has fired correctly twice on subsequent runs.
Plus three skills under .agents/skills/ (auto-discovered as Agent Skills):
canvas-update— edit the rightcanvas/scripts/diag_*.py, re-run it (the script POSTs Excalidraw elements to the canvas server), re-export the snapshot. Edit the script, not the.excalidrawexport.wiki-update— write Starlight prose pages with frontmatter, one concept per file, citations to source files in the formpath/to/file.go:42. Internal links omit.md(Starlight serves extension-less URLs).observe-traces— read the previous twentyreflect.logrows and write a learning when a recurring pattern appears.
On every run the agent reads its IDENTITY/ENVIRONMENT/MEMORY index, reads git diff <last_processed_commit>..HEAD from the watched repo, decides what changed structurally, updates the canvas and/or wiki, optionally writes a learning, then commits everything in its cwd as a single commit named pi-reflect: <one-line>. If the diff is meaningless (whitespace, generated files), it exits cleanly without committing — the seeded disposition explicitly prefers doing nothing over doing something noisy.
Run pi-reflect on a repo for a month and you accumulate:
- A canvas that doesn't lie. Every structural change gets reflected in
canvas/scripts/diag_*.pyand re-exported ascanvas/*.excalidraw. The seededENVIRONMENT.mdmakes the rule explicit: "if the diagram or wiki contradicts the shards, the diagram or wiki is wrong" — and pi-reflect knows to fix it. - A wiki that grows. One concept per file, line-cited. New concepts get a page; obsolete pages get deleted.
- A learnings folder. Real mistakes pi-reflect made on this codebase: boundary violations it doesn't repeat, refactors it now spots, file shapes that surprised it.
- A playbook. Hardened rules specific to your codebase. After a learning fires correctly twice, it's promoted from
learnings/intoplaybook/<slug>.md. The agent gets less wrong. - A
git log. The knowledge dir is its own git repo.git log --onelinein there is a complete audit trail of how the librarian learned the project, one commit per cycle.
Coming back to a repo three months cold is no longer expensive — the knowledge dir is your onramp. Read the wiki, look at the canvas, scan the playbook. The librarian already mapped what changed.
Five active projects means five living knowledge dirs maintained by five separate pi-reflect instances. Each one runs independently — same seed scaffold (scaffoldAgentIdentity in internal/reflect/scaffold.go), divergent identity, memory, learnings, and playbook accumulating against each repo's actual quirks. Without a single root and a single dashboard you'd be tailing five terminals to know what each librarian was up to. trimmrog gives you both:
~/Documents/personal/trimmrog/knowledge/ # default; override via $TRIMMROG_KNOWLEDGE_ROOT
├── trimmrog/ # IDENTITY · MEMORY · learnings · playbook · canvas · wiki
├── syke/ # a different agent identity, growing for syke
├── personal-site/ # its own
└── …
The agent in trimmrog/ knows what trimmrog is; the agent in syke/ knows what syke is. They do not share state.
TRIMMROG_KNOWLEDGE_ROOT puts the whole fleet under one tree (and lets you move it onto a different drive). trimmrog tail --repo <path-to-watched-repo> --once pulls a snapshot of any one repo's librarian without cd-ing in. trimmrog doctor --repo <path> does the same for daemon health.
Independently of whether the agent is firing, trimmrog's daemon keeps three plain-English siblings next to every indexed source file. The shards are pi-reflect's ground truth and they're useful on their own (the substrate loop runs whether or not pi is on PATH):
src/auth.py ← what you write
src/auth.calls.py [callers] Authenticate ← login (cmd/login.go:42)
[callees] Authenticate → hashPassword (auth/hash.go:18)
src/auth.deps.py [imports] crypto/sha256 · internal/session
[imported-by] cmd/login.go · http/middleware.go
src/auth.impact.py risk: medium direct: 4 transitive: 9
Capped at 800 entries per [callers]/[callees]/[affects] list. Go shards prepend //go:build ignore so go build skips them. Refreshed in ~3 s through the Claude Code PostToolUse hook (UDP, fire-and-forget) and ~5 s through fsnotify for everything else.
The shard layer is a local replica of Supermodel's on-disk pattern, sitting on top of GitNexus's call-graph engine. Full sample shards in examples/.
flowchart LR
A["any AI assistant<br/>(Claude Code · Codex · Cursor<br/>vim+LLM · VS Code · cat)"]
S["src/auth.py"]
SH["src/auth.calls.py<br/>src/auth.deps.py<br/>src/auth.impact.py"]
KD["knowledge/<repo>/<br/>canvas · wiki · agents"]
D["trimmrog daemon<br/>(UDP + fsnotify + .git/HEAD)"]
PR["pi-reflect agent<br/>(spawned per-commit)"]
A -- reads --> S
A -- reads --> SH
A -- reads --> KD
A -- edits --> S
S -. "edit" .-> D
D == "2s debounce<br/>→ gitnexus + render<br/>→ atomic writes" ==> SH
S -. "commit lands" .-> D
D == "stable + quiet<br/>→ flock + spawn pi" ==> PR
PR == "canvas / wiki / learnings<br/>committed to its own git" ==> KD
trimmrog's daemon reacts to three kinds of events:
- Edit (file changed). UDP for Claude Code, fsnotify for everyone else → 2 s debounce → gitnexus + render → atomic writes. Hooks return in <100 ms; the daemon does the slow work.
- Commit (HEAD advanced). Stability detector waits a 2-minute quiet window, takes a flock on the knowledge dir, spawns
piundercwd = knowledge/<repo>/. Agent updates canvas/wiki/learnings and commits to the knowledge dir's git. One TSV row per cycle inreflect.log(cost · tokens · duration · stop reason). - Compact (Claude Code session compaction).
PreCompactwrites.trimmrog/snapshot.md;SessionStart{compact}re-injects it viaadditionalContextso the agent stays oriented after/compact.
<watched-repo>/
├── .gitignore # managed: .trimmrog/, .gitnexus/, *.calls.*, ...
├── AGENTS.md # managed block
├── .claude/settings.json # project-local hooks
├── .gitnexus/ # GitNexus owns this (LadybugDB)
├── .trimmrog/ # daemon state: pid, port, status, snapshot
└── src/{auth.py, auth.calls.py, auth.deps.py, auth.impact.py}
knowledge/<watched-repo>/ # the librarian's home
├── .git/ # the agent's own git history
├── AGENTS.md # auto-loaded by the agent (cwd root)
├── .agents/skills/{canvas-update,wiki-update,observe-traces}/SKILL.md
├── agents/pi-reflect/ # IDENTITY · ENVIRONMENT · MEMORY · learnings · playbook
├── canvas/{scripts/diag_*.py + diagram_lib.py, *.excalidraw}
├── wiki/{index.md, <concept>.md}
├── reflect.log # TSV of every cycle (gitignored)
└── last_processed_commit # short SHA bookmark (gitignored)
| Command | Purpose |
|---|---|
trimmrog init [path] |
Idempotent per-repo setup (creates .trimmrog/, hooks, daemon) |
trimmrog analyze [path] |
Run gitnexus + render all shards |
trimmrog watch [path] |
Long-lived daemon (UDP + fsnotify + reflect poll) |
trimmrog hook |
PostToolUse handler (UDP fire-and-forget) |
trimmrog snapshot |
PreCompact handler |
trimmrog restore |
SessionStart{compact} handler |
trimmrog skill |
Print the trimmrog instruction block |
trimmrog doctor |
Self-checks: daemon, shards, hooks, snapshot, gitnexus |
trimmrog reflect [now] [note] |
Trigger one pi-reflect cycle (auto-fire if HEAD advanced; now to force; trailing words become an operator note to the agent) |
trimmrog tail [--once] |
Live operator dashboard for pi-reflect activity |
- Boundary. Watched repo is read-only for the agent. Knowledge dir is its only write surface, enforced by
cwd = knowledgeDirat spawn. Writes outside surface asflags=boundaryinreflect.log; the agent records a learning. - One flock guards both entry paths.
cmd/reflect.goand the daemon's reflect-scheduler both go throughreflect.Run, which holds a flock on the knowledge dir. Concurrent fires wait; they don't double-spawn. - Hooks finish in <100 ms. UDP is fire-and-forget; the daemon does the slow work.
- Shards. Atomic writes, path-traversal guard, 800-entry chunking on
[callers]/[callees]/[affects],//go:build ignoreprefix on Go shards. - No cloud, no MCP server of our own, no embeddings, no telemetry. Everything is local; gitignored where appropriate; never pushed by trimmrog itself.
Full invariants in AGENTS.md.
A representative real-repo measurement on spf13/cobra (36 .go source files, no vendored deps), MacBook Pro M-series:
| Stage | Time |
|---|---|
gitnexus analyze (call-graph build) |
~2.7 s |
Render (108 shard files: 36 × .calls/.deps/.impact) |
~6 s |
Total trimmrog init (analyze + render + scaffolding) |
~8.6 s |
Steady-state per-edit cost is millisecond-level — gitnexus analyze is incremental and only re-renders the touched file's shards. Initial full analyze is the only slow path; if you have a >10k-file repo, run trimmrog init . --skip-analyze and let the daemon's first reindex finish in the background.
Trimmrog is autonomous; when something goes wrong, it has to do so safely. Here is exactly what mutates and what defends each surface.
What pi-reflect can mutate: only the per-repo knowledge directory at $TRIMMROG_KNOWLEDGE_ROOT/<repo>/. The watched repo is read-only.
Boundary defense (post-hoc). Before each cycle: snapshot the watched repo's HEAD SHA + stash its WIP. After: re-fingerprint. If the fingerprint moved, git reset --hard <pre-pi SHA> + git clean -fd reverts pi's writes. The deferred unstash restores your WIP. Verified by integration tests + the boundary flag in reflect.log. Step B will replace this with a pre-hoc tool-call hook (see docs/STEP-B-PI-AGENT-CORE.md).
Cost defense (three layers).
- Per-HEAD breaker. 3 boundary violations on the same SHA → that SHA is skipped permanently (until
trimmrog reflect resume). - Consecutive breaker. 5 violations in a row across any HEADs → the daemon halts firing entirely.
- Cost-window cap. If the last hour's auto-fire spend exceeds $5, the next auto-fire is skipped. Manual
trimmrog reflect nowbypasses (you typed it). - All three are persisted to
.trimmrog/breaker.jsonso a daemon crash-loop doesn't reset them.
Daemon crash → safe restart. last_processed_commit advances only on a clean run, so a restart resumes correctly. The breaker counters survive in breaker.json. Stash entries are tracked by SHA, robust to concurrent stash shifts.
fsnotify scope. Only .git/HEAD and .git/refs/heads/* events trigger pi-reflect. git fetch, git tag, git gc, and worktrees are deliberately ignored — they don't change watched-repo HEAD semantics.
One flock. Manual and auto fires both go through one flock on the knowledge dir. Concurrent invocations wait; they don't double-spawn.
Trimmrog wraps two external CLIs:
- GitNexus (
gitnexuson PATH, npm-installable, Node 18+). The call-graph engine. Required. @mariozechner/pi-coding-agent(pion PATH). The agent runtime pi-reflect drives. Required for pi-reflect; trimmrog still works as a shard renderer without it.
Both are NPM packages on a single maintainer (Mario Zechner / pi-mono, Abhigyan Patwari / GitNexus). Mario explicitly does not design pi as a daemon-friendly binary. Step B (planning doc at docs/STEP-B-PI-AGENT-CORE.md) tracks the path to swap the shell-out for a persistent sidecar using @mariozechner/pi-agent-core directly.
If pi breaks compatibility, the daemon will still keep your shards fresh; only pi-reflect cycles will fail (visible in trimmrog doctor and reflect.log).
| Doc | What it covers |
|---|---|
| docs/ARCHITECTURE.md | System design, data flow, the ten subcommands, pi-reflect subsystem |
| docs/INSTALL.md | Install, prerequisites, verification, agent setup |
| docs/INTEGRATION.md | Wiring per tool — Claude Code, Codex, OpenCode, vim, Cursor, MCP |
| docs/TROUBLESHOOTING.md | Symptom → diagnosis → fix |
| docs/EDGE_CASES.md | Multi-repo, .git/* events, gitignore mid-session, pi-reflect in-flight |
| docs/INTERNALS.md | Package-by-package code map for changes |
| docs/PATCHES.md | Recipes — add a language, swap substrate, retune pi-reflect |
| docs/diagrams/ | Source for the architecture diagrams |
| CHANGELOG.md | What changed and when |
trimmrog is one project; this is the credit split.
Borrowed: the shard format and the on-disk principle. The three-file shape (*.calls, *.deps, *.impact next to source) and the bet that passive shards beat MCP-style active queries for read-heavy structural work both come from Supermodel (github.com/supermodeltools). Their argument that an agent spends ~90% of its tool budget on map-building is in their blog post; their mcpbr regression harness showed that adding an MCP query tool on SWE-bench Verified hurt resolution on 10 of 12 repos. trimmrog replicates the on-disk part locally on top of GitNexus's call-graph engine.
Original: everything else trimmrog does. The daemon (UDP + fsnotify + stability detector); the agent — pi-reflect — and its self-managed memory hierarchy (IDENTITY.md · ENVIRONMENT.md · MEMORY.md index · learnings/ · playbook/); the promotion rule (a twice-fired learning becomes a playbook entry); the boundary-enforced spawn-per-commit loop; the per-repo fleet under one root ($TRIMMROG_KNOWLEDGE_ROOT/<basename>/); the operator surface (trimmrog tail live dashboard, reflect.log TSV, trimmrog doctor self-checks); compaction recovery via PreCompact snapshots and SessionStart re-injection. The agent character is named "pi-reflect" because it shells out to a pi binary (via internal/picaller/picaller.go).
Built with multiple agentic coding tools (Claude Code, Codex, plus trimmrog's own shards once the daemon was running). The invariants in AGENTS.md hold regardless of which tool is editing.
PolyForm Noncommercial 1.0.0 — same as GitNexus. Personal / research use only. See LICENSE.

