Declarative repository context and skills for agents on your local or cloud machines.
envit.json
{
"repos": [
"sqlite/sqlite",
"PostHog/posthog-js",
"getsentry/sentry-javascript",
"earendil-works/pi",
"pierrecomputer/pierre",
"cloudflare/cloudflare-os"
],
"skills": {
"mattpocock/skills": ["grill-me", "grilling"],
"emilkowalski/skills": "*",
"dmmulroy/anti-slop": "*",
"backnotprop/bro": "bro",
"obra/superpowers": "*",
"remotion-dev/skills": { "pick": "*", "modelInvocable": false },
"cursor/plugins": { "path": "pstack", "pick": ["interrogate", "blast-radius"] }
}
}
external repos that your agents can use as context, and skills that they load
curl -fsSL https://envit.dev/install.sh | sh
brew install plannotator/tap/envit
cargo install envit
npm install -g envit
| scope | |
|---|---|
envit init | This project. Writes envit.json for the repo. Commit it: teammates, CI, and cloud agents reproduce the same context. |
envit init -g | This machine. Writes ~/.envit/envit.json. Skills land in ~/.agents/skills/ and ~/.claude/skills/, for every project. |
envit sync | Materializes both scopes. Output labels each global line. |
$ envit sync
fetched sqlite 1b3f6d2a94c0 3184 files · 94.2 MB
fetched posthog-js 9c41d2e807aa 2311 files · 18.7 MB
fetched sentry-javascript 4b0e91cc35d8 3928 files · 24.1 MB
fetched pi 77aa10f3d92b 612 files · 4.8 MB
fetched pierre 5e2c0b7a13fd 1187 files · 9.6 MB
skill grill-me e1f0a9b62c44 (mattpocock/skills)
skill grilling e1f0a9b62c44 (mattpocock/skills)
global bro 0537a9fc42c0 (backnotprop/bro)
✓ synced 5 repo(s) · 45 skill(s) · 1 global skill(s) · 151.4 MB added to store
$ envit sync
✓ synced 5 repo(s) · store unchanged # 9 ms
What agents see
Skills need no explanation to the agent. They land where every agent already looks, and load like any other skill.
Repos
envit stores each repo once per machine, in ~/.envit/store/,
keyed by commit. Every project links to that one copy. Ten projects that
use sqlite share one checkout. Nothing is duplicated on your disk.
my-app/
├── .agents/skills/grill-me/ # agents discover skills here natively
├── .claude/skills/grill-me/ # Claude Code reads its own dir
└── .envit/
├── AGENTS.md # generated inventory: what is here, at which commit
├── CLAUDE.md → AGENTS.md # symlink, so Claude Code reads it natively
└── repos/
├── sqlite → ~/.envit/store/…/sqlite/<commit>/ # symlink; full source, read-only
└── posthog-js → ~/.envit/store/…/posthog-js/<commit>/
Repo context announces itself the same way. Each sync generates
.envit/AGENTS.md with a CLAUDE.md symlink
beside it. The file lists each repo, its commit, and its resolved path.
When an agent enters the directory, it reads the inventory the way it
reads any AGENTS.md. If the project root already has an
AGENTS.md or CLAUDE.md, init and
sync append a short fenced note that points there. Set
"agentsMd": false in envit.json to opt out.
1. Repo context
Declare repos in envit.json:
{
"repos": [
"PostHog/posthog-js",
"gitlab:group/internal-lib",
{ "source": "getsentry/sentry-javascript", "ref": "9.44.0", "update": "frozen" }
]
}
envit sync writes the source to
.envit/repos/. The checkouts are read-only, at the commits
that envit.lock.json records. Commit both files. Then any
machine reproduces the same context:
$ envit sync --frozen # CI / sandboxes: lockfile only, bit-identical
2. Skills
Pick single skills from any repo, as the manifest above shows. You do not install a full collection.
sync links each skill into
.agents/skills/ (the
cross-agent convention) and
.claude/skills/. A global manifest at
~/.envit/envit.json applies skills to every project on the
machine:
$ envit init -g
$ envit sync -g
global web-design-guidelines dd089a8c752c (vercel-labs/agent-skills)
Does envit replace my skills installer?
No. Keep it. Use it to find skills and try them.
envit does one thing those tools do not: it reads a file.
envit.json says which repos and which skills a project
uses, and envit sync makes any machine match it. You
compose the set: which skills from which repos, which ones the agent
can invoke on its own. One file holds all of it, next to the code,
reviewed in a PR. Skills you installed by hand or with another tool
stay where they are. envit only manages what is declared in its file.
On disk
~/.envit/store/
├── git/github.com/tokio-rs/tokio.git # one bare repo per remote
└── checkouts/github.com/tokio-rs/tokio/
└── a1b2c3d4e5f6…/ # one read-only tree per commit
my-app/.envit/repos/tokio → ~/.envit/store/checkouts/…/a1b2c3d4e5f6…/
The store keys each checkout by commit SHA. Two projects on the same commit share the same bytes. When a branch moves upstream, the contents of a project do not change. An update is one atomic symlink flip.
Commands
| command | |
|---|---|
envit sync | resolve → fetch → checkout → link → lock. Refreshes branch-tracking repos at most once per 24 h. |
envit sync --frozen | lockfile only. Errors on drift. For CI and sandboxes. |
envit sync --offline | no network. Links what the store has. |
envit add <source> | owner/repo, gitlab:…, any https git URL. --ref for branch/tag/sha. |
envit update [name] | move branch-tracking repos to current remote heads. Prints old → new. |
envit pin <name> | freeze at the locked commit. unpin resumes tracking. |
envit status | ref, commit, link health, staleness. Never touches the network. |
envit skills | declared skills, both scopes. |
envit gc | delete checkouts no project references. --dry-run first. |
Constraints
- No daemon.
psshows nothing between invocations. - One static binary, 4 MB. Git is embedded (gitoxide): no system git, no libgit2, no OpenSSL.
- Plain git protocol. GitHub, GitLab, Codeberg, sourcehut, self-hosted. No forge APIs.
- Kill switches:
--offline,--frozen,ENVIT_NO_REFRESH=1,"update": "frozen". - Everything on disk is inspectable: bare git repos, directories, JSON, symlinks.
Agent skill
skills/envit/ in the repo is an
Agent Skill that teaches an agent
how to use envit. Install it with the skills CLI:
npx skills add plannotator/envit
Install
envit is pre-release. Build it from source with a stock Rust toolchain:
$ git clone https://github.com/plannotator/envit && cd envit
$ cargo build --release
$ ./target/release/envit --help