# iEvo

Self-evolving AI agent framework — interactive console tool.

## Interactive model

iEvo is an **interactive console tool** like Claude Code itself — not a set of separate CLI commands. The primary interface is the **Textual TUI** (must-have).

```
ievo (start)
  ├── Textual TUI — interactive dashboard (agents, pipeline, evolution, files)
  ├── Generate session ID
  ├── Check Claude auth → if missing, prompt login → save to config
  ├── Telemetry opt-in (first run) → Sentry + evolution sharing
  └── When Claude needed → Docker sandbox starts interactively
      ├── Full native Claude Code access preserved (user is NOT limited)
      ├── iEvo orchestration layered on top (agents, teams, tools)
      ├── .claude/ mounted for session persistence + hooks
      └── Session ID passed for continuity (--resume)
```

Key principle: **preserve full native Claude Code access + add orchestration on top**. Never limit what the user can do with Claude.

## Project

- **Name**: ievo
- **Language**: Python 3.13+
- **Framework**: Typer + Rich + Textual (TUI)
- **Package manager**: uv (hatchling build)
- **Entry point**: `ievo` → TUI dashboard | `ievo <cmd>` → CLI

## Architecture

```
src/ievo/
├── cli.py              # Typer app, registers all commands
├── commands/           # One file per command group (all deprecated, use TUI)
│   ├── init.py         # ievo init — project scaffold
│   ├── add.py          # ievo add — install agent .md to .claude/agents/
│   ├── remove.py       # ievo remove
│   ├── update.py       # ievo update — self-update + agent update
│   ├── list_cmd.py     # ievo list
│   ├── run.py          # ievo run — launches Claude session
│   ├── team.py         # ievo team (Phase 2)
│   ├── learn.py        # ievo learn log/push/status — EVO
│   ├── cortex.py       # ievo cortex build — build Cortex repo from sources
│   └── dev.py          # ievo dev new/test/publish
├── core/               # Business logic
│   ├── config.py       # Paths, URLs, project root detection
│   ├── project.py      # .ievo/manifest.yaml manifest model
│   ├── agent.py        # agent.yaml model (AgentPackage — legacy format)
│   ├── docker.py       # Docker sandbox (check, build, command builder)
│   ├── registry.py     # Marketplace registry (fetch, cache, bundled fallback)
│   └── updater.py      # CLI self-update (check PyPI, install via uv/pip)
├── marketplace/        # Bundled marketplace content (shipped in pip package)
│   ├── __init__.py     # Access layer: bundled_agent_path(), list_bundled_agents(), etc.
│   ├── registry.yaml   # Agent catalog (name, version, model, deps, category)
│   ├── agents/*.md     # 12 agent definitions (YAML frontmatter + instructions)
│   ├── templates/*.md  # 7 spec templates (REQ, CR, PLAN, QUESTION, etc.)
│   └── skills/         # 2 skills (evo, backlog)
├── tui/                # Textual TUI dashboard (must-have)
│   └── app.py          # IevoApp: agents table, pipeline, evo log, file tree
└── utils/
    └── console.py      # Rich theme, success/error/info helpers
```

## Data map — what iEvo saves

### Global (`~/.ievo/`)

| File | Purpose |
|------|---------|
| `config.json` | default_model, marketplace_url, github_auth, telemetry opt-in |
| `credentials.enc` | encrypted secrets (RSA + AES-256-GCM) |
| `ievo_key` / `ievo_key.pub` | RSA key pair (generated on first init) |
| `cache/registry.yaml` | marketplace registry cache |

### Project (`./.ievo/`)

