Skip to main content
The CLI stores its configuration in the ~/.deepagents/ directory. The main config files are:

Environment variables

The CLI loads environment variables from dotenv files so you don’t need to export API keys in your shell profile or duplicate .env files across projects.

Loading order and precedence

Two .env files are loaded at startup:
  1. Project .env — the .env file in your current working directory (if present)
  2. Global ~/.deepagents/.env — a single shared file that acts as a fallback for all projects
The effective precedence is: shell environment > project .env > global .env. Values already set in the shell are never overwritten—including on /reload.

DEEPAGENTS_CLI_ prefix

All CLI-specific environment variables use a DEEPAGENTS_CLI_ prefix (e.g., DEEPAGENTS_CLI_AUTO_UPDATE, DEEPAGENTS_CLI_DEBUG). See the CLI environment variable reference for the full list. The prefix also works as an override mechanism for any environment variable the CLI reads, including third-party credentials. The CLI checks DEEPAGENTS_CLI_{NAME} first, then falls back to {NAME}:
~/.deepagents/.env
On /reload, the CLI re-reads .env files and picks up prefixed values, so you can rotate keys without restarting.

Example

Store API keys once in ~/.deepagents/.env:
Then override per-project where needed by placing a .env in the project directory.

Config file

~/.deepagents/config.toml lets you customize model providers, set defaults, and pass extra parameters to model constructors.

Default and recent model

[models].default always takes priority over [models].recent. The /model command only writes to [models].recent, so your configured default is never overwritten by mid-session switches. To remove the default, use /model --default --clear or delete the default key from the config file.

Provider configuration

Each provider is a TOML table under [models.providers]:
Providers have the following configuration options:

Model constructor params

Any provider can use the params table to pass extra arguments to the model constructor:

Per-model overrides

If a specific model needs different params, add a model-keyed sub-table under params to override individual values without duplicating the entire provider config:
With this configuration:
  • ollama:qwen3:4b gets {temperature: 0.5, num_ctx: 4000} — model overrides win.
  • ollama:llama3 gets {temperature: 0, num_ctx: 8192} — no override, provider-level params only.
The merge is shallow: any key present in the model sub-table replaces the same key from the provider-level params, while keys only at the provider level are preserved.

CLI overrides with --model-params

For one-off adjustments without editing the config file, pass a JSON object via --model-params at launch or mid-session with the /model command:
Inside the TUI
These take the highest priority, overriding values from config file params. Mid-session params are applied for the current session only and are not persisted. --model-params cannot be combined with --default.

Profile overrides (Advanced)

Override fields in the model’s runtime profile to change how the CLI interprets model capabilities. The most common use case is lowering max_input_tokens to trigger auto-summarization earlier — useful for testing or for constraining context usage:
Per-model sub-tables work the same way as params — the model-level value wins on conflict:
Profile overrides are merged into the model’s profile after creation. Any feature that reads the profile — context-limit display in the status bar, auto-summarization thresholds, capability checks — will see the overridden values.

CLI profile overrides with --profile-override (Advanced)

To override model profile fields at runtime without editing the config file, pass a JSON object via --profile-override:
These are merged on top of config file profile overrides (CLI wins). The priority chain is: model default < config.toml profile < CLI --profile-override. --profile-override values persist across mid-session /model hot-swaps — switching models re-applies the override to the new model.

Custom base URL

Some provider packages accept a base_url to override the default endpoint. For example, langchain-ollama defaults to http://localhost:11434 via the underlying ollama client. To point it elsewhere, set base_url in your configuration:
Refer to your provider’s reference documentation for compatibility information and additional considerations.

Compatible APIs

For providers that expose APIs that are wire-compatible with OpenAI or Anthropic, you can use the existing langchain-openai or langchain-anthropic packages by pointing base_url at the provider’s endpoint:
Any features added on top of the official spec by the provider will not be captured. If the provider offers a dedicated LangChain integration package, prefer that instead.

Adding models to the interactive switcher

Some providers (e.g. langchain-ollama) don’t bundle model profile data (see Provider reference for full listing). When this is the case, the interactive /model switcher won’t list models for that provider. You can fill in the gap by defining a models list in your config file for the provider:
The /model switcher will now include an Ollama section with these models listed. This is entirely optional. You can always switch to any model by specifying its full name directly:

Arbitrary providers

You can use any LangChain BaseChatModel subclass using class_path. The CLI imports and instantiates the class directly — no built-in provider package required.
api_key_env and base_url are optional. class_path providers are expected to handle their own authentication internally — useful when your model uses custom auth (JWT tokens, proprietary headers, mTLS, etc.) rather than a standard API key:
With this config, switch to the model with /model xyz:abc-xyz-1 or --model xyz:abc-xyz-1.
Deep Agents requires tool calling support. If your custom model supports tool calling but the CLI doesn’t know about it, declare it in the provider profile:
Set max_input_tokens to what your model supports to enable accurate context length tracking and auto-summarization.
The provider package must be installed in the same Python environment as deepagents-cli:
When you switch to my_custom:my-model-v1 (via /model or --model), the model name (my-model-v1) is passed as the model kwarg:
class_path executes arbitrary Python code from your config file. This has the same trust model as pyproject.toml build scripts — you control your own machine.
Your provider package may optionally provide model profiles at a _PROFILES dict in <package>.data._profiles in lieu of defining them under the models key. See LangChain model profiles for more info.

Skills extra allowed directories

