Eva is a command line intelligence assistant. It uses deterministic local tools for file discovery, tree generation, search, configuration, caching, and quota tracking, while reserving LLM calls for natural-language reasoning, summarization, code review, command generation, and patch generation.
The design goal is practical: keep routine terminal work fast and local, "Can't remember the right command... Don't leave CLI just for a single command and simple work insted ask eva directly from your CLI"
📖 Website & Documentation: Full interactive documentation is available at eva-cli.vercel.app/docs (or offline in the docs/ directory).
- Provider abstraction with fallback routing across OpenRouter, Groq, Gemini, and OpenCode Zen.
- Streaming responses with disk-backed caching.
- Local RPM/RPD budget tracking to avoid unexpectedly exhausting free-tier provider quotas.
.gitignore-aware context collection with common heavy directories pruned automatically.- Safe file-context reads with missing-file, binary-file, large-file, and invalid-UTF-8 handling.
- Hardened command generation via
eva work: strict parsing, risk checks, noshell=True, dry-run mode, and audit logging. - Git-aware workflows for explaining diffs and generating commit messages.
- Reviewable patch generation with
eva edit. - Health checks through
eva config doctor. - Persistent chat sessions with
eva chat --session.
Eva requires Python 3.10 or newer.
Quick install:
curl -fsSL https://raw.githubusercontent.com/rootagi/eva/main/install.sh | shOr with a package manager, once published to PyPI:
uv tool install eva-cli # or: pipx install eva-cli / pip install --user eva-cliFor the Rust-accelerated file walker, once published:
uv tool install "eva-cli[fast]"Shell completion setup (bash, zsh, fish):
eva --install-completionOr with Docker (no Python required):
docker run --rm -it -v "$(pwd):/workspace" -e EVA_OPENAI_API_KEY="$OPENAI_API_KEY" \
ghcr.io/rootagi/eva chatFrom a local checkout (for contributing):
python -m pip install -e .For development:
python -m pip install -e ".[dev]"Set the default provider:
eva use groqStore an API key using the OS keyring:
eva config set-key groqHeadless environments such as containers, CI runners, and SSH-only servers may not have a usable OS keyring backend. In that case, use provider-specific environment variables:
export EVA_GROQ_API_KEY="..."
export EVA_OPENROUTER_API_KEY="..."
export EVA_GEMINI_API_KEY="..."
export EVA_OPENCODE_ZEN_API_KEY="..."Check the local setup:
eva config doctorAsk a one-shot question:
eva ask "Explain the difference between a process and a thread"Include a file as context:
eva ask "What does this module do?" --file src/eva/cli.pyInclude a directory tree as context:
eva ask "Where should I add a new provider?" --dir src/evaPack an entire repository as context (up to provider token budget):
eva ask "Explain the overall architecture" --repo . --dry-run
eva ask "Find potential bugs across the codebase" --repo . --yesAgentic repo exploration (Codex-style multi-turn tool calling):
eva investigate "Find how CLI commands are wired and explain the app flow" . --yes| Feature | eva ask --repo |
eva investigate |
|---|---|---|
| Approach | Single-pass context dump | Multi-turn iterative tool calling |
| Context Selection | Query-blind (dependency centrality, file size) | Query-driven (model decides what to inspect) |
| Tool Calling | None | Iteratively calls list_directory, read_file, search_code |
| Token Usage | Pre-packs files up to token budget | Reads only files needed for the query |
| Supported Providers | All providers | Tool-capable providers (groq, openrouter, opencode_zen, gemini) |
Explain a file or directory:
eva explain src/eva/routerAnalyze piped output:
pytest -q | eva analyze "Summarize the failures"Start an interactive chat session:
eva chat --session refactor-router
eva chat --session refactor-router --resumeGenerate a safe command from natural language:
eva work "list the largest files in this repository" --dry-runExplain current git changes:
eva changes
eva changes --stagedGenerate a commit message:
eva commit-messageGenerate a reviewable patch:
eva edit "add validation for empty provider names" --file src/eva/cli.pyApply the generated patch after confirmation:
eva edit "add validation for empty provider names" --file src/eva/cli.py --applyLocal utility commands do not use LLM quota:
eva find "*.py"
eva tree src/eva
| Command | Purpose |
|---|---|
eva ask |
Ask a one-shot question, optionally with file, directory, or repo-wide packed context (--repo, --dry-run, --yes). |
eva explain |
Explain a file, concept, or repository (with stack detection & module dependency graph). |
eva analyze |
Analyze piped terminal output. |
eva chat |
Run an interactive chat session, optionally saved and resumed. |
eva work |
Generate and optionally execute a single safe local command. |
eva edit |
Generate a unified diff for one or more files. |
eva workflow run |
Walk through a declarative multi-step YAML workflow with human approval gates. |
eva workflow list |
List available built-in and user-defined workflows. |
eva workflow show |
Display workflow steps and commands without executing them. |
eva workspace |
Manage isolated session workspaces, notes, and bookmarks. |
eva workspace create |
Create a new named session workspace. |
eva workspace switch |
Switch to a named session workspace. |
eva workspace list |
List all session workspaces. |
eva workspace note |
Add a note to the active workspace (secrets redacted automatically). |
eva workspace bookmark |
Bookmark a file path or URL in the active workspace. |
eva workspace show |
Display notes, bookmarks, and activity history for a workspace. |
eva replay |
Replay recorded terminal execution sessions (eva replay <session> or eva replay --list). |
eva changes |
Explain unstaged or staged git changes. |
eva commit-message |
Generate a concise commit message from a git diff. |
eva find |
Find files locally without AI usage. |
eva tree |
Print a .gitignore-aware directory tree. |
eva usage |
Show normalized local provider usage counters. |
| eva config set-key <provider> | Store an API key in the OS keyring. |
| eva config remove-key <provider> | Delete a stored API key from the OS keyring. |
| eva config set-model <provider> <model> | Set active model for a provider. |
| eva config | Manage provider, model, and API-key configuration. |
| eva cache clear | Clear cached AI responses. |
Global options:
eva --version
eva --verbose ask "Why did this fail?"Verbose mode writes diagnostics to stderr and to Eva’s log file.
eva work and eva workflow are intentionally conservative:
- model output must resolve to exactly one command line;
- malformed Markdown fences and trailing explanations are rejected;
- shell operators such as pipes, redirects, command substitution, and chained commands are rejected;
- high-risk patterns such as
sudo,rm -rf,curl | bash,dd,mkfs, recursive ownership changes, and system-path redirects are blocked; - secrets, API keys, tokens, and high-entropy strings are redacted before any network request and before writing to local disk;
- every generated, blocked, declined, or executed command is recorded in a cryptographic hash-chained audit log with SHA-256 tamper verification;
- optional sandboxed execution (
sandbox_risky_commands = truein config) runs commands in a stripped subprocess environment with environment variable isolation and strict timeouts.
See SECURITY.md for full security documentation.
Eva collects zero data by default (telemetry_enabled = false). When explicitly opted-in via configuration, Eva records only anonymized provider response latency, error types, and success status. Prompt text, code snippets, file contents, and terminal commands are never collected. An optional self-hosted export endpoint is supported via telemetry_export_endpoint.
Eva is tested across Linux (ubuntu-latest), macOS (macos-latest), and Windows (windows-latest):
- Path Handling: Eva normalizes file paths to POSIX format internally for
.gitignorepattern matching and diff operations across all operating systems. - Credential Storage: On Linux/macOS/Windows desktops with a native keyring backend (macOS Keychain, Windows Credential Manager, D-Bus SecretService),
eva config set-keystores secrets securely in the OS keyring. In headless Linux/CI runners without a D-Bus secret service, keyring access degrades gracefully to provider environment variables (e.g.EVA_GROQ_API_KEY). - Shell Execution & Command Safety:
eva workand sandboxed subprocess execution adjust argument splitting (shlex) for Windows command semantics.- POSIX-specific blast-radius protection patterns (e.g.
rm -rf /,chmod/chown,curl | bash,/dev/*writes) are tailored for Unix shells (sh,bash,zsh). On Windows (cmd.exe/powershell.exe), equivalent high-risk command prevention relies on interactive confirmation gates and sandbox execution.
- Performance Accelerators: The optional
eva_fastwalkC/Rust extension accelerates directory tree generation and file discovery when compiled binaries exist for the platform; on platforms without binary wheels, Eva seamlessly falls back to Python standard library directory traversal (os.walk/Path.iterdir).
Eva tries the configured default provider first. If fallback is enabled, it then tries providers in the configured fallback order. Provider failures are logged instead of being silently discarded, and final provider failure messages include a concise summary of what failed.
Configured providers:
- OpenRouter
- Groq
- Gemini
- OpenCode Zen
- Ollama (offline local backend)
- llama.cpp (offline GGUF backend)
Eva CLI features an extensible plugin system. Third-party packages can register CLI commands, contribute custom AI providers, or register workflow step hooks by creating standard Python packages with entry points.
- Subclass
EvaPluginfromeva.plugins:
import typer
from eva.plugins import EvaPlugin
class MyPlugin(EvaPlugin):
name = "my-plugin"
version = "0.1.0"
def register_commands(self, app: typer.Typer) -> None:
@app.command("my-command")
def my_command():
"""Custom command contributed by plugin."""
typer.echo("Hello from my plugin!")
def register_providers(self) -> None:
# Register custom LLM provider implementations here
pass- Expose the plugin in your package's
pyproject.tomlunder[project.entry-points."eva.plugins"]:
[project.entry-points."eva.plugins"]
my_plugin = "my_package.module:MyPlugin"- Install your package into the environment (
pip install .orpip install -e .). Eva automatically discovers installed plugins viaimportlib.metadata.entry_pointson startup.
If an installed plugin fails to load or raises an exception during initialization, Eva logs a warning and skips the broken plugin without interrupting CLI operation.
See examples/eva-plugin-hello for a complete example plugin package.
Install development dependencies:
python -m pip install -e ".[dev]"Run tests:
pytestRun linting:
ruff check .Run the same checks in CI by pushing to a branch or opening a pull request. See .github/workflows/ci.yml.
docs— documentation suite (eva-cli.vercel.app/docs).pyproject.toml— package metadata, dependencies, and tooling.CHANGELOG.md— release history.LICENSE— MIT license.src/eva— CLI implementation.tests— regression tests.
Eva CLI is released under the MIT License.