| File | Purpose |
|------|---------|
| `.ievo/manifest.yaml` | project manifest (name, version, agents, overrides) |
| `.claude/settings.local.json` | Claude Code hooks config (PreCompact → claude-mem) |
| `.claude/hooks/precompact_save.py` | session observation extractor → SQLite |
| `.claude/agents/<name>.md` | installed agents (single .md with YAML frontmatter) |
| `spec/` | SDD specs (requirements, changes, questions, templates) |
| `plans/` | architecture plans (PLAN-REQ-xxx.md) |
| `agents/` | legacy agents directory (kept for backward compat) |
| `.github/workflows/` | CI/CD for spec-writer, architect, coder agents |

### Per-Agent (`.claude/agents/<name>.md`)

New format — single markdown file with YAML frontmatter:

```markdown
---
name: spec-writer
description: Converts features into atomic requirements
model: sonnet
---

# Spec Writer

Agent instructions here...
```

Installed by `ievo add`. Source priority: bundled (`src/ievo/marketplace/agents/`) → GitHub download → placeholder.

### Per-Agent legacy (`./agents/<name>/`)

Legacy directory format (still supported for reading, not used for new installs):

| File | Purpose |
|------|---------|
| `agent.yaml` | manifest (model, sandbox config, tools, hooks, deps, disclosure) |
| `ROLE.md` | system prompt (loaded as `--system-prompt`) |
| `EVOLUTION_LOG.md` | self-evolution history (errors → lessons) |
| `memory/CONTEXT.md` | persistent context across sessions |
| `memory/DECISIONS.md` | architectural decisions log |
| `memory/VOCABULARY.md` | domain-specific terms |
| `memory/HISTORY.md` | session history index with Status column |
| `memory/sessions/NNN/plan.md` | session plan (goals, steps) |
| `memory/sessions/NNN/log.md` | session log (what happened, commits, errors) |

## Session journal convention

Same convention as Eva. Every agent maintains a session journal.

**Structure:**
```
agents/<name>/memory/
  HISTORY.md              ← index with STATUS column
  sessions/
    001/
      plan.md             ← session plan (written when plan is approved)
      log.md              ← session log (written during/after session)
    002/
      plan.md
      log.md
```

**HISTORY.md format:**
```
| # | Date | Topic | Status | Key outcome |
|---|------|-------|--------|-------------|
```

Statuses: `planned` → `in_progress` → `completed`

**Session plan (`plan.md`)**: written as soon as the plan is approved, BEFORE starting implementation. Contains: goals, phases, steps, decisions to make.

**Session log (`log.md`)**: written during and after the session. Things change during implementation — the log captures what actually happened. Contains: what was built, commits, errors, pending items.

**Session plan = first priority**: as soon as a plan is approved, IMMEDIATELY save it. Update throughout the session. If the session crashes with a stale plan, recovery is blocked.

### Project-level sessions (future)

```
.ievo/
  sessions/
    <session-id>/
      meta.yaml           ← session metadata (id, start time, agents used, model)
      log.md              ← session log (auto-generated from Claude transcript)
```

## Startup data flow

```
ievo (first run)
  ├── Generate session ID (UUID)
  ├── Check ~/.ievo/config.json for Claude auth
  │   └── If missing: run `claude auth` → save token
  ├── Check telemetry opt-in (first run only)
  │   └── "Help improve iEvo?" → save to config.json
  │       ├── Yes: enable Sentry (hardcoded DSN), enable evolution sharing
  │       └── No: local-only mode
  ├── Start Docker sandbox (if agents require it)
  │   ├── Mount project → /workspace
  │   ├── Mount .claude/ → /workspace/.claude  (session persistence)
  │   ├── Pass CLAUDE_CODE_OAUTH_TOKEN
  │   └── Pass --resume <session-id> for continuity
  └── Transparent Claude Code session with agents loaded
```

Sentry DSN (hardcoded): `https://09d4c54e8ac3b9cc79efea50bf635efb@o4510962988482560.ingest.us.sentry.io/4510963845758976`

## Key patterns

