Run 5+ AI agents on the same repo at once without them stomping each other.
Plain filesystem + multiple agents = stale reads, overwritten edits, lost work on crash. git-fs gives every agent its own git branch in a shared object store. Edits are commits. Sessions end with a 3-way merge into main, then materialize to disk. No locks, no coordination server, no clone-per-agent.
┌─ agent A ──┐
│ edit foo.ts│──┐
└────────────┘ │
┌─ agent B ──┐ ├──► merge.lock ──► 3-way merge ──► main ──► disk
│ edit bar.ts│──┤
└────────────┘ │
┌─ agent C ──┐ │
│ rm baz.ts │──┘
└────────────┘
| Plain FS + N agents | git-fs | |
|---|---|---|
| Session startup cost | clone / worktree add (seconds–minutes) | git branch (~ms, 0 file copies) |
| Disk per extra agent | full working tree (~repo size × N) | 0 bytes — shared object store |
| Write atomicity | partial writes possible on crash | every edit = 1 git commit, atomic |
| Concurrent edits to repo | last-writer-wins, silent loss | isolated branches, explicit 3-way merge |
| Crash recovery | unsaved buffers gone | git reflog — every tool call is recoverable |
| Audit trail | filesystem mtime, no author | git log — who, what, when, which session |
| Cross-agent visibility | none | siblings read each other's .git-fs/session/ live |
| Merge race protection | none | proper-lockfile on merge.lock — serialized Stop hooks |
| Tooling for LLMs | shell out to git |
first-class MCP: git_fs_read/write/replace/merge/... |
| Install footprint | — | pure JS bundle, no native binary, no platform matrix |
- 🌿 N agents, 1 repo, 0 collisions — each session lives on
agent/<uuid>. - ⚡ Edit-as-commit —
git_fs_replace/write/rmeach produce one commit. Roll back withgit reset. - 🔀 Auto-merge on Stop — exclusive
merge.lock+ 3-way line merge intomain+ checkout to disk. - 🧹 Mergeignore — junk files (
.agent,CONFLICTS.md, your own globs) never reachmain. - 🪟 Worktree-aware — per-worktree by default, or share one bare repo across worktrees via
GIT_FS_REPO. - 📜 Full audit —
git log agent/<id>answers "what did this agent do?" - 🔌 MCP-native — works with any MCP client; ships as a Claude Code plugin.
/plugin marketplace add yesitsfebreeze/git-fs
/plugin install git-fs@git-fs
That's it. On the next session start the plugin:
- Spawns bundled JS (
dist/cli.jsanddist/mcp.js) via the Node already running Claude Code. No binary download, no architecture matrix, no PATH changes. - Auto-initializes any git repo it lands in — creates
.git-fs/and writesEdit/Writedeny rules into the project's.claude/settings.jsonso all edits go through git-fs MCP tools. - Registers the
git-fsMCP server and theSessionStart/Stop/PreToolUse(Read)/PostToolUse(Write|Edit)hooks. - Exposes the
git-fsskill so Claude knows the tool-selection rules.
Verify: session banner shows Branch: agent/<uuid>, and git_fs_branch_list returns the active branches.
Runtime requirement: Node 20+. Claude Code already ships with a compatible runtime, so the plugin works on any OS Claude Code supports.
Install from npm (when published) or clone + build:
git clone https://github.com/yesitsfebreeze/git-fs
cd git-fs/git-fs
npm install
npm run buildThen point your agent at the MCP entry:
// .mcp.json (or your agent's equivalent)
{
"mcpServers": {
"git-fs": {
"command": "node",
"args": ["/abs/path/to/git-fs/dist/mcp.js"]
}
}
}Inside the target repo run node /abs/path/to/git-fs/dist/cli.js init-project to create .git-fs/. Non-Claude agents do not get the auto-merge Stop hook — use git_fs_merge manually at end of session.
/plugin update git-fs@git-fs
cd git-fs
npm install
npm run typecheck
npm test
npm run build # emits ./dist and ../dist (consumed by the plugin)flowchart LR
A[Claude Code session] -- SessionStart --> B[agent/<id> branch]
A -- git_fs_replace / write / rm --> C[(bare git repo: .git-fs/)]
C -- branches --> D[agent/A]
C -- branches --> E[agent/B]
C -- branches --> F[main]
A -- Stop hook --> G{strip mergeignored<br/>+ acquire merge.lock<br/>+ 3-way merge}
G --> F
F -- checkout --> H[Disk]
| Stage | What happens |
|---|---|
| SessionStart | Create agent/<uuid> from main. Seed .git-fs/session/intent.md. |
| Tool calls | git_fs_* MCP tools write commits to the agent branch. |
| Stop | Strip mergeignored (hard defaults .agent, CONFLICTS.md) → take merge lock → 3-way line merge into main → checkout to disk. |
Spec: docs/multi-agent-session.md.
Sibling agents commit to their own agent/<id> branch, not to main. main only catches up at Stop. So mid-session, main does not reflect work other agents have already shipped.
Before editing a file a sibling might also be touching:
git_fs_branch_list— find activeagent/*branches.git_fs_diff ref_a:main ref_b:agent/<sibling>orgit_fs_read ref:agent/<sibling> path:<file>— see their version.- Align to the latest version across all agent branches, not just your own. Otherwise both agents fork from stale state and clobber each other at Stop.
- If a sibling already implemented a similar pattern, mirror it — less merge surface, more coherent code.
Need a sibling's work visible mid-session? Merge it in explicitly with git_fs_merge (or the /merge skill).
Per-worktree (default). Each worktree has its own .git-fs/ at its root. Sessions in worktree A do not see worktree B.
Shared store across worktrees. Set GIT_FS_REPO env var to one absolute bare repo path. In shared mode, git_fs_branch_list returns every agent across every worktree, the merge lock serializes Stop hooks globally, and Stop's final checkout main → cwd still writes into the calling session's own worktree — only the git history is shared.
- isomorphic-git — pure-JS git core.
- node-diff3 — 3-way line merge.
- diff — unified-diff patch output.
- picomatch — mergeignore globs.
- proper-lockfile — cross-platform merge lock.
- esbuild — single-file bundling.
<plugin root>/
├── .claude-plugin/
│ ├── plugin.json # MCP server registration
│ └── marketplace.json # marketplace entry
├── hooks/hooks.json # SessionStart / Stop / PreToolUse / PostToolUse
├── dist/launcher.js # node: hook dispatch to cli.js
├── dist/cli.js # bundled CLI + hooks
├── dist/mcp.js # bundled MCP stdio server
├── skills/git-fs/SKILL.md # tool-selection rules
└── git-fs/ # TypeScript source
MIT — see LICENSE.