# agent-environments A Nix flake library for declaring environments for coding agents ([Claude Code](https://github.com/sadjow/claude-code-nix) and [Codex](https://github.com/sadjow/codex-cli-nix)) as modules, and for launching them under separate logins. An **environment** is a set of modules: instructions (`CLAUDE.md` / `AGENTS.md`), skills, and agent settings. A **launcher** is a command that starts one agent in one environment, with its own state directory `~/.local/agent-environments/` (passed as `CLAUDE_CONFIG_DIR` / `CODEX_HOME`). Different ids mean different logins, so `claude-work-acc` and `claude-pers-acc` can share one environment but keep separate credentials. ## Use ```nix { inputs.agent-environments.url = "github:/agent-environments"; inputs.nixpkgs.follows = "agent-environments/nixpkgs"; outputs = { agent-environments, nixpkgs, ... }: let pkgs = nixpkgs.legacyPackages.x86_64-linux; inherit (agent-environments.lib) mkEnvironment mkSkill mkSkillModule; my-skill = mkSkill { inherit pkgs; name = "my-skill"; src = ./skills/my-skill; # a directory containing SKILL.md runtimeInputs = [ pkgs.jq ]; # put on PATH for the agent skills = [ ]; # other mkSkill packages it needs }; in { packages.x86_64-linux.default = (mkEnvironment { inherit pkgs; modules = [ (mkSkillModule my-skill) # declares modules.skills.my-skill.enable { claude.enable = true; codex.enable = true; modules.skills.my-skill.enable = true; instructions.style.text = "Prefer small commits."; claude.settings.includeCoAuthoredBy = false; codex.settings.approval_policy = "on-request"; launchers = { claude-work-acc.agent = "claude"; claude-pers-acc.agent = "claude"; codex-work-acc.agent = "codex"; }; } ]; }).package; }; } ``` Runnable examples: [`examples/basic`](examples/basic) and [`examples/multi-account`](examples/multi-account). The root flake exposes only `lib` and a default environment without skills: `nix shell .#default` puts its `claude` and `codex` launchers on `PATH`. ## Options | Option | Meaning | |---|---| | `claude.enable`, `codex.enable` | Make the agent available to launchers. | | `claude.package`, `codex.package` | Agent package; defaults come from this flake's inputs. | | `claude.settings` | JSON passed with `--settings` (layered over the account's `settings.json`). | | `claude.plugins` | Plugin directories loaded with `--plugin-dir`; nothing is installed into the account. | | `claude.mcpServers` | MCP servers loaded with `--mcp-config` (the `mcpServers` object of an MCP JSON file). Logins stay in the account: run `/mcp` once. | | `codex.settings` | TOML linked as `$CODEX_HOME/agent-environments.config.toml` and selected with `--profile agent-environments` (layered over the account's `config.toml`). | | `instructions..{text,order}` | Fragments of `CLAUDE.md` / `AGENTS.md`, sorted by `order` (default 1000), then name. | | `skills.packages` | `mkSkill` packages to enable. Their skill dependencies are added automatically. | | `packages` | Packages put on every launcher's `PATH`, e.g. tools that hooks in `claude.settings` call. | | `modules.skills..enable` | Declared by `mkSkillModule`; adds that skill. | | `launchers..{agent,id,stateDir}` | A command. `id` defaults to the command name; `stateDir` defaults to `~/.local/agent-environments/`; it must be absolute or start with `~/`, and is taken literally. | | `launchers..sharedDirs.` | Share one directory of the state dir with another launcher, e.g. `projects = "claude-work"` for sessions and memory across logins. A managed symlink to that launcher's `/`, created if missing. | | `companions.enable` | Put every launcher of the environment on the agent's `PATH` (as companions, which cannot start companions themselves) and add a generated `companions` skill listing each one with its non-interactive call. Launchers export `AGENT_ENV_LAUNCHER` (their command name). | ## What a launcher does On every start, under a lock on the state dir: 1. Links the managed files into the state dir: the instruction file, one link per skill in `skills/`, and the Codex profile. Everything it manages is listed in `.agent-environments-managed`. 2. Removes managed links that are no longer wanted. Anything not in the manifest (your own skills, Codex's `skills/.system`, logins, `settings.json`, `config.toml`) is never touched. 3. Refuses to start if a managed path is occupied by something it does not own, such as a hand-written `CLAUDE.md`. Move it away (or turn it into an `instructions` fragment) and start again. 4. Sets `CLAUDE_CONFIG_DIR` / `CODEX_HOME`, puts `packages` and the skills' runtime inputs on `PATH`, and runs the agent. Launchers that share an `id` share a login and managed files. The most recent launch decides which files are linked, so only share an id between launchers of the same environment. ## Development ```sh nix flake check # behaviour tests (stub agents) + real Codex integration check nix fmt -- $(git ls-files '*.nix') nix build ./examples/basic --override-input agent-environments path:$PWD ```