- **Project detection**: `find_project_root()` walks up looking for `.ievo/manifest.yaml`
- **Bundled marketplace**: 12 agents, 7 templates, 2 skills shipped inside the pip package (`src/ievo/marketplace/`). CLI works fully offline
- **Registry load order**: cache (`~/.ievo/cache/registry.yaml`) → bundled (`marketplace/registry.yaml`) → empty. GitHub fetch updates the cache
- **Agent install**: bundled `.md` → GitHub download (`download_agent_md`) → placeholder. Agents deploy as single `.md` files to `.claude/agents/<name>.md`
- **Agent format**: single `.md` with YAML frontmatter (`---\nname: ...\nmodel: ...\n---`). Parsed by `parse_agent_frontmatter()`. Legacy `agents/<name>/agent.yaml` still supported for reading
- **Run**: Docker sandbox by default, `--local` for direct Claude CLI. Builds command with ROLE.md as system prompt + memory files + `--allowedTools`
- **Docker sandbox**: agents run in `ievoai/sandbox:latest` container with project as volume mount. Tool-level isolation via `--allowedTools` (not `--network none` — Claude needs API access). Auto-builds image if missing
- **TUI**: `ievo` without args launches Textual dashboard (must-have, 4 tabs: Agents, Pipeline, Evolution, Files)
- **Self-update**: `ievo update --self` checks PyPI for newer version, installs via uv/pip. Startup version check warns if outdated
- **Async**: registry fetch/download use httpx async

## Commands reference

```bash
# Project lifecycle
ievo init [name]                    # Create .ievo/, spec/, plans/, agents/
ievo add <agent> [agent...]         # Pull from marketplace + resolve deps
ievo remove <agent> [--force]       # Remove agent (checks dependents)
ievo update [agent]                 # Update to latest marketplace version
ievo list [--marketplace]           # Show installed or available agents

# Running
ievo run <agent> [-m msg] [-p file] [--model opus|sonnet|haiku] [--local]
ievo team <agents...>               # Phase 2

# Evolution
ievo learn log <agent> [--last N]   # Show EVOLUTION_LOG.md
ievo learn push [agent]             # Send anonymized logs (opt-in)
ievo learn status [agent]           # Evolution stats

# Cortex
ievo cortex build [--source path]   # Build Cortex repo from sources

# Development
ievo dev new <name>                 # Scaffold agent locally
ievo dev test <agent>               # Run eval suite (Phase 2)
ievo dev publish <agent>            # Publish to marketplace (Phase 2)
```

## Dependencies

- typer — CLI framework
- rich — terminal formatting
- textual — TUI dashboard (from Rich creators)
- pyyaml — YAML parsing (manifest.yaml, agent.yaml, registry.yaml)
- httpx — async HTTP for marketplace registry
- pydantic — data validation (future use)
- cryptography — RSA/AES encryption for credentials

## Working rules

- **Session plan = first priority**: as soon as a plan is approved, IMMEDIATELY save it to the agent's `memory/sessions/NNN/plan.md`. Update throughout the session. A stale plan blocks recovery after crashes.
- **Incremental session bookkeeping**: after completing a phase or milestone, immediately update session files (plan.md + log.md) before starting the next phase.
- **100% test coverage**: all code must have 100% test coverage. Run `uv run pytest --cov --cov-report=term-missing` to verify. Never lower `fail_under` threshold.
- **Complete test coverage per feature**: every feature must be covered by ALL relevant test types:
  - **Unit tests**: individual functions, edge cases, error paths, boundary values
  - **Integration tests**: verify actual outcomes — files created, state changed, real objects returned. If a test only asserts `.assert_called_once()` without checking real side effects, it's incomplete
  - **Edge case tests**: empty inputs, missing files, permission errors, malformed data, concurrent access
  - **TUI tests**: use Textual's `app.run_test()` + `Pilot` API for real widget interaction — `pilot.press()`, `pilot.click()`, verify widget content with `app.query_one()`. Don't just mock widgets, test the real Textual app lifecycle. TUI Pilot tests MUST cover: (1) which widget holds focus on launch, (2) keyboard navigation between focusable widgets, (3) primary interactive actions (selection, toggle, confirmation). Mock-only TUI tests cannot detect focus, rendering, or post-mount interaction bugs — a TUI feature is not done until all three are covered by real lifecycle tests
  - Mocks are acceptable ONLY for true external boundaries (Docker daemon, network APIs, subprocess calls). Everything else should use real objects and real filesystems (`tmp_path`)
