Invisible, auto-syncing personal storage for any git repo
Project description
Swarf
Invisible, auto-syncing personal storage for any git repo.
Swarf is the metal shavings left on the workshop floor after machining — the byproduct of making something. When you build software (especially with AI agents), you generate a lot of byproduct: research docs, design specs, agent skills, open questions, scratch notes. Swarf gives this material a durable home alongside any project, without touching the project itself.
The problem
You have files that should live near your code but don't belong in the repo:
- Agent-generated research and design docs
- Personal notes, roadmaps, and open questions
- Agent skill files and config (
.copilot/skills/,AGENTS.md) - Security audits, experiment logs, scratch work
Today these files are either untracked and local-only (one rm -rf from
gone) or committed to the repo (polluting history, leaking on public
repos). Neither is good.
How it works
Swarf creates a .swarf/ directory inside your project — regular files,
invisible to the host repo via .git/info/exclude (managed automatically).
A background daemon watches for changes, mirrors them to a central backup
store, and syncs to a remote backend (git or rclone).
my-project/ <- your repo (public)
├── src/
├── tests/
├── .swarf/ <- private storage (auto-excluded from git)
│ ├── docs/research/ <- durable notes
│ ├── docs/design/ <- specs and decisions
│ ├── links/ <- files projected into the host tree
│ │ └── AGENTS.md <- symlinked to ./AGENTS.md
│ └── open-questions.md
└── AGENTS.md -> .swarf/links/AGENTS.md
Files live locally in each project. The daemon mirrors all .swarf/ dirs
to ~/.local/share/swarf/ (a git repo), then pushes to your remote.
Install
# Via pip / uvx (any platform — no Go required)
pip install swarf
# or: uvx swarf
# Via Go
go install github.com/mschulkind-oss/swarf@latest
# From source
git clone https://github.com/mschulkind-oss/swarf && cd swarf
just deploy # builds and copies to ~/.local/bin/
Quick start
cd ~/projects/my-app
swarf init
That's it. If this is your first time, swarf init walks you through
everything:
$ swarf init
No global config found. Let's set one up.
Backend (git/rclone) [git]: git
Remote URL: git@github.com:you/my-swarf.git
✓ Wrote ~/.config/swarf/config.toml
Created .mise.local.toml with enter hook.
✓ Initialized swarf in /home/you/projects/my-app/.swarf
Backend: git
Remote: git@github.com:you/my-swarf.git
Daemon is not running. Install as system service? [Y/n]: y
✓ Installed and started swarf daemon
Next time you run swarf init in another project, it reuses your config
and the daemon picks it up automatically — no prompts.
Rclone backend (Google Drive, Dropbox, S3, etc.)
If you prefer cloud storage over a git repo, install rclone first:
brew install rclone # or: mise use rclone
rclone config
# -> n (new remote), name: gdrive, type: drive
# -> scope: drive.file (option 2 — can only see files rclone created)
# -> auto config: y (opens browser)
Then run swarf init and enter rclone as the backend and gdrive:swarf
as the remote. The drive.file scope means the OAuth token can only access
files swarf created — it cannot read your other Google Drive documents.
Verify your setup
swarf doctor
# ✓ Global config: backend=git, remote=git@github.com:you/my-swarf.git
# ✓ Git remote reachable
# ✓ Daemon is running
# ✓ .swarf/ directory exists
# ✓ .swarf/ is gitignored
# ...
Using swarf
Adding content
Put files directly into .swarf/:
# Research notes, design docs, anything you want backed up
echo "# Architecture Notes" > .swarf/docs/design/architecture.md
echo "# Open Questions" >> .swarf/open-questions.md
The daemon auto-commits and syncs after a 5-second quiet period.
Sweeping files into swarf
Use swarf sweep to move existing files into .swarf/links/ and replace
them with symlinks. The host repo automatically ignores the symlinks via
.git/info/exclude.
# Sweep an existing file into swarf
swarf sweep AGENTS.md
# AGENTS.md -> .swarf/links/AGENTS.md
# Sweep multiple files at once
swarf sweep .copilot/skills/SKILL.md .cursor/rules/project.md
# Verify
ls -la AGENTS.md
# AGENTS.md -> .swarf/links/AGENTS.md
You can also configure auto-sweep in ~/.config/swarf/config.toml to
automatically sweep common files (like AGENTS.md) whenever you cd into
a project. See Configuration.
Checking health
swarf doctor # validate setup: gitignore, git repo, remote, links
swarf status # show all projects, sync state, daemon status
Commands
| Command | Description |
|---|---|
swarf init |
Initialize .swarf/ in current project |
swarf sweep <file>... |
Move files into .swarf/links/ and symlink back |
swarf doctor |
Validate setup and backend health |
swarf status |
Show all projects and sync status |
swarf daemon start |
Start background sync daemon |
swarf daemon stop |
Stop the daemon |
swarf daemon status |
Check if daemon is running |
swarf daemon install |
Install as systemd user service (auto-start on login) |
Daemon as a system service
To have the daemon start automatically on login:
swarf daemon install
This creates a systemd user service. Check logs with:
journalctl --user -u swarf -f
How it works on a company monorepo
The same way. swarf init writes to .git/info/exclude, which is per-repo
and never committed. No changes to the monorepo's .gitignore, no submodules,
no permission needed.
cd ~/work/big-monorepo
swarf init
Your agent config, personal notes, and research are now durable and synced, completely invisible to your coworkers and CI.
How it works with AI agent containers
If you use containerized agents (like yolo-jail), the daemon runs on the host, not inside the container. The container mounts the workspace directory, so file changes from agents are visible to the host filesystem. The daemon picks them up and syncs automatically. Agents never need credentials.
Configuration
Global config lives at ~/.config/swarf/config.toml (created automatically
by swarf init on first run):
[sync]
backend = "git" # or "rclone"
remote = "origin" # git remote or rclone remote path
debounce = "5s" # wait this long after last change before syncing
[auto_sweep]
# Files to automatically sweep into .swarf/links/ when entering a project.
# Only swept if the file exists and isn't already a symlink.
paths = ["AGENTS.md", "CLAUDE.md", ".copilot/skills/"]
This is set once and applies to all projects.
Contributing
See CONTRIBUTING.md for development setup and guidelines.
License
Apache 2.0
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distributions
Built Distributions
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file swarf-0.3.0-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: swarf-0.3.0-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 6.0 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
69f069bf0dd9d3075ff366fef19ba55328cf8e989d6d179e742436b8b85381b6
|
|
| MD5 |
1763cc43280e76d1f6479a20b75871db
|
|
| BLAKE2b-256 |
7385d491270acc55ef38e793d3cd9f3440199a5e2d9f29a0e0ca0c11bfbbde90
|
File details
Details for the file swarf-0.3.0-py3-none-musllinux_1_2_aarch64.whl.
File metadata
- Download URL: swarf-0.3.0-py3-none-musllinux_1_2_aarch64.whl
- Upload date:
- Size: 5.7 MB
- Tags: Python 3, musllinux: musl 1.2+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
909ce19406fce4b7f32b31cfcbba738c735b5c2a48bd58e2cc95c4149b79a85e
|
|
| MD5 |
17b7ac86dcc80807b50c8c8f4202b1f0
|
|
| BLAKE2b-256 |
bb41dd80fa8d04652801d5f823166b5458251d9c9a4bed355e56f0fbfa7a0a11
|
File details
Details for the file swarf-0.3.0-py3-none-manylinux_2_17_x86_64.whl.
File metadata
- Download URL: swarf-0.3.0-py3-none-manylinux_2_17_x86_64.whl
- Upload date:
- Size: 6.0 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe61766190dd9552d2a0de7ae7389f295f99974069596f7994cdf0967d86be9a
|
|
| MD5 |
417917d7405f2e3d04f3cf0ffb6d8ff7
|
|
| BLAKE2b-256 |
a1993d972080426037ce1dfb1a7b3d9c12d2cb3be9611bcfb8268a10b6365f45
|
File details
Details for the file swarf-0.3.0-py3-none-manylinux_2_17_aarch64.whl.
File metadata
- Download URL: swarf-0.3.0-py3-none-manylinux_2_17_aarch64.whl
- Upload date:
- Size: 5.7 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2e13ff0a5cab42be36c2fbdb6e21cc17f175c8ca0dc3c929f559005e1afd331
|
|
| MD5 |
6af73c121fd5fb94e6246970dc2db491
|
|
| BLAKE2b-256 |
a8fb5f43471784c123938e39cae707676c25fbd0ee89e73dd5c25771afc2b152
|
File details
Details for the file swarf-0.3.0-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: swarf-0.3.0-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 5.8 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5232f2cb8d5b3ffe29f926f2d714c7d6d29b26c633b36bc624c2dd14e9aa7da
|
|
| MD5 |
1b98eb177fe93e6346f4f168dd4adf57
|
|
| BLAKE2b-256 |
3c185d6a5835f32f4e7dc78dd2053b09062db67462d9e9e2cc1a873861c37ef7
|
File details
Details for the file swarf-0.3.0-py3-none-macosx_10_9_x86_64.whl.
File metadata
- Download URL: swarf-0.3.0-py3-none-macosx_10_9_x86_64.whl
- Upload date:
- Size: 6.2 MB
- Tags: Python 3, macOS 10.9+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a25f446fdbbaea65e4e361e9f17f9ef65b01f9af722880cc798d3ef826143a13
|
|
| MD5 |
1d87cd222c599e02ddecccaa3cb21614
|
|
| BLAKE2b-256 |
ff731940fef51797adb47a2ae9cf88fafe1f89b6b9a0579ac5f257977eee6dfc
|