envit

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 initThis project. Writes envit.json for the repo. Commit it: teammates, CI, and cloud agents reproduce the same context.
envit init -gThis machine. Writes ~/.envit/envit.json. Skills land in ~/.agents/skills/ and ~/.claude/skills/, for every project.
envit syncMaterializes 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 syncresolve → fetch → checkout → link → lock. Refreshes branch-tracking repos at most once per 24 h.
envit sync --frozenlockfile only. Errors on drift. For CI and sandboxes.
envit sync --offlineno 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 statusref, commit, link health, staleness. Never touches the network.
envit skillsdeclared skills, both scopes.
envit gcdelete checkouts no project references. --dry-run first.

Constraints

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