- **Acceptance before done**: before marking any requirement as complete, self-review: all test types present (unit, integration, edge cases, UI), real outcomes verified (not just mock assertions), coverage on changed files is 100%, docs updated if user-facing. A requirement is NOT done until this passes.
- **Pre-commit after edits**: always run `uv run pre-commit run --files <changed-files>` after editing files.
- **Tests before push**: always run full test suite before pushing.
- **Don't reinvent the wheel**: use existing packages from PyPI. Before adding a dependency, search for well-maintained packages, evaluate stars/downloads/maintenance, then use. Don't write custom implementations for solved problems.
- **Context economy**: don't load all sessions into context. Load only the active session. If that's not enough, search previous sessions and docs on demand.
- **15-minute rule**: Architect decomposes every requirement into tasks of ≤15 minutes. If a task grows beyond this, stop — decompose further, implement what's ready, queue the rest in Backlog. Spec Writer does NOT own time estimates — only Architect does.
- **Git flow — trunk-based**: feature branches are short-lived and branch off `main`; branch naming is `<type>/<ticket>-<description>` (e.g., `feat/REQ-001-auto-init`). Before the first commit of any requirement, check DECISIONS.md for a git-flow entry. If none exists, ASK the user before committing.
- **PR health check — collect all blockers first**: when diagnosing a blocked PR, run all four checks before acting on any one: (1) CI status, (2) merge state and review decisions, (3) branch freshness relative to base, (4) open review comments. Report all blockers in a single summary, then fix.
- **External communications — pattern-level only**: any content posted to a public or external repository must describe error classes and behavior patterns only. No config key names, file paths, module names, code snippets, agent instruction text, or naming conventions. Apply the blog-post test before posting: "Could this be published publicly without revealing how this project is implemented?"
- **Guard conditions over mocks**: when a test fails because a code path calls `input()` or any interactive prompt, find and satisfy the guard condition that determines whether that path is taken — using real config files and real state. Do not mock internal application logic. Mocks are only permitted for true external boundaries (Docker daemon, network APIs, OS subprocess).
- **Evolution propagation**: after logging an evolution finding, update the governing working document (CLAUDE.md for project-wide rules, agent ROLE.md for agent-specific rules) with a concise rule. An evolution lesson is not complete until it appears in the document agents actually load.
- **Single canonical predicate**: when the same logical condition (e.g., "project exists") must be checked in multiple places, define it once and call it everywhere. Never re-implement the check inline with slight variations — logical-condition drift between cooperating functions creates dead states users cannot escape.
- **Integration tests for guard chains**: for any guard condition that gates a user-facing action triggered by another function, write an integration test that exercises the full trigger chain end-to-end (e.g., detector → prompt → init-guard), not just the guard in isolation. Isolated guard tests miss deadlocks that only appear when two functions disagree on the same condition.

## Related repos

- [ievo-ai/eva](https://github.com/ievo-ai/eva) — mother agent (private)
- [ievo-ai/ievo.ai](https://github.com/ievo-ai/ievo.ai) — landing page
- ievo-ai/marketplace — agent registry
- ievo-ai/sdk — dev template
- ievo-ai/curator — cross-agent pattern curator

## Conventions

- Use `from ievo.utils.console import success, error, info, warn, step` for output
- All commands are separate files in `commands/`
- Async operations (network) use `asyncio.run()` at command level
- Project root required: use `require_project()` which exits with error if no `.ievo/manifest.yaml`
