ctrl+f, or Go to chat in the command palette, opens a fuzzy search over the chat tab names and switches to the chat picked. Switching with ctrl+tab or ctrl+shift+tab set the active tab but left nothing focused, so typing went nowhere until the prompt was clicked. Both now focus the new chat's prompt, as Go to chat does. Closes #52.
9 KiB
Configuration
oterm is configured through a JSON file (config.json) and a small set of environment variables.
Where config.json lives
The file is stored in a directory specific to your operating system. By default:
- Linux:
~/.local/share/oterm - macOS:
~/Library/Application Support/oterm - Windows:
C:/Users/<USER>/AppData/Roaming/oterm
Other platforms use ~/.local/share/oterm. Everywhere except Windows, XDG_DATA_HOME replaces the base directory, so the directory becomes ${XDG_DATA_HOME}/oterm. Override the location entirely by setting OTERM_DATA_DIR.
If in doubt, run oterm --data-dir (or uvx oterm --data-dir) to print the resolved location.
config.json at a glance
A complete example showing every supported key:
{
"splash-screen": true,
"theme": "textual-dark",
"keymap": {
"next.chat": "ctrl+tab",
"prev.chat": "ctrl+shift+tab",
"new.chat": "ctrl+n",
"toggle.thinking": "ctrl+t",
"copy.message": "ctrl+o",
"show.logs": "ctrl+l",
"go.to.chat": "ctrl+f",
"quit": "ctrl+q",
"newline": "shift+enter",
"add.image": "ctrl+i"
},
"openaiCompatible": {
"vllm": {
"base_url": "http://localhost:8000/v1"
},
"openrouter": {
"base_url": "https://openrouter.ai/api/v1",
"api_key": "${OPENROUTER_API_KEY}"
}
},
"mcpServers": {
"everything": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-everything"]
}
}
}
Each key is optional; the sections below cover them in turn. For mcpServers, see the full reference in Model Context Protocol.
splash-screen and theme
splash-screen(boolean, defaulttrue): whether the splash screen plays on startup.theme(string, default"textual-dark"): the active theme.otermrewrites this whenever you switch themes from the command palette, so you generally don't need to set it by hand.
keymap: customizing key bindings
Sane defaults are provided, but terminal emulators and shells will sometimes intercept them.
When picking a replacement, avoid the keys the prompt's text editor already claims. The prompt is focused most of the time, and a binding on the focused widget wins over an application one, so such a key will silently do nothing. The editor takes most navigation and editing keys, notably ctrl+a, ctrl+c, ctrl+d, ctrl+e, ctrl+k, ctrl+u, ctrl+v, ctrl+w, ctrl+x, ctrl+y, ctrl+z, ctrl+shift+k, ctrl+backspace, ctrl+left and ctrl+right (with their shift variants), f6, f7, the arrow keys, home, end, pageup, pagedown, and the super+ equivalents on macOS. The authoritative list is TextArea.BINDINGS in the installed version of Textual.
Override any of the bindings below by setting the matching key in the keymap block:
| ID | Default | Action |
|---|---|---|
next.chat |
ctrl+tab |
Switch to the next chat tab. |
prev.chat |
ctrl+shift+tab |
Switch to the previous chat tab. |
new.chat |
ctrl+n |
Open the new-chat dialog. |
toggle.thinking |
ctrl+t |
Turn thinking mode on or off for the current session (not persisted). |
copy.message |
ctrl+o |
Copy the last message of the current chat to the clipboard. |
show.logs |
ctrl+l |
Open the log viewer. |
go.to.chat |
ctrl+f |
Search the chats by name and switch to one. |
quit |
ctrl+q |
Quit oterm. |
newline |
shift+enter |
Insert a newline in the prompt. ctrl+m is also accepted as a fallback for terminals that can't distinguish shift+enter from enter, and is not configurable. |
add.image |
ctrl+i |
Attach an image to the next message. |
openaiCompatible: custom OpenAI-compatible endpoints
Connect to any OpenAI API-compatible service, whether a local runner (vLLM, LM Studio, llama.cpp) or a hosted aggregator (OpenRouter, LiteLLM), by adding named endpoints under openaiCompatible:
{
"openaiCompatible": {
"vllm": {
"base_url": "http://localhost:8000/v1"
},
"openrouter": {
"base_url": "https://openrouter.ai/api/v1",
"api_key": "${OPENROUTER_API_KEY}"
}
}
}
Each entry takes:
base_url(required): the OpenAI-compatible API base URL.api_key(optional): an API key. Reference an environment variable with${VAR}(or${VAR:-default}to fall back when unset), or pass a literal string. Omit for local endpoints that don't require authentication.
Each endpoint appears in the provider dropdown under its name. Select it and type the model name. If the endpoint exposes /v1/models, suggestions appear as you type.
With thinking turned off, oterm sends chat_template_kwargs: {"enable_thinking": false} with each request. vLLM and oMLX pass it to the model's chat template, which turns thinking off. LM Studio accepts the field and ignores it. With thinking on, nothing extra is sent and the server's default applies.
The context window shown under each reply comes from max_model_len in /v1/models (vLLM, oMLX), or from LM Studio's context length for the loaded model. For other servers only the tokens used are shown.
Providers and API keys
oterm discovers providers from your environment at startup. A provider appears in the new-chat dropdown only when its required environment variables are set. .env files in the working directory are loaded automatically.
| Provider | Provider ID | Required env var(s) |
|---|---|---|
| Ollama | ollama |
none for local; OLLAMA_API_KEY for ollama.com cloud models. Endpoint via OLLAMA_HOST / OLLAMA_URL. |
| OpenAI | openai-chat |
OPENAI_API_KEY |
| OpenAI Responses | openai-responses |
OPENAI_API_KEY |
| Anthropic | anthropic |
ANTHROPIC_API_KEY |
| Google AI | google |
GOOGLE_API_KEY |
| Google Vertex AI | google-cloud |
GOOGLE_APPLICATION_CREDENTIALS |
| Groq | groq |
GROQ_API_KEY |
| Mistral | mistral |
MISTRAL_API_KEY |
| Cohere | cohere |
COHERE_API_KEY |
| AWS Bedrock | bedrock |
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY |
| DeepSeek | deepseek |
DEEPSEEK_API_KEY |
| Grok | grok |
GROK_API_KEY |
| Cerebras | cerebras |
CEREBRAS_API_KEY |
| Hugging Face | huggingface |
HF_TOKEN |
The openai-responses provider routes through OpenAI's Responses API and enables image generation as a builtin tool. Pick a Responses-compatible model (e.g. gpt-5.4) and ask for an image in the prompt. Returned images render inline in the chat; click one to save it to $OTERM_DATA_DIR/downloads/.
For any other backend with an OpenAI-compatible API, see the openaiCompatible config block above.
Environment variables
| Variable | Default | Purpose |
|---|---|---|
OTERM_DATA_DIR |
OS-specific (above) | Directory for config.json, store.db, memory.db, saved logs and downloaded images. |
OTERM_VERIFY_SSL |
True |
Set to False to disable SSL verification when talking to Ollama. |
OLLAMA_HOST |
127.0.0.1:11434 |
Ollama host/port. Used to derive OLLAMA_URL when it isn't set. |
OLLAMA_URL |
from OLLAMA_HOST |
Full Ollama base URL (e.g. https://ollama.example.com). |
OLLAMA_API_KEY |
unset | Bearer token for ollama.com cloud models. Local Ollama doesn't need it. |
OTERM_OLLAMA_IMAGE_MODEL |
x/z-image-turbo |
Default Ollama model for the generate_image tool. |
Provider-specific API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, …) are listed in the provider table above.
Chat storage
All chat sessions are stored locally in a sqlite database under OTERM_DATA_DIR. Run oterm --db to print its path.