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
34 changes: 34 additions & 0 deletions plugins/herdr-layerfs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Parallel agent workspaces in [Herdr](https://herdr.dev) without copying your repo. Each workspace the plugin opens is a LayerFS Branch: forking one copies no file bytes, closing it saves it, and after Herdr or the machine restarts the plugin mounts the branches again and reopens their workspaces.

## What it adds

Herdr actions, also runnable from a shell or by an agent as `node <plugin_root>/lfs.mjs <command>`:

- `init` (**LayerFS: import this repo**): imports the focused repo's `HEAD` as a LayerFS project.
- `fork` (**LayerFS: new branch workspace**, or **new named branch**): forks the project, or the focused branch after saving it, and opens it as a workspace `lfs:<name>` whose first pane is inside the branch.
- `commit`: saves the workspace on its branch.
- `drop`: discards the workspace without saving.
- `reopen`: mounts a saved branch again.
- `list` (**LayerFS: status**): projects and branches.

Two hooks: closing a LayerFS workspace saves and unmounts it; on Herdr startup, branches whose mounts are gone are mounted and reopened.

## How it works

The plugin calls `@layerfs/agent` from short Node processes that Herdr starts for each action and hook, and keeps a small state file mapping Herdr workspaces to branches. In docker mode (LayerFS `main`, Linux) the branch lives in the LayerFS Sandbox container and the workspace's pane enters it with `docker exec`.

On Debian 13 (4 vCPU, Docker 29, Herdr 0.9.5) with a 603 MB repo: import 3.2 s, a new branch workspace 0.07–0.19 s, save 0.06 s, discard 0.4 s. A server restart and a reboot-style restart (containers stopped) both brought back all 3 workspaces with their saved files.

`herdr plugin action invoke` acts on the workspace focused in Herdr's UI, so agents should run `lfs.mjs` directly: it then uses their own pane's workspace.

## You need

- Linux with Docker, Node 22+, Herdr 0.9+, and Rust 1.85.1 to build LayerFS.
- A LayerFS checkout at the pinned commit with the daemon and native binding built, a runtime image with git and a shell, and a Docker volume (the install prompt covers all three).

## Limits

- Docker mode reads the Store inside the Docker volume, so Herdr runs as a user that can read `/var/lib/docker/volumes` (root on a default install).
- Edits a branch has not saved are lost if the machine reboots.
- Uncommitted changes in the source repo are not imported; `HEAD` is.
- The repository also has a native mode for macOS (each branch a host folder and a linked git worktree, shown under the repo in Herdr's sidebar). It needs LayerFS PR #427, which is not on `main`, so it is not part of this listing.
11 changes: 11 additions & 0 deletions plugins/herdr-layerfs/install.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Install LayerFS for Herdr {{version}} for me, in docker mode, on this Linux machine.

1. Check the tools: Linux, `node --version` (22 or newer), `docker info`, `herdr --version` (0.9 or newer), `git`, `jq`, and `cargo +1.85.1 --version` (if Rust 1.85.1 is missing, ask me before installing it with rustup). If anything is missing or this is not Linux, stop and tell me.
2. Clone https://github.com/Ephemeral-AI-Lab/layerfs and check out commit {{layerfs.commit}} ({{layerfs.version}} or later on main). Use exactly this commit: it is the one this version was tested with.
3. In that checkout build the daemon and the native binding: `cargo +1.85.1 build --manifest-path core/Cargo.toml --release --locked -p layerfs-daemon`, then `npm --prefix api install` and `npm --prefix api run build:native`. Expect `core/target/release/layerfs-daemon` and `api/binding/layerfs.node`.
4. Build the runtime image and the volume: `printf 'FROM node:22-trixie\nRUN apt-get update && apt-get install -y --no-install-recommends git procps\n' | docker build -t layerfs-rt:herdr -`, then `docker volume create layerfs-herdr`.
5. Install the plugin at the pinned commit: `herdr plugin install jiangkoumo/herdr-plugin-layerfs --ref {{commit}} --yes`.
6. Write the config: copy the plugin's `config.env.example` to `"$(herdr plugin config-dir layerfs)/config.env"`, keep the docker block, and set `LAYERFS_API` and `LAYERFS_DAEMON` to the checkout from step 2, `LAYERFS_IMAGE` to the output of `docker image inspect layerfs-rt:herdr | jq -r '.[0].Id'`, `LAYERFS_VOLUME=layerfs-herdr`, and `LAYERFS_STORE_PATH=/var/lib/docker/volumes/layerfs-herdr/_data/global/store.sqlite`. Herdr must run as a user that can read that path (root on a default Docker install); if it does not, stop and tell me.
7. Check it on a scratch repo: `git clone --depth 20 {{repository}} /tmp/lfs-check`, open it as a Herdr workspace (`herdr workspace create --cwd /tmp/lfs-check --label lfs-check`), then from a shell in that workspace run `LFS=$(herdr plugin list --plugin layerfs --json | jq -r '.result.plugins[0].plugin_root')`, `node "$LFS"/lfs.mjs init`, `node "$LFS"/lfs.mjs fork check` and `node "$LFS"/lfs.mjs list`. Expected: `imported`, `check ready`, and one open branch `check`. Then discard it from the new workspace with `node "$LFS"/lfs.mjs drop` and delete `/tmp/lfs-check`.

Report what you ran and what each step returned. If anything fails, stop and give me the exact error, and don't retry it on your own.
8 changes: 8 additions & 0 deletions plugins/herdr-layerfs/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions plugins/herdr-layerfs/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"schema": 1,
"id": "herdr-layerfs",
"name": "LayerFS for Herdr",
"summary": "Each Herdr workspace is a LayerFS Branch: a new one in ~0.2 s, restored after restart.",
"authors": [{ "name": "jiangkoumo", "github": "jiangkoumo" }],
"license": "MIT",
"repository": "https://github.com/jiangkoumo/herdr-plugin-layerfs",
"tags": ["herdr", "agents", "workspaces", "multi-agent", "terminal"],
"platforms": ["linux"],
"uses": ["agent"],
"host": { "name": "Herdr", "url": "https://herdr.dev" },
"status": "preview",
"versions": [
{
"version": "0.3.3",
"commit": "d2bf1a854bded0a696381d04721a0c79d5af8cb0",
"date": "2026-10-26",
"layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" },
"notes": "A failed fork or reopen now stops the Sandbox daemon that attempt created, through SDK sandbox.close, once its mounts are cleaned up; reused or shared daemons keep running."
},
{
"version": "0.3.2",
"commit": "f2e7ba6b99fd14956de2a312ae7cef1ec4e908eb",
"date": "2026-10-26",
"layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" },
"notes": "A failed fork is kept as a closed Branch with its error (reopen or drop to recover); reopen can adopt an existing Store Branch without a plugin record; fork from an agent pane outside the repo opens the Branch under the source repo."
},
{
"version": "0.3.1",
"commit": "34dd08cc29dc587ab041e087511d0ce6520efa0c",
"date": "2026-10-26",
"layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" },
"notes": "drop [name] now cleans up closed Branches directly (no remount); optional --delete-git-branch deletes the Git branch only when merged. Store data is kept, so drop does not reclaim space."
},
{
"version": "0.3.0",
"commit": "2bbd70880fcee75d832ebe9a093d231782b93475",
"date": "2026-10-26",
"layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" },
"notes": "Adds Branch merge (reports real conflicts, ignores each Branch's root .git link), keeps state until drop cleanup succeeds, and resolves the current Branch from cwd outside Herdr."
},
{
"version": "0.2.1",
"commit": "dba17fb30d46acb7daba5ba61e4e0048a01e3d43",
"date": "2026-10-25",
"layerfs": { "version": "v1.1.0", "commit": "ad78914e36b258b360daf13aec164b5655814689" },
"notes": "First listing: docker mode on LayerFS main verified end to end on Debian 13; agents fork from their own pane."
}
]
}