-
Notifications
You must be signed in to change notification settings - Fork 1
development
For people working on eVi itself.
python -m venv .venv
.venv\Scripts\Activate.ps1 # Windows PowerShell
# source .venv/bin/activate # Linux / macOS
pip install -e '.[dev,web,mcp,scheduler,downloads,web-tools,stt,computer]'The stt and computer extras pull native deps (PortAudio for
sounddevice, screen capture libs for pyautogui). If you're not actively
touching those areas, skip them.
pytest -q # ~12 s, 183 tests
pytest -q tests/test_agent.py # one file
pytest -q -k "permission" # by keyword
pytest --timeout=15 # ceiling per testThe suite avoids network calls and external processes by:
- Stubbing the OpenAI client with hand-rolled
_FakeClientchunks (seetests/test_agent.py). - Patching
httpxwithMockTransportfor backend HTTP boundaries (tests/test_backends.py,tests/test_websearch.py,tests/test_image_comfy.py). - Spawning real
gitfor worktree tests, but they're skipped whengitisn't on PATH. - Stubbing the MCP
ClientSessionfor hook-running and bridge tests.
ruff check evi apps tests
ruff format evi apps tests # if you want formatter behavior toopyproject.toml sets line-length = 100, target-version = "py311".
evi/ core library — no IO at import time
agent.py Agent class + Event types
backends/ LM Studio / Ollama / llama.cpp / OpenAI-compat
commands.py ~/.evi/commands/ loader
config.py Config dataclasses + load/save + dir constants
dream.py memory consolidation engine
downloads.py HF GGUF puller
hardware.py NVIDIA + RAM detection
hooks.py hook config loader + runner
llm/
agent.py re-exports from evi.agent (or holds the loop)
client.py make_client(settings) → OpenAI SDK
subagent.py run_subagent() + SUBAGENT_PROFILES dict
mcp/
bridge.py async-to-sync bridge thread
manager.py MCPManager — boots stdio servers, registers tools
servers.py MCPServer dataclass + JSON loader
memory.py MemoryStore with .attic/ soft-delete
profiles.py partial TOML overlay merge
project.py find_project_file → ProjectContext
recommend.py tiered model picks per VRAM
scheduled.py ScheduledTask + TaskStore
scheduler.py Scheduler — APScheduler wrapper
skills.py SkillStore + minimal frontmatter parser
tools/
base.py @tool decorator + REGISTRY + Tool dataclass
code.py run_python tool (subprocess)
computer.py pyautogui wrappers — screenshot/click/type/key/scroll
fs.py read_file/write_file/list_dir
image_comfy.py ComfyUI text2img
memory.py remember/recall/forget/list_memories
skills.py list_skills/invoke_skill
subagent.py delegate_explore/delegate_plan
voice.py speak_text/transcribe_microphone
websearch.py web_search/web_fetch
transcripts.py JSONL session log store
util/streaming.py legacy
voice.py TTS (platform CLIs) + STT (faster-whisper)
worktree.py git worktree wrapper
apps/ frontends (shipped in the wheel)
cli/main.py Typer CLI — every public command
web/server.py FastAPI + SSE + permission flow
web/static/ vanilla JS chat UI
desktop/ Tauri 2 project (NOT a Python package)
tests/ pytest — one file per module
docs/ this folder
scripts/ install + dev helpers
-
Create
evi/tools/<thing>.py:from evi.tools.base import tool @tool( description="What this tool does for the model.", category="thing", # also: the config toggle name ) def the_tool(arg1: str, arg2: int = 5) -> str: ... return "result"
-
Add the category to
ToolTogglesinevi/config.py. -
Import for side effect in:
evi/apps/cli/main.pyevi/apps/web/server.py
-
Decide whether the category belongs in
auto.auto_approve. If it does anything irreversible or network-y, leave it out. -
Test pattern:
from evi.tools.base import REGISTRY import evi.tools.thing # noqa: F401 register def test_the_tool() -> None: out = REGISTRY["the_tool"].call(json.dumps({"arg1": "x"})) assert ...
evi/backends/__init__.py registers backends in KNOWN_BACKENDS. Each
subclass of Backend:
-
name: strclass attribute -
__init__(base_url, api_key, request_timeout)constructor -
make_client() -> OpenAI— required - Override
list_models / model_info / pull_model / delete_model / supports_pullas makes sense for the backend.
Add a default port to _DEFAULT_URLS in factory.py. Add tests in
tests/test_backends.py (use httpx.MockTransport).
Built-in commands live in evi/apps/cli/main.py and evi/apps/web/server.py.
The two paths are intentionally separate:
- CLI:
_handle_<name>(agent, args, cmd_store) -> SlashResult. Register in the_BUILTINSdict. - Web:
_handle_slashis one switch statement returning_SlashOutcome. Mirror your CLI handler there.
User-defined commands need no code — they're just markdown files in
~/.evi/commands/.
Add an entry to SUBAGENT_PROFILES in evi/llm/subagent.py:
SUBAGENT_PROFILES["reviewer"] = {
"system_prompt": "You review pull requests for correctness…",
"tool_categories": ("fs",),
}Then add a delegate tool in evi/tools/subagent.py that wraps
run_subagent(...) with that profile.
-
Agent.history vs transcripts:
historyis in-process, dies with the Agent. Transcripts are append-only JSONL on disk. Don't conflate them. - Tool registration is import-side-effect-driven. Forgetting to import the module in the CLI / web entry points means the tool won't appear in REGISTRY. The same module imported via tests works because the test imports it.
-
config.pyconstants: Lots of~/.evi/<thing>paths live there. Always import the constant, never hard-codePath.home() / ".evi" / .... -
Test isolation: Several modules cache state at module level
(
memory_tools._store,voice_mod._WHISPER_MODEL). Tests that monkeypatch their roots must do so via the module attribute, not the globals. -
MCP bridge ownership:
MCPManagerconstructs its own bridge by default. If you pass one in (tests do),manager.stop()doesn't stop the bridge — the caller is responsible.
When you add a meaningful chunk of work:
- Add tests covering the new code paths.
- Update
README.md"What's built" table. - Update
docs/architecture.mdif you added a new subsystem. - Add a memory entry in
~/.claude/projects/C--evi/memory/project_evi.mdso the next Claude Code session knows what changed.
Nothing automated yet. Planned: scripts/release.sh that runs
pytest && ruff check && python -m build, tags the commit, attaches
wheels.
Generated from docs/development.md — edit there, not here.
Start here
Guides
- Architecture
- [[Agent SDK (
evi.sdk)|sdk]] - SDK coverage + borrowable features
- Multi-machine setup
- Self-update design (Phase 29 proposal)
- [[Self-build — developing and building eVi with eVi|self-build]]
- Development notes
- Releasing
- Desktop bundling
- Code signing policy
- Surface parity — CLI ↔ Web ↔ Desktop
- eVi vs Claude Code — feature comparison
- Future integrations — backlog
- Roadmap
Feature deep-dives
- eVi feature guides
- Agents & Orchestration
- Recipes, Routines, Scheduled tasks, Channels
- Evals & LLM-as-judge
- Content Guardrails
- Hooks (tool + lifecycle, command/url)
- MCP (client + serve)
- Memory & Context management
- Observability (OpenTelemetry, stats, crash reports)
- Permissions & Sandbox
- Plugins & Marketplace
- Sessions, Resume, Handoff, Checkpoints
- Skills
- Slash commands
- Structured Outputs & Batch
- Ultracode
- Voice (TTS engines, STT, AutoSpeaker)
- Web & Desktop (settings, multi-user, deep links, updater)