By default, when the CLI loads skills it validates that a resolved skill file path stays inside one of the standard skill directories. This prevents symlinks inside skill directories from reading arbitrary files outside those roots. If you store shared skill assets in a non-standard location and use symlinks from a standard skill directory to reference them, you can add that location to the containment allowlist. This does not add a new skill discovery location — skills are still only discovered from the standard directories.
When the environment variable is set, it takes precedence over the config file value. Paths support ~ expansion. Changes take effect on /reload.

Themes

Use /theme to open an interactive theme selector. Navigate the list to preview themes in real-time, press Enter to persist your choice to config.toml. The CLI ships with many built-in themes. The selected theme is persisted under [ui]:

User-defined themes

Define custom themes under [themes.<name>] sections in config.toml. Each section requires label (str). dark (bool) defaults to false if omitted — set to true for dark themes. All color fields are optional — omitted fields fall back to the built-in dark or light palette based on the dark flag.
User-defined themes appear alongside built-in themes in the /theme selector.

Override built-in theme colors

To tweak a built-in theme’s colors without creating a new theme, use a [themes.<builtin-name>] section. Only color fields are read — label and dark are inherited from the built-in:
Omitted color fields retain the existing built-in values. Changes to [themes.*] sections take effect on /reload.

Auto-update

The CLI can automatically check for and install updates.
The environment variable takes precedence over the config file. When enabled, the CLI checks PyPI for a newer version at session start and automatically upgrades using the detected install method (uv, Homebrew, or pip). When disabled (default), the CLI shows an update hint with the appropriate install command instead. You can also check for and install updates manually at any time with the /update slash command, which bypasses the cache and reports success or failure inline. After an upgrade, the CLI shows a “what’s new” banner on the next launch with a link to the changelog. At session exit, if a newer version was detected during the session, an update banner is displayed as a reminder.

Managed deployments

The install script supports running as root, targeting macOS MDM tools (Kandji, Jamf, etc.) that execute scripts in a minimal root environment. When id -u is 0, the script:
  1. Resolves the real console user’s HOME (via /dev/console or a /Users directory scan)
  2. chowns all created files back to the target user after each install step
Non-root installs are unaffected: all root-specific code paths short-circuit when not running as root. To pre-configure auto-update for managed installs, set DEEPAGENTS_CLI_AUTO_UPDATE=1 in the user’s shell profile or deploy a config.toml with [update] auto_update = true to ~/.deepagents/config.toml. To suppress automatic updates and update checks entirely, set DEEPAGENTS_CLI_NO_UPDATE_CHECK=1.

CLI environment variable reference

All CLI-specific environment variables use the DEEPAGENTS_CLI_ prefix. See DEEPAGENTS_CLI_ prefix for how the prefix also works as an override for third-party credentials.

External editor

Press Ctrl+X or type /editor to compose prompts in an external editor. The CLI checks $VISUAL, then $EDITOR, then falls back to vi (macOS/Linux) or notepad (Windows). GUI editors (VS Code, Cursor, Zed, Sublime Text, Windsurf) automatically receive a --wait flag so the CLI blocks until you close the file.

Hooks

Hooks let external programs react to CLI lifecycle events. Configure commands in ~/.deepagents/hooks.json and the CLI pipes a JSON payload to each matching command’s stdin whenever an event fires. Hooks run fire-and-forget in a background thread — they never block the CLI and failures are logged without interrupting your session.

Setup

Create ~/.deepagents/hooks.json:
Now every time a session starts or ends, the CLI appends the event payload to ~/deepagents-events.log.

Hook configuration

The config file contains a single hooks array. Each entry has:
The second hook above has no events filter, so it receives every event the CLI emits.

Payload format

Each hook command receives a JSON object on stdin with an "event" key plus event-specific fields:

Events reference

session.start

Fired when an agent session begins (both interactive and non-interactive modes).

session.end

Fired when a session exits.

user.prompt

Fired in interactive mode when the user submits a chat message. No additional fields.

input.required

Fired when the agent requires human input (human-in-the-loop interrupt). No additional fields.

permission.request

Fired before the approval dialog when one or more tool calls need user permission.

tool.error

Fired when a tool call returns an error.

task.complete

Fired when the agent finishes its current task (the streaming loop ends without further interrupts).

context.compact

Fired before the CLI compacts (summarizes) the conversation context. No additional fields.

Execution model

  • Background thread: Hook subprocesses run in a thread via asyncio.to_thread so the main event loop is never blocked.
  • Concurrent dispatch: When multiple hooks match an event, they run concurrently in a thread pool.
  • 5-second timeout: Each command has a 5-second timeout. Commands that exceed this are killed.
  • Fire-and-forget: Errors are caught per-hook and logged at debug/warning level. A failing hook never crashes or stalls the CLI.
  • Lazy loading: The config file is read once on the first event dispatch and cached for the rest of the session.
  • No shell expansion: Commands are executed directly (not through a shell). Wrap in ["bash", "-c", "..."] if you need shell features like pipes or variable expansion.

Hook examples

Log all events to a file

Desktop notification on task completion (macOS)

Python handler

Write a handler script that reads the JSON payload from stdin:
my_handler.py
~/.deepagents/hooks.json

Security considerations

Hooks follow the same trust model as Git hooks or shell aliases — any user who can write to ~/.deepagents/hooks.json can execute arbitrary commands. This is by design:
  • No command injection: Payload data flows only to stdin as JSON, never to command-line arguments. json.dumps handles escaping.
  • No shell by default: Commands run with shell=False, preventing shell injection.
  • Malformed config: Invalid JSON or unexpected types produce logged warnings, not security issues.
Only add hooks from sources you trust. A hook has the same permissions as your user account.