Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 30 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,10 @@ Over time, agents build a library of reliable capabilities. Simple skills become
## Quick Start

```python
from pathlib import Path
from py_code_mode import Session, FileStorage
from py_code_mode.execution import SubprocessExecutor, SubprocessConfig
from py_code_mode import Session

storage = FileStorage(base_path=Path("./data"))

# SubprocessExecutor provides process isolation (recommended)
config = SubprocessConfig(tools_path=Path("./tools"))
executor = SubprocessExecutor(config=config)

async with Session(storage=storage, executor=executor) as session:
# Agent writes code with tools, skills, and artifacts available
# One line setup - auto-discovers tools/, skills/, artifacts/, requirements.txt
async with Session.from_base("./.code-mode") as session:
result = await session.run('''
# Search for existing skills
results = skills.search("github analysis")
Expand All @@ -60,6 +52,33 @@ skills.create(
''')
```

**Need process isolation?** Use subprocess:

```python
async with Session.subprocess("~/.code-mode") as session:
...
```

**Full control?** The convenience methods above are shortcuts. For production deployments, custom storage backends, or container isolation, use the component APIs directly:

```python
from py_code_mode import Session, FileStorage, RedisStorage
from py_code_mode import SubprocessExecutor, SubprocessConfig, ContainerExecutor, ContainerConfig

# Custom storage
storage = RedisStorage(url="redis://localhost:6379", prefix="myapp")

# Custom executor
config = SubprocessConfig(tools_path="./tools", python_version="3.11", cache_venv=True)
executor = SubprocessExecutor(config=config)

# Full control
async with Session(storage=storage, executor=executor) as session:
...
```

See [Session API](./docs/session-api.md) and [Executors](./docs/executors.md) for complete documentation.

**Also ships as an MCP server for Claude Code:**

```bash
Expand Down
23 changes: 10 additions & 13 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,20 +43,10 @@ The storage directory will contain `skills/` and `artifacts/` subdirectories.
### As a Python Library

```python
from pathlib import Path
from py_code_mode import Session, FileStorage
from py_code_mode.execution import SubprocessConfig, SubprocessExecutor
from py_code_mode import Session

# Create storage backend for skills and artifacts
storage = FileStorage(base_path=Path("./data"))

# Configure executor with tools path (SubprocessExecutor recommended for most use cases)
config = SubprocessConfig(tools_path=Path("./tools"))
executor = SubprocessExecutor(config=config)

