Metadata-Version: 2.4
Name: agent-venv
Version: 0.1.0
Summary: Run coding-agent CLIs in an isolated profile and workspace.
Project-URL: Homepage, https://github.com/JacobLinCool/agent-venv
Project-URL: Repository, https://github.com/JacobLinCool/agent-venv
Author: Jacob Lin
License: MIT
License-File: LICENSE
Keywords: agent,claude-code,codex,isolation,sandbox
Classifier: Development Status :: 3 - Alpha
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Provides-Extra: dev
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff>=0.4; extra == 'dev'
Description-Content-Type: text/markdown

# agent-venv (Python)

Python implementation of [`agent-venv`](https://github.com/JacobLinCool/agent-venv). See the root README for the project overview and the [`spec/`](../../spec) directory for the cross-language contract.

## Install

```bash
pip install agent-venv
# or, from this monorepo:
uv pip install -e .
```

Requires Python 3.10+.

## Layer 1 — generic environment

```python
import subprocess
from agent_venv import Environment, EnvironmentSpec

# Ephemeral: cleaned up on context exit
with Environment.ephemeral(
    EnvironmentSpec(env_overrides={"CLAUDE_CONFIG_DIR": "$EPHEMERAL_HOME"})
) as env:
    subprocess.run(
        ["claude", "--print", "hi"],
        env={**os.environ, **env.env_overrides},
        cwd="/anywhere",
    )

# Persistent: keyed by name, survives the process
env = Environment.create_or_attach(
    "myapp-skill-x",
    EnvironmentSpec(env_overrides={"CLAUDE_CONFIG_DIR": "$EPHEMERAL_HOME"}),
)
print(env.path, env.env_overrides)
# ...later, from anywhere...
env = Environment.attach("myapp-skill-x")
```

The library **never spawns the agent**. The caller does. The library only manages the profile dir and the env vars to point the agent at it.

## Layer 2 — built-in adapters

`ClaudeCode` and `Codex` are thin wrappers that produce the right `EnvironmentSpec`, including reading host credentials so the agent runs under your subscription.

```python
from agent_venv import Environment, ClaudeCode

with Environment.ephemeral(adapter=ClaudeCode()) as env:
    subprocess.run(
        ["claude", "--print", "hi"],
        env={**os.environ, **env.env_overrides},
    )
```

The adapter copies your `~/.claude/.credentials.json` (or macOS Keychain entry) into the new profile with mode `0600`. The original is not modified.

## Persistent registry

Persistent envs live under `$XDG_DATA_HOME/agent-venv/envs/` (or `~/.local/share/agent-venv/envs/`). Override with `AGENT_VENV_REGISTRY_ROOT` env var or per-call `registry_root=`.

```python
Environment.list()                          # all persistent env names
Environment.attach("name")                  # raises if missing
Environment.destroy_by_name("name")         # removes from disk + registry
env.refresh_credentials()                   # re-copy host credentials
```

## Async

```python
from agent_venv import AsyncEnvironment

async with await AsyncEnvironment.ephemeral(adapter=ClaudeCode()) as env:
    ...
```

## Errors

```python
from agent_venv import errors

try:
    Environment.attach("missing")
except errors.EnvironmentNotFoundError as exc:
    print(exc.kind, exc)  # "EnvironmentNotFound", "..."
```

All errors subclass `errors.AgentVenvError` and carry a `kind: str` matching the spec.

## Conformance

```bash
python -m agent_venv.conformance < requests.ndjson
```

See [`spec/conformance-protocol.md`](../../spec/conformance-protocol.md).
