# ClawAgents Python

A lean, full-stack agentic protocol. ~2,500 LOC.

## Quick Start

```bash
pip install -e .
```

Create a `.env`:

```env
PROVIDER=gemini
GEMINI_API_KEY=AIza...
GEMINI_MODEL=gemini-3-flash-preview
STREAMING=1
CONTEXT_WINDOW=128000
MAX_TOKENS=4096
```

### One-Line Agent

```python
from clawagents import create_claw_agent

agent = create_claw_agent("gemini-3-flash")
result = await agent.invoke("List all Python files in src/")
print(result.result)
```

### With Instruction

```python
agent = create_claw_agent("gpt-5", instruction="You are a code reviewer.")
result = await agent.invoke("Review the code and suggest improvements")
```

### CLI

```bash
python -m clawagents --task "Find all TODO comments in the codebase"
```

## API

### `create_claw_agent(model, instruction, ...)`

| Param | Type | Default | Description |
|:---|:---|:---|:---|
| `model` | `str \| LLMProvider \| None` | `None` | Model name or provider. `None` = auto-detect from env |
| `instruction` | `str` | `None` | What the agent should do / how it should behave |
| `tools` | `list` | `None` | Additional tools. Built-in tools always included |
| `skills` | `str \| list` | auto-discover | Skill directories. Default: checks `./skills`, `./.skills`, `./skill`, `./.skill` |
| `memory` | `str \| list` | auto-discover | Memory files. Default: checks `./AGENTS.md`, `./CLAWAGENTS.md` |
| `streaming` | `bool` | `True` | Enable streaming |
| `on_event` | `callable` | `None` | Event callback |

### Built-in Tools

Every agent includes these — no setup needed:

| Tool | Description |
|:---|:---|
| `ls` | List directory with size + modified time |
| `read_file` | Read file with line numbers + pagination |
| `write_file` | Write/create file (auto-creates dirs) |
| `edit_file` | Replace text (supports `replace_all`) |
| `grep` | Search — single file or recursive with glob filter |
| `glob` | Find files by pattern (`**/*.py`) |
| `execute` | Shell command execution |
| `write_todos` | Plan tasks as a checklist |
| `update_todo` | Mark plan items complete |
| `task` | Delegate to a sub-agent with isolated context |
| `use_skill` | Load a skill's instructions (when skills exist) |

### Hooks (Convenience Methods)

```python
agent = create_claw_agent("gemini-3-flash", instruction="Code reviewer")

# Block dangerous tools
agent.block_tools("execute", "write_file")

# Or whitelist only safe tools
agent.allow_only_tools("read_file", "ls", "grep", "glob")

# Inject context into every LLM call
agent.inject_context("Always respond in Spanish")

# Limit tool output size
agent.truncate_output(3000)
```

**Advanced:** Raw hooks are also available for custom logic:

```python
agent.before_llm = lambda messages: messages      # modify messages before LLM
agent.before_tool = lambda name, args: True        # return False to block
agent.after_tool = lambda name, args, result: result  # modify tool results
```

## Auto-Discovery

The factory automatically discovers project files:

| What | Default locations checked |
|:---|:---|
| **Memory** | `./AGENTS.md`, `./CLAWAGENTS.md` |
| **Skills** | `./skills`, `./.skills`, `./skill`, `./.skill`, `./Skills` |

Pass explicit paths to override: `memory="./docs/AGENTS.md"`, `skills=["./my-skills", "./shared-skills"]`

## Memory System

### Project Memory
Loads `AGENTS.md` files and injects content into every LLM call. Use for project context.

### Auto-Compaction
When conversation exceeds **75% of `CONTEXT_WINDOW`**:
1. Full history **offloaded** to `.clawagents/history/compacted_*.json`
2. Older messages **summarized** into `[Compacted History]`
3. Last 6 messages kept intact

## Environment Variables

| Variable | Default | Description |
|:---|:---|:---|
| `PROVIDER` | auto-detect | `openai` or `gemini` |
| `OPENAI_API_KEY` | — | OpenAI API key |
| `OPENAI_MODEL` | `gpt-5-nano` | OpenAI model |
| `GEMINI_API_KEY` | — | Gemini API key |
| `GEMINI_MODEL` | `gemini-3-flash-preview` | Gemini model |
| `STREAMING` | `1` | `1` = enabled, `0` = disabled |
| `CONTEXT_WINDOW` | `128000` | Token budget for compaction |
| `MAX_TOKENS` | `4096` | Max output tokens per response |

## Testing

```bash
pip install -e ".[dev]"
python -m pytest tests/ -v
```