# Create a session
async with Session(storage=storage, executor=executor) as session:
# Run agent code
# One line setup - auto-discovers tools/, skills/, artifacts/, requirements.txt
async with Session.from_base("./data") as session:
result = await session.run('''
# Search for existing skills
results = skills.search("data processing")
Expand All @@ -81,6 +71,13 @@ print(greeting)
print(f"Result: {result.value}")
```

**Need process isolation?**

```python
async with Session.subprocess("~/.code-mode") as session:
...
```

### With Claude Code (MCP)

Once installed, the MCP server provides these tools to Claude:
Expand Down
116 changes: 106 additions & 10 deletions docs/session-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,104 @@ Complete reference for the Session class - the primary interface for py-code-mod
Session wraps a storage backend and executor, providing a unified API for code execution with tools, skills, and artifacts.

```python
from pathlib import Path
from py_code_mode import Session, FileStorage
from py_code_mode.execution import SubprocessExecutor, SubprocessConfig
from py_code_mode import Session

storage = FileStorage(base_path=Path("./data"))
config = SubprocessConfig(tools_path=Path("./tools"))
executor = SubprocessExecutor(config=config)

async with Session(storage=storage, executor=executor) as session:
# Simplest: auto-discovers tools/, skills/, artifacts/, requirements.txt
async with Session.from_base("./.code-mode") as session:
result = await session.run("tools.curl.get(url='https://api.github.com')")
```

---

## Constructor
## Convenience Constructors

### from_base()

One-liner for local development. Auto-discovers resources from a workspace directory.

```python
Session.from_base(
base: str | Path,
*,
timeout: float | None = 30.0,
extra_deps: tuple[str, ...] | None = None,
allow_runtime_deps: bool = True,
sync_deps_on_start: bool = False,
) -> Session
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `base` | `str \| Path` | Workspace directory path. |
| `timeout` | `float \| None` | Execution timeout in seconds. None = unlimited. |
| `extra_deps` | `tuple[str, ...]` | Additional packages beyond requirements.txt. |
| `allow_runtime_deps` | `bool` | Allow deps.add()/remove() at runtime. |
| `sync_deps_on_start` | `bool` | Install configured deps when session starts. |

**Auto-discovers:**
- `{base}/tools/` - Tool definitions (YAML files)
- `{base}/skills/` - Skill files (Python)
- `{base}/artifacts/` - Persistent data storage
- `{base}/requirements.txt` - Pre-configured dependencies

**Example:**

```python
async with Session.from_base("./.code-mode") as session:
await session.run("tools.list()")
```

### subprocess()

Process isolation via subprocess with dedicated virtualenv. Same auto-discovery as `from_base()`.

```python
Session.subprocess(
base_path: str | Path,
*,
timeout: float | None = 60.0,
extra_deps: tuple[str, ...] | None = None,
allow_runtime_deps: bool = True,
sync_deps_on_start: bool = False,
python_version: str | None = None,
cache_venv: bool = True,
) -> Session
```

**Example:**

```python
async with Session.subprocess("~/.code-mode") as session:
await session.run("import pandas; pandas.__version__")
```

### inprocess()

Fastest execution, no isolation. Same auto-discovery as `from_base()`.

```python
Session.inprocess(
base_path: str | Path,
*,
timeout: float | None = 30.0,
extra_deps: tuple[str, ...] | None = None,
allow_runtime_deps: bool = True,
sync_deps_on_start: bool = False,
) -> Session
```

**Example:**

```python
async with Session.inprocess("~/.code-mode") as session:
await session.run("1 + 1")
```

---

## Direct Constructor

For full control, use the constructor directly with explicit storage and executor.

```python
Session(
Expand All @@ -34,9 +117,22 @@ Session(
| Parameter | Type | Description |
|-----------|------|-------------|
| `storage` | `StorageBackend` | Required. FileStorage or RedisStorage instance. |
| `executor` | `Executor` | Optional. Defaults to SubprocessExecutor if not provided. |
| `executor` | `Executor` | Optional. Defaults to InProcessExecutor. |
| `sync_deps_on_start` | `bool` | If True, install pre-configured deps when session starts. |

**Example:**

```python
from py_code_mode import Session, FileStorage, SubprocessExecutor, SubprocessConfig

storage = FileStorage(base_path=Path("./data"))
config = SubprocessConfig(tools_path=Path("./tools"))
executor = SubprocessExecutor(config=config)

async with Session(storage=storage, executor=executor) as session:
...
```

---

## Lifecycle Methods
Expand Down
35 changes: 35 additions & 0 deletions src/py_code_mode/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,30 @@
ToolNotFoundError,
ToolTimeoutError,
)

# Execution (commonly needed at top level)
from py_code_mode.execution import (
CONTAINER_AVAILABLE,
SUBPROCESS_AVAILABLE,
Capability,
Executor,
InProcessConfig,
InProcessExecutor,
)

# Conditionally import optional executors
if SUBPROCESS_AVAILABLE:
from py_code_mode.execution import SubprocessConfig, SubprocessExecutor
else:
SubprocessConfig = None # type: ignore[assignment, misc]
SubprocessExecutor = None # type: ignore[assignment, misc]

if CONTAINER_AVAILABLE:
from py_code_mode.execution import ContainerConfig, ContainerExecutor
else:
ContainerConfig = None # type: ignore[assignment, misc]
ContainerExecutor = None # type: ignore[assignment, misc]

from py_code_mode.session import Session

# Storage backends (commonly needed at top level)
Expand All @@ -45,6 +69,17 @@
"StorageBackend",
"FileStorage",
"RedisStorage",
# Execution
"Executor",
"Capability",
"InProcessExecutor",
"InProcessConfig",
"SubprocessExecutor",
"SubprocessConfig",
"ContainerExecutor",
"ContainerConfig",
"SUBPROCESS_AVAILABLE",
"CONTAINER_AVAILABLE",
# Errors
"CodeModeError",
"ToolNotFoundError",
Expand Down
Loading
Loading