# Repotector ⬡ **The repo that remembers.** Every AI agent that visits your repo starts from zero: it re-derives the map, re-discovers the conventions, re-breaks the thing the last agent just fixed. Repotector gives the repo itself a memory. An arriving agent knocks at the front door, handshakes, and is handed the map, the standing decisions, and the last visitor's briefing; on the way out it signs the register. The next agent continues in two minutes instead of wandering for an hour — and a refactor doesn't quietly break what already works. Portable and self-contained: pure Node ESM, two dependencies (`@modelcontextprotocol/sdk`, `zod`). Works on any repo — JS/TS deeply, other stacks in honest orientation-lite. ## Install into any repo ```bash cd /path/to/your/repo npx repotector init ``` `init` scans the repo (never fails you on day one — see *grandfathered baseline* below), wires the MCP server into `.mcp.json`, and writes the doorway blocks so every arriving agent knows to handshake. It writes only inside `.repotector/` and inside `REPOTECTOR:BEGIN/END` markers — your prose and config are never overwritten. See [SECURITY.md](./SECURITY.md) for the exact posture. Under `.repotector/`: | File | What it is | |-----------------|----------------------------------------------------------------------| | `intent.json` | The contract: standards, required paths, bounded contexts, canon rules. | | `atlas.json` | The map: exports/imports/purpose/kind per file, routes, components, stack, and a deterministic fingerprint. | | `dna.json` | Reverse-engineered entities + API contracts + intent. | | `baseline.json` | The grandfathered floor — the debt that existed on day one. | | `proof.json` | Gate verdict, regressions vs grandfathered debt. | | `register.jsonl`| The visitor ledger: who entered, when, what they touched, who's inside. | | `dna.inferred.json` | Reverse-DNA clauses for foreign repos (skipped when a `.psx/` mirror exists). | Plus, at the repo root, a regenerated **`JOURNAL.md`** — the diario di bordo, newest-first, that `handshake` serves the tail of so the next agent continues prior work instead of re-deriving it. ## The front door (handshake-first) The MCP server refuses deep tools until an agent calls `handshake` — and the `initialize` response already tells the agent to. In return the agent gets oriented in one call, and its exit is recorded for the next one. - **`handshake({ who, model?, purpose? })`** — orientation, ground rules, live gate verdict, map freshness, passport. Read-only and fast (no tree walk). - **`city_map()`** — intent, stack, built-vs-missing, brain pointers. On a non-JS repo it says *orientation-lite* instead of faking an empty map. - **`find_existing({ intent })`** — REUSE: does it already exist? Don't rebuild. - **`blast_radius({ changedFiles? })`** — PROTECT: transitive dependents + impacted routes/components. Omit `changedFiles` to use the git diff. - **`canon_check({ changedFiles? })`** — INTEGRATE: the repo's canon rules. - **`atlas_query({ query })`** — keyword search across the map. - **`quality_gates()`** — line-budget / structure / secret-hygiene, reported as regressions vs grandfathered debt. - **`register()`** — who's inside now and the full crossing log. - **`journal({ limit? })`** — the repo's recent story: what prior agents did and left unfinished. **`whats_next()`** — the sensible next work, derived from DNA gaps + open threads + TODOs, every suggestion citing its evidence. - **`dna_query({ clause?, topic? })`** — what the repo *specified* (authored from a `.psx/` mirror, or inferred, never merged). **`dna_coverage()`** — per clause: implemented / partial / missing. **`dna_diff({ changedFiles? })`** — which clauses a change touches. - **`depart({ summary? })`** — sign out; the git delta of your visit is recorded even if you forget the summary. Your summary becomes the next agent's briefing. ## Grandfathered baseline — never red on day one A guardian that fails your repo the moment you install it gets uninstalled the moment you install it. So `init` snapshots the debt that already exists (oversize files, tracked secrets, missing paths). Gates then fail **only on regressions** against that floor — a *new* offender, an offender that *grew*, a *new* leak. Pre-existing debt is reported loudly, never blocking. `repotector baseline` re-snapshots after you pay it down. ## v1.2 "Gatekeeper" — from advisor to checkpoint - **Commit guard** — `repotector hooks` installs a pre-commit that runs the gates; `gates` exits non-zero on regressions, so hooks and CI actually block. Grandfathered baseline means it never blocks day-one debt — only new damage. - **Protected paths** — `intent.protect.paths` globs (CI workflows, LICENSE…) that agents must not touch: change-based, never grandfathered, overridden only by editing the intent (an explicit, diffable act). - **Claims** — `claim({ paths })` declares your work zone; overlapping claims from live sessions answer `granted:false` with who/why. Advisory by design (blocking would be theater on a filesystem we don't control); claims die with the session. - **Decision records** — `depart({ decisions: [{ chose, over, because }] })` writes the *why* to the register and projects `DECISIONS.md`; the handshake serves standing decisions and `decisions_query` answers "was this deliberate?" — so agent 2 doesn't undo agent 1's choice. - **`repotector doctor`** — one command, semaphore answer to "how protected is this repo, really?", with a fix for every red. ## v1.5 "Mission" — the single verifiable chain The whole visit becomes one auditable contract: ``` handshake → declare_mission → (auto)claim → work → off-claim / forbidden reconciliation → evidence pack (machine-verified vs agent-declared) → depart → the next agent inherits it all ``` - **`declare_mission({ goal, acceptance, claimPaths?, forbiddenPaths?, risk? })`** — bind the visit to a contract: your zone is auto-claimed (conflicts surfaced), and you get a one-shot briefing (gates, merge status, standing decisions, protected paths). - **Evidence pack at depart** — the register records what the MACHINE verified (gates verdict, trial-merge clean/conflicted, files touched, off-claim, forbidden violations) strictly apart from what the AGENT declared (per-criterion self-report); unreported criteria are listed, never assumed. "Done" becomes something the register can check, not a courtesy. - Other agents see missions in progress at handshake — what you're doing, not just where. ## v1.3 "Merge Guard" — commit without colliding - **merge_check** — a zero-damage TRIAL merge (git merge-tree) of HEAD against the integration base, run BEFORE you commit: reports clean/conflicted with the exact files, each attributed to who holds that zone (live claims — and, in PSX Workbench repos, the Merge Machine's leases via the .psx mirror). CLI: `repotector merge-check [target]` (exit 1 on conflicts, so hooks/CI can gate on it). Zero network: it trial-merges against your LOCAL refs and says so — fetch first for the freshest truth. ## Resilient register — agents die without signing out Sessions that enter and never leave (the agent was killed, the pipe closed) are auto-departed on the next handshake, with the git delta of what they touched. The ledger never fills with immortal open sessions, and a depart carries real work even when nobody called it. ## CLI ```bash npx repotector init # scan, wire .mcp.json, write the doors — day-one green npx repotector refresh # re-derive the map + re-stamp the doorway blocks npx repotector handshake # orientation + live gate + passport (logged visit) npx repotector city-map # built-vs-missing + brain pointers npx repotector dna-coverage # per specified clause: implemented / missing npx repotector whats-next # the sensible next work, with evidence npx repotector journal # the diario di bordo (regenerates JOURNAL.md) npx repotector gates # regressions vs grandfathered debt npx repotector baseline # re-snapshot the grandfathered floor npx repotector register # the visitor ledger npx repotector lock # optional passphrase gate on the deep map npx repotector mcp # start the stdio MCP server ``` ## Docker The image on GHCR wraps the CLI and the MCP server; the guarded repo is mounted, never baked in: ```bash docker run --rm -v "$PWD:/repo" ghcr.io/dir-ai/repotector init docker run --rm -v "$PWD:/repo" ghcr.io/dir-ai/repotector gates docker run -i --rm -v "$PWD:/repo" ghcr.io/dir-ai/repotector mcp # stdio MCP ``` Multi-arch (amd64/arm64), built and smoke-tested by CI on every release tag (see [Dockerfile](./Dockerfile)). ## GitHub Action Guard a repo in CI with one step — grandfathered semantics apply, so the job fails only on regressions, never on day-one debt: ```yaml - uses: dir-ai/repotector@main with: command: gates # default; any CLI command works version: latest # pin an exact npm version for reproducible CI ``` ## Honesty Repotector guards repos, so it holds itself to its own standard. The lock is a compliance signal, not filesystem access control; the register is append-integrity, not tamper-proof; a static badge is self-reported. It spawns only `git`, makes no network calls, and pins exact versions in `.mcp.json`. The full threat model is in [SECURITY.md](./SECURITY.md) — no security theater. ## Determinism The Atlas `fingerprint` is a SHA-256 over sorted source contents (16 hex chars), no timestamps — the same tree always fingerprints the same. Large files (>1MB, generated/minified) are marked, never read into the fingerprint. `builtAtHead` stamps the git sha so freshness can be checked without a walk. ## License MIT © PSX System. The core an agent needs in a repo is free forever; the compounding brain (Genome cloud, authored DNA) is the premium layer.