# Envit

> How to use envit, the repo-context tool — it materializes the source code of a project's dependencies into .envit/repos/ so you can read real library source instead of guessing. Use this skill whenever you see an envit.json or .envit/ directory in a project, whenever you need to read the source of a dependency or open-source library, whenever library context seems missing or stale, or whenever the user mentions envit, repo context, or asks you to add/pin/update a context repo.

- Skill: `plannotator/envit` (Agent Skill)
- Install (CLI): `npx skillmds@latest add plannotator/envit`
- Raw SKILL.md: https://api.skillmd.com/api/skills/plannotator/envit/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: MIT OR Apache-2.0
- Author: plannotator (https://skillmd.com/u/plannotator)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/plannotator/envit

---


# envit — repo context for agents

envit is a virtualenv for repo context. The project's `envit.json` declares
which git repositories an agent should be able to read; `envit sync`
materializes them, read-only and commit-pinned, under `.envit/repos/`.
The bytes live once per machine in a shared store — never edit them, and
never commit `.envit/` (it's gitignored; the manifest and `envit.lock.json` are
committed instead).

## Reading context (the common case — no commands needed)

1. Read `.envit/AGENTS.md` first: it lists every available repo, its
   commit, and its resolved path.
2. Dependency source lives at `.envit/repos/<name>/` — real, complete,
   at the exact commit the lockfile pins. Prefer reading it over guessing
   at a library's behavior from memory.
3. The entries are **symlinks**: `rg` and `fd` skip them by default.
   Use `rg --follow` / `fd -L`, or search the resolved store path from
   AGENTS.md (generated) directly.
4. Everything is **read-only by design**. If you need to modify a
   dependency, that's a fork/vendor decision for the user — say so
   instead of fighting the permissions.

If `.envit/repos/` is missing but `envit.json` exists, run `envit sync`.

## Commands

| Command | Use it to |
|---|---|
| `envit sync` | Materialize everything the manifest declares. Fast when warm (~ms). Advances stale branch-tracking repos at most once per 24h. |
| `envit sync --frozen` | Reproduce the lockfile exactly (CI, sandboxes, teammates). Errors on manifest/lock drift instead of resolving. |
| `envit sync --offline` | Never touch the network; link only what the store already has. |
| `envit add <source> [--ref R] [--name N]` | Declare a new repo. `github:owner/repo`, other forge shorthands, or any https git URL. Omit `--ref` to track the default branch. Does not sync. |
| `envit remove <name>` | Remove a declaration. |
| `envit update [name…]` | Deliberately move branch-tracking repos to current remote heads. Prints `old → new`. |
| `envit pin <name>` | Freeze a repo at its currently locked commit. |
| `envit unpin <name>` | Resume tracking its original ref. |
| `envit status` | Per repo: ref, locked commit, link health, policy/staleness. Never touches the network. |
| `envit gc [--dry-run]` | Remove store checkouts no project references. |

Env vars: `ENVIT_HOME` (store location, default `~/.envit`),
`ENVIT_NO_REFRESH=1` (block all implicit refresh),
`ENVIT_REFRESH_TTL` (e.g. `"1h"`, global staleness window).

## Manifest format (`envit.json`)

JSON: a list of repo links, plus an optional skills object. A bare
`"owner/repo"` string means GitHub at the default branch; an entry needing
options becomes an object:

```json
{
  "repos": [
    "tokio-rs/tokio",
    { "source": "Effect-TS/effect", "ref": "next" },
    { "source": "serde-rs/serde", "ref": "v1.0.200", "update": "frozen" }
  ],
  "skills": {
    "vercel-labs/agent-skills": ["web-design-guidelines"]
  }
}
```

Per-entry keys: `source` (required), `name`, `ref`, `update`
(`auto`/`manual`/`frozen`), `ttl` (e.g. `"1h"`), `history` (`"full"`).
Skills sources also take `path` (subdirectory in a monorepo) and
`modelInvocable` (`true`/`false`): whether the model may invoke the
skill on its own. Overrides the author's controls for both Claude
(`disable-model-invocation`) and Codex (`allow_implicit_invocation`).
Applies per source, or per skill via
`{ "name": ..., "modelInvocable": ... }` in a pick list.
Prefer `envit add`/`remove`/`pin` over hand-editing; CLI edits re-emit
canonical pretty-printed JSON.

## Situations and the right move

**You need source the project doesn't declare yet** (debugging into a
library, checking an API): `envit add github:owner/repo && envit sync`.
Tell the user you added it — it edits their committed manifest.

**Context changed under you mid-task** (files shifted after a sync
advanced a branch): `envit pin <name>` freezes the repo at the commit
you're looking at, instantly. Note the pin in your summary so the user
knows to unpin later.

**Context looks stale** (the lockfile commit is weeks old and you need
newer code): `envit update <name>`. Never assume freshness — `envit
status` shows what's pinned and when remotes were last checked.

**Bootstrapping a fresh checkout or sandbox**: `envit sync --frozen`
gives bit-identical context to the committed lockfile. This is the only
sync mode CI and cloud environments should use.

**Merge conflict in `envit.lock.json`**: don't hand-merge — resolve the
manifest, then run `envit sync`; the lock is machine-regenerated
canonically.

## Semantics worth knowing

- The manifest is intent ("track main"); `envit.lock.json` is fact ("main was
  3f2a9c1 when materialized"). Both are committed.
- Plain `sync` does not chase branches — a locked repo stays put unless
  it is `auto` (the default) *and* its remote hasn't been checked in 24h.
  `update` is the explicit "move now."
- Checkouts are keyed by commit SHA in the store, so two projects pinned
  to different commits coexist and nothing changes behind a project's
  back. Refreshes swap the symlink atomically.
- envit runs no daemons; nothing happens between invocations.

If the `envit` binary is not on PATH, ask the user how it is installed in
this environment (in envit's own development repo it is
`target/release/envit` after `cargo build --release`).

