A ReAct coding agent in your terminal. Multi-provider LLM, parallel tool dispatch, async sub-agents, swappable UI.
evva runs a tool-using LLM agent in your terminal. It speaks Anthropic Claude, DeepSeek, OpenAI, and Ollama through one llm.Client interface; dispatches multiple tool calls per turn in parallel; tracks tasks and sub-agents through an observable store; and renders into a bubbletea TUI or a plain-text CLI sink.
The architecture is small on purpose — adding a new LLM provider, panel, or UI implementation is roughly one package each.
Requires Go 1.25+. Currently macOS and Linux only — Windows support is not yet available.
go install github.com/johnny1110/evva/cmd/evva@latestThe binary lands in $GOBIN (or $GOPATH/bin). Make sure it's on your PATH.
git clone https://github.com/johnny1110/evva
cd evva
make installOverride the location if you want it elsewhere:
sudo make install PREFIX=/usr/local/bin # system-wide
make install PREFIX=$HOME/.local/bin # user-localevva -versionUninstall removes only the binary; your ~/.evva/ config is preserved:
make uninstallTo update evva to the latest version without Go:
evva updateThis checks GitHub Releases for a newer version, downloads the pre-built binary for your OS/arch, and replaces the current one atomically. No Go toolchain required.
You can also check for updates from inside the TUI with /update.
To see your current version:
evva -versionJust type evva from any directory. On the first launch evva auto-creates:
~/.evva/
├── config/
│ └── evva-config.yml # user-tunable settings (auto-created with defaults)
└── skills/ # optional skill scripts (your own)
A one-line stderr notice fires the first time only:
evva: wrote new config to ~/.evva/config/evva-config.yml — fill in your API keys to use cloud providers.
~/.evva/.env is optional. If you want to override deployment knobs (LOG_LEVEL, LOG_DIR, APP_ENV, LOG_FORMAT, SKILLS_DIR, USER_PROFILE), create it; otherwise the built-in defaults apply.
Two ways:
- From inside the TUI: type
/config, navigate to<provider>.api_key, press Enter, paste your key, press Enter again. Saved immediately. - By hand: open
~/.evva/config/evva-config.ymland fill inproviders.<provider>.api_key.
Cloud providers (Anthropic, DeepSeek, OpenAI) need a key; Ollama is local and key-less.
Full usage documentation covering the TUI interface, slash commands, keybindings, yank mode, the permission system, sub-agents, and all configuration options:
User-tunable settings. Created automatically on first launch. Edit live via /config in the TUI, or by hand:
# Agent loop
max_iterations: 30
max_tokens: 4096
auto_compact_threshold: 0.8
display_thinking: true
# Default model used at startup (overwritten by /model swap)
default_provider: deepseek
default_model: deepseek-v4-pro
# Permission stance at startup. Cycle at runtime with Shift+Tab; -permission-mode CLI flag overrides.
permission_mode: default # default | accept_edits | plan | bypass
# Web tooling
fetch_max_bytes: 100000
tavily_api_key: ""
# Per-provider credentials. Empty api_url falls back to the constant's default.
providers:
anthropic: { api_key: "", api_url: "" }
deepseek: { api_key: "", api_url: "" }
openai: { api_key: "", api_url: "" }
ollama: { api_url: "" }Place in your working directory or at ~/.evva/.env. Only used for deployment / logging knobs — never user preferences:
APP_ENV=dev # dev | prod
LOG_LEVEL=info # debug | info | warn | error
LOG_FORMAT=text # text | json
LOG_DIR= # unset → $EVVA_HOME/logs (default); path → custom dir; explicit empty → stdout-only
SKILLS_DIR=skills # subpath under ~/.evva/
USER_PROFILE=user_profile.mdevva # interactive TUI (when stdout is a TTY)
evva -version # print version, commit, and build date
evva update # self-update from GitHub Releases (no Go required)
evva -temp 0.7 # sampling temperature (default unset)
evva -max-tokens 2048 # per-completion output cap (overrides YAML)
evva -max-iters 40 # loop iteration cap (overrides YAML)
evva -permission-mode=plan # boot in plan mode (read-only; see "Permission modes")
evva -permission-mode=bypass # boot with the gate disabled
evva -no-tui "explain loop.go" # one-shot plain-text mode
echo "list files in /tmp" | evva -no-tui # piped promptAgent loop
- ReAct-style: LLM call → parallel tool dispatch → tool results → repeat.
- Multiple
tool_useblocks per turn, executed concurrently. - Iteration cap surfaces as a pausable state.
- Cancellable via
ctx; Esc / Ctrl+C honored end-to-end.
LLM providers
- Anthropic Claude (extended thinking + cryptographic signature round-trip).
- DeepSeek (OpenAI-compatible chat, reasoning_content echoed back).
- OpenAI.
- Ollama (local).
- Per-provider option pattern (
WithTemperature,WithEffort, ...).
Tools
- File system:
read_file,write_file,edit_file— strict-absolute paths, structured*FileDiffmetadata for diff rendering. - Shell:
bash,grep,tree. - Tasks (six tools sharing one observable
*task.Store). - Meta:
agent(sub-agents),tool_search(lazy schema loading),skill,schedule_wakeup. - Plus stubs for
web_*,cron_*,notebook_edit,monitor,mode,ux.
Sub-agents
explore(read-only) andgeneral-purposepresets.- Sync mode (parent blocks) and async mode (parent continues, result lands on next iteration).
- Two-layer hierarchy: sub-agents can't spawn sub-agents.
Observable store framework (internal/observable)
- One pub/sub primitive any store can embed. Adding a new panel costs zero edits to the agent or event packages.
Swappable UI (internal/ui)
- Narrow
UIandControllerinterfaces. Reference bubbletea TUI underinternal/ui/bubbletea/.-no-tuifalls back to a plain CLI sink.
Streaming completions (chunked text + thinking).
2-level compaction
- micro: compress tool-result blocks when context budget approaches threshold.
- full: summarize the whole session into a single assistant brief.
evva/
├── cmd/evva/ # CLI entry point — wires agent + UI
├── configs/ # config loading (.env + YAML)
├── docs/ # design notes, tool docs, system prompts
├── internal/
│ ├── agent/ # agent loop, profiles, spawn
│ │ ├── event/ # event types + sink contract
│ │ └── sysprompt/ # system prompt builder
│ ├── constant/ # provider / model / status enums
│ ├── llm/ # llm.Client interface + shared params
│ │ ├── claude/ deepseek/ ollama/
│ ├── llmfactory/ # provider factory keyed by constant
│ ├── logger/ # structured slog wrapper + pretty fmt
│ ├── observable/ # pub/sub framework for stores
│ ├── session/ # conversation history + cumulative usage
│ ├── tools/ # tool interface (Name/Schema/Execute)
│ │ ├── cron/ dev/ fs/ meta/ mode/ monitor/ notebook/
│ │ ├── shell/ task/ ux/ web/
│ ├── toolset/ # tool catalog + ToolState registry
│ └── ui/ # UI plugin contract
│ └── bubbletea/ # reference TUI implementation
├── log/ # per-agent runtime logs (gitignored)
├── pkg/common/ # small shared utilities (UUID, ...)
└── scripts/ # demo / dev scripts
Key boundaries:
agentknows aboutevent.Sink, never about a concrete UI.tools/*packages producetools.Result(text + opaqueMetadata); the UI type-asserts onMetadatato render structured payloads.observablehas no dependencies on agent or UI.uidefines two narrow interfaces; implementations live under it.
- Multimodal Read: images, PDFs (with
pagesrange), Jupyter notebooks. - Overwrite diffs: proper Myers/Hunt-McIlroy diff for
write_fileoverwrites. - Per-agent LLM: sub-agent can use a different provider than its parent.
- Veronica space: long-running local sandbox service on
:8080. - Web UI: a second
UIimplementation served over WebSocket. - Session persistence:
/resumeto reload a session snapshot.
- Windows is not yet supported. macOS and Linux only.
- Sub-agent hierarchy is exactly two layers (no nested spawning).
- Token counts depend on provider reporting — Ollama only reports prompt / eval, not cache or reasoning splits.
- The TUI transcript grows unbounded in a long session; compaction is on the list above.
See LICENSE.

