Terminal-first AI runtime for commands, chat, editing, tools, jobs, agents, and local workflows.
Docs hub: https://term-llm.com
- turn natural language into executable shell commands
- run persistent chat with tools and MCP servers
- edit files with model assistance
- support agents, skills, sessions, jobs, and local automation
- work with hosted or local models
$ term-llm exec "find all go files modified today"
> find . -name "*.go" -mtime 0 Uses find with name pattern
fd -e go --changed-within 1d Uses fd (faster alternative)
find . -name "*.go" -newermt "today" Alternative find syntax
something else...curl -fsSL https://raw.githubusercontent.com/samsaffron/term-llm/main/install.sh | shOr:
go install github.com/samsaffron/term-llm@latestNo API key needed if you use Zen:
term-llm exec --provider zen "list files"
term-llm ask --provider zen "explain git rebase"
term-llm chatIf you already have a provider key:
export ANTHROPIC_API_KEY=your-key
# or OPENAI_API_KEY / GEMINI_API_KEY / OPENROUTER_API_KEY / XAI_API_KEYThe detailed docs live at https://term-llm.com and are authored in Markdown in this repo, then built with Hugo.
Common entry points:
- Configuration
- Providers and models
- Web UI and API
- Search
- Usage
- Agents
- Skills
- MCP servers
- Memory
- Jobs
- Text embeddings
- Audio generation
- Music generation
- Usage tracking
- Transcription
- Notifications
Widgets are local web apps that are mounted inside the chat UI and proxied by term-llm. They are opt-in and disabled by default.
term-llm serve web --enable-widgetsWidgets are discovered from ~/.config/term-llm/widgets/ (override with --widgets-dir or serve.widgets_dir in config.yaml). Each sub-directory with a widget.yaml manifest becomes a widget.
title: "Sam's Hebrew Keyboard"
mount: hebrew # optional; defaults to directory name
description: "On-screen Hebrew keyboard"
command: ["uv", "run", "python", "server.py", "--socket", "$SOCKET"]title: "Dev Server Widget"
command: ["npm", "run", "dev", "--", "--port", "$PORT"]Required fields: title, command
command must contain exactly one of:
$SOCKET: term-llm creates a Unix domain socket and passes the path in argv and as env varsTERM_LLM_WIDGET_SOCKET/SOCKET$PORT: term-llm allocates a free localhost port and passes it asTERM_LLM_WIDGET_PORT/PORT
The widget process is started lazily on first request and stopped after 10 minutes of idle time.
| Variable | Value |
|---|---|
TERM_LLM_WIDGET_ID |
directory basename |
TERM_LLM_WIDGET_MOUNT |
mount path segment |
TERM_LLM_WIDGET_BASE_PATH / BASE_PATH |
full URL prefix for the widget |
TERM_LLM_WIDGET_SOCKET / SOCKET |
socket path (socket mode) |
TERM_LLM_WIDGET_HOST / HOST |
127.0.0.1 (port mode) |
TERM_LLM_WIDGET_PORT / PORT |
allocated port (port mode) |
| Path | Description |
|---|---|
GET {base}/widgets/ |
HTML index listing all widgets |
{base}/widgets/<mount>/ |
Proxied widget (lazy-started on first hit) |
GET {base}/admin/widgets/status |
JSON status of all widgets |
POST {base}/admin/widgets/reload |
Re-scan widgets directory |
POST {base}/admin/widgets/<mount>/stop |
Stop a running widget process |
MIT
