An AI that lives in your terminal.
ET is a Linux-first AI terminal assistant. It helps you understand, write, debug and execute shell commands using natural language — right where you already work.
ET is not a chatbot inside a terminal. It understands your shell, your filesystem, your commands and your environment, and it never blindly executes anything: every command is classified by an independent safety engine and must be confirmed before it runs.
et ❯ what is using port 8080?
╭─────── SAFE ────────╮
│ │
│ ss -ltnp | grep :8080 │
│ │
│ Shows which process listens on port 8080. │
╰─────────────────────╯
Run? [y/N] y
- Natural language → shell command — describe what you want, get a command with an explanation and a risk rating.
- Explain mode —
et explain "sudo pacman -Syu". - Command execution with a safety layer — commands are classified
safe/moderate/dangerousby deterministic rules, not by the AI. Dangerous commands require typingCONFIRM. - Error debugging — ask "why did that fail?" and ET analyzes the last command's exit code, stdout and stderr.
- Local-first, API-ready — works offline with Ollama, or through any OpenAI-compatible API (OpenAI, Groq, Mistral, DeepSeek…), OpenRouter, or Google Gemini.
- Persistent local history — every interaction is stored in SQLite.
- Extensible tools — a tool registry ready for plugins, MCP and more.
et doctor— check your installation and environment.
- Python 3.12+
- Linux (Arch Linux fully supported; most distros work)
- Any one backend: a local Ollama server, an OpenRouter key, an OpenAI key (or any OpenAI-compatible API), or a Google Gemini key
# recommended — isolated install
pipx install et
# or from source
git clone https://github.com/you/et.git
cd et
pip install .python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"# interactive session
et
# one-shot question or command request
et "find files larger than 500MB"
# explain a command
et explain "git rebase -i HEAD~3"
# generate, confirm, then run
et run "list running docker containers"
# inspect your setup
et doctoret ❯ find running docker containers
╭──── SAFE ────╮
│ docker ps │
│ Lists currently running Docker containers. │
╰──────────────╯
Run? [y/N] y
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
…
Ctrl+Ccancels a request.Ctrl+Dorexitleaves the session.- Type
helpfor usage hints.
ET reads ~/.config/et/config.toml. Run et config to see its location, or
et config --open to edit it. A default file is created on first use:
[ai]
provider = "ollama" # ollama | openrouter | openai | gemini
ollama_host = "http://localhost:11434"
ollama_model = "qwen2.5-coder"
openrouter_model = "anthropic/claude-3.5-sonnet"
openai_base_url = "https://api.openai.com/v1"
openai_model = "gpt-4o-mini"
gemini_model = "gemini-2.0-flash"
[ui]
show_thinking = true
theme = "auto"
max_output_lines = 200
[safety]
auto_confirm_safe = false
require_typing_for_dangerous = true
confirm_word = "CONFIRM"
[history]
enabled = true
max_entries = 2000
default_shell = "/bin/zsh" # optional; autodetected when empty
telemetry = falseAPI keys are never stored in the config file. Use environment variables
(see .env.example):
# OpenRouter — one key for Claude, GPT, Gemini, Llama…
export OPENROUTER_API_KEY="sk-or-..."
# OpenAI / OpenAI-compatible — OpenAI, Groq, Mistral, DeepSeek, local vLLM…
export OPENAI_API_KEY="sk-..."
export OPENAI_BASE_URL="https://api.groq.com/openai/v1" # optional
export OPENAI_MODEL="llama-3.3-70b-versatile" # optional
# Google Gemini (native)
export GEMINI_API_KEY="AIza..."| Purpose | Path |
|---|---|
| Config | ~/.config/et/config.toml |
| Data | ~/.local/share/et/ (history in et.db) |
| Cache | ~/.cache/et/ |
# install & start
curl -fsSL https://ollama.com/install.sh | sh
ollama serve
# pull a coding model
ollama pull qwen2.5-coder[ai]
provider = "ollama"
ollama_model = "qwen2.5-coder"export OPENROUTER_API_KEY="sk-or-..."[ai]
provider = "openrouter"
openrouter_model = "anthropic/claude-3.5-sonnet"OpenRouter proxies models from many vendors behind one key.
ET speaks the universal /chat/completions API, so it works with OpenAI,
Groq, Mistral, DeepSeek, Together, and local servers (vLLM, llama.cpp, LM
Studio) with no extra code:
export OPENAI_API_KEY="sk-..."
export OPENAI_BASE_URL="https://api.openai.com/v1" # default: OpenAI
export OPENAI_MODEL="gpt-4o-mini"Examples of OPENAI_BASE_URL:
| Provider | OPENAI_BASE_URL |
|---|---|
| OpenAI | https://api.openai.com/v1 (default) |
| Groq | https://api.groq.com/openai/v1 |
| Mistral | https://api.mistral.ai/v1 |
| DeepSeek | https://api.deepseek.com/v1 |
| Together | https://api.together.xyz/v1 |
| local vLLM | http://127.0.0.1:8000/v1 |
| local LM Studio | http://127.0.0.1:1234/v1 |
Local servers (localhost / 127.0.0.1) can run without an API key.
[ai]
provider = "openai"
openai_base_url = "https://api.groq.com/openai/v1"
openai_model = "llama-3.3-70b-versatile"export GEMINI_API_KEY="AIza..." # https://aistudio.google.com/apikey[ai]
provider = "gemini"
gemini_model = "gemini-2.0-flash"GOOGLE_API_KEY works as an alias for GEMINI_API_KEY. GEMINI_BASE_URL
overrides the endpoint (useful for proxies or regional endpoints).
ET never trusts the AI. The flow is:
- The AI proposes a command and a risk rating.
- The safety engine independently classifies the command using
deterministic rules (
rm -rf /,mkfs,dd,reboot, fork bombs…). - The effective risk is the highest of the two.
- The user decides — nothing runs without approval.
safecommands are shown; confirmation still applies unlesssafety.auto_confirm_safeis enabled.moderatecommands askRun? [y/N].dangerouscommands demand the literal wordCONFIRM.
Unknown commands default to moderate, never safe. In non-interactive
(piped) contexts, execution is always refused.
ET also sanitizes context: secrets like API keys, tokens, passwords and
.env values are stripped before anything is sent to an AI provider.
et/
├── cli.py CLI entry (Typer) + default-command routing
├── cli_group.py custom Typer group (free-form prompt + subcommands)
├── ai/ provider abstraction: Ollama, OpenAI-compatible,
│ OpenRouter, Gemini
├── core/ agent, context, executor, permissions, session, doctor
├── memory/ SQLite history + short-term conversation buffer
├── tools/ tool registry: shell, filesystem, system
├── config/ pydantic models, TOML loading, XDG paths
└── ui/ Rich console, renderer, prompts
User prompt
│
▼
Agent ──► AI provider (Ollama / OpenRouter)
│ │
│ └── structured JSON {intent, command, explanation, risk}
▼
Safety engine ────► independent classification (safe/moderate/dangerous)
│
▼
Renderer ──► confirm? ──► CommandExecutor ──► SQLite history
See docs/architecture.md for details and docs/development.md for contribution guidance.
source .venv/bin/activate
pip install -e ".[dev]"
# run tests
pytest
# type-check (optional)
python -m pip install mypy && mypy src/etpytestThe suite covers CLI routing, config loading and env overrides, risk classification, command execution, provider abstraction (via mock HTTP transports), system context sanitization, history, the tool registry and the end-to-end session flow. Tests never execute destructive commands.
- Plugin system and MCP support
- Git / Docker / Kubernetes integrations
- SSH assistant and remote management
- Terminal command autocomplete and shell integration
- Semantic memory with local embeddings
Contributions are welcome. Please:
- Open an issue for discussion before large changes.
- Keep commits small and focused.
- Run
pytestbefore opening a PR. - Follow the style of the existing code (type hints, docstrings, no global state, small functions).