This guide is for the people who deploy, operate, secure, and extend Aider: platform admins standing it up for a team, DevOps engineers wiring it into CI, and developers who need to understand its internals. It documents Aider's architecture, the complete configuration surface, security posture, operational procedures, and a module-by-module developer reference.
For installation and day-to-day usage, see the companion User Guide.
What Aider is, in one sentence: a Python command-line application that mediates between a git repository and a large language model — assembling source context (including a tree-sitter "repo map"), prompting the model to produce structured edits, applying and committing those edits, and optionally linting/testing the result.
Table of Contents
- Executive Summary
- Architecture Overview
- Component Inventory
- Requirements
- Configuration Mastery
- Model Subsystem
- The Edit-Format / Coder System
- The Repo Map
- Git Subsystem
- Security
- Deployment Patterns
- Operations
- Developer Internals
- Testing Strategy
- Potential Improvements
- Quick Reference
1. Executive Summary
| Attribute | Value |
|---|---|
| Project | Aider — AI pair programming in your terminal |
| Package (PyPI) | aider-chat |
| Entry-point command | aider (→ aider.main:main) |
| Language | Python (requires >=3.10,<3.15) |
| License | Apache-2.0 |
| Provider abstraction | LiteLLM (works with virtually any LLM) |
| Persistence | The user's git repository (no database/server of its own) |
| Interfaces | Interactive terminal chat, one-shot CLI, IDE watch mode, Streamlit browser GUI, web-chat copy/paste |
| Deployment model | Local developer tool / CLI; no daemon or network service |
Aider is not a client/server system. It is a single-process CLI that runs entirely on the developer's machine. Its only outbound network calls are to the configured LLM provider and (optionally) to PyPI for version checks, to product analytics, and to web pages you ask it to scrape. There is no Aider backend to operate.
Operationally, the things an admin cares about are: (1) how credentials are supplied and scoped, (2) how configuration precedence resolves across machines/teams, (3) the model/cost controls, and (4) the git safety guarantees. Those are the focus of this guide.
2. Architecture Overview
┌──────────────────────────────────────────────┐
│ aider (CLI process) │
│ │
user ──prompt/voice──▶ │ InputOutput (io.py) ◀── prompt_toolkit │
│ │ │
│ ▼ │
│ Commands (commands.py) /add /web /run ... │
│ │ │
│ ▼ │
git repo ◀───────────│ Coder (coders/base_coder.py) │
(GitRepo, repo.py) │ │ ├─ RepoMap (repomap.py) ──tree-sitter│
▲ │ │ │ └─ networkx PageRank ranking │
│ auto-commit │ │ ├─ ChatSummary (history.py) │
│ │ │ ├─ Linter (linter.py) │
│ │ │ └─ edit format (EditBlock/Whole/...) │
files ◀──apply edits──│ │ │
│ ▼ │
│ Model (models.py) ──▶ LiteLLM ──▶ provider │
│ (HTTP) │
│ │
│ Analytics (posthog/mixpanel) Scraper(/web) │
│ Voice(/voice) FileWatcher(--watch-files) │
│ GUI(gui.py, Streamlit) Onboarding(OAuth) │
└──────────────────────────────────────────────┘
Request lifecycle (interactive code mode)
- Startup (
main.py:main) — resolve git root, build the config-file search path, parse args (viaconfigargparse, see §5), load.envfiles, register model settings/metadata, select the model, constructInputOutput,Commands,GitRepo,ChatSummary, and finally theCoderviaCoder.create(). - Prompt —
InputOutput.get_input()collects the user's message (prompt_toolkit provides history, completion, multiline, vi/emacs modes). - Context assembly — the
Coderbuilds the message list: system prompt, repo map (RepoMap.get_repo_map()), in-chat file contents, read-only files, chat history (summarized byChatSummaryif too large), and the user message. - Model call —
Model.send_completion()calls LiteLLM, which dispatches to the provider; the response streams back throughMarkdownStream. - Edit extraction & application — the active coder subclass parses the model's reply in its edit format (
get_edits()) and applies it to disk (apply_edits()). - Post-processing — auto-lint (
Linter), auto-test (/testcommand), then auto-commit throughGitRepo.commit()with a weak-model-generated message. - Loop — control returns to step 2. Mode/model switches raise
SwitchCoder, which rebuilds the coder while preserving state.
Note
The whole system is filesystem- and git-centric: there is no server-side state. Chat history is a markdown file (
.aider.chat.history.md), the repo map cache is on disk, and "undo" is justgit revert-style rollback of Aider's own commits.
3. Component Inventory
All modules live under the aider/ package. The most important:
| Module | Class(es) | Responsibility |
|---|---|---|
main.py |
— | Entry point; arg parsing, environment setup, object wiring, top-level control loop. |
args.py |
get_parser() |
Defines every CLI flag (≈110) via configargparse; also the YAML/env config schema. |
args_formatter.py |
YamlHelpFormatter, MarkdownHelpFormatter, DotEnvFormatter |
Render the sample .aider.conf.yml / .env / docs from the parser. |
coders/base_coder.py |
Coder |
The core engine: context assembly, model calls, edit application, lint/test/commit orchestration. Coder.create() is the factory. |
coders/*.py |
14 coder subclasses | One per edit format (see §7). |
commands.py |
Commands, SwitchCoder |
All /slash commands (40+ cmd_* methods) and tab-completions. |
models.py |
Model, ModelSettings, ModelInfoManager |
Model registry, aliases, per-model settings/metadata, sanity checks. |
repomap.py |
RepoMap |
Tree-sitter symbol extraction + networkx PageRank ranking → the repo map. |
repo.py |
GitRepo |
All git operations: tracked files, diffs, commit, attribution, ignore handling. |
io.py |
InputOutput, AutoCompleter, ConfirmGroup |
Terminal I/O via prompt_toolkit; prompts, colors, history, completion. |
history.py |
ChatSummary |
Summarizes chat history when it exceeds the token budget. |
linter.py |
Linter, LintResult |
flake8 (Python) + tree-sitter syntax checks; per-language lint commands. |
watch.py |
FileWatcher |
Watches files for ai/ai!/ai? comments (IDE mode). |
scrape.py |
Scraper |
/web page fetching (Playwright or httpx) + pandoc → markdown. |
voice.py |
Voice |
Microphone capture (sounddevice/soundfile) + Whisper transcription. |
gui.py |
GUI, CaptureIO |
Streamlit browser interface. |
analytics.py |
Analytics |
Opt-in anonymous usage analytics (PostHog + Mixpanel). |
onboarding.py |
OAuthCallbackHandler |
OpenRouter OAuth and default-model selection for first-time users. |
help.py |
Help |
In-chat /help knowledge base over Aider's own docs. |
exceptions.py |
LiteLLMExceptions, ExInfo |
Maps LiteLLM exceptions to retry/no-retry behavior. |
mdstream.py |
MarkdownStream |
Live markdown rendering of streamed responses (rich). |
copypaste.py |
ClipboardWatcher |
Auto copy/paste mode plumbing. |
deprecated.py |
handle_deprecated_model_args |
Back-compat for old --sonnet/--4o/etc. shortcut flags. |
versioncheck.py |
— | Update checks, --upgrade, --install-main-branch. |
report.py |
— | Uncaught-exception reporting and /report GitHub issue helper. |
4. Requirements
Runtime
| Requirement | Detail |
|---|---|
| Python | >=3.10,<3.15 (3.10–3.14). |
| OS | Linux, macOS, Windows. |
| git | Recommended; required for auto-commit/undo. Index version 1 or 2. |
| Network | Outbound HTTPS to the chosen LLM provider. |
Key dependencies (from requirements.txt)
| Dependency | Pinned (at this commit) | Role |
|---|---|---|
litellm |
1.82.3 | Provider-agnostic LLM gateway. |
openai |
2.28.0 | OpenAI SDK (used via litellm). |
gitpython |
3.1.46 | Git operations. |
prompt-toolkit |
3.0.52 | Terminal input UI. |
rich |
14.3.3 | Styled output / markdown rendering. |
configargparse |
1.7.5 | Unified CLI + env + YAML config parsing. |
grep-ast |
0.9.0 | Tree-sitter-backed code structure extraction (repo map). |
tree-sitter-language-pack |
0.13.0 | Grammars for 100+ languages. |
networkx |
3.4.2 | PageRank ranking for the repo map. |
scipy / numpy |
1.16+ / 2.3+ | Numerical backing for ranking. |
diskcache |
5.6.3 | On-disk caches (repo-map tags). |
pathspec |
1.0.4 | .gitignore/.aiderignore matching. |
beautifulsoup4, pypandoc |
— | Web scraping → markdown. |
sounddevice, soundfile, pydub |
— | Voice capture. |
posthog, mixpanel |
— | Analytics. |
watchfiles |
1.1.1 | File watching for IDE mode. |
pillow |
12.1.1 | Image handling. |
Optional extras
| Extra | Adds |
|---|---|
aider-chat[browser] |
streamlit for the GUI. |
aider-chat[help] |
Embeddings/RAG deps for /help. |
aider-chat[playwright] |
Headless browser for JS-heavy /web pages. |
aider-chat[dev] |
Test + lint toolchain. |
5. Configuration Mastery
Precedence chain (highest → lowest)
Aider uses configargparse, which merges three sources. From main.py, the effective precedence is:
1. Command-line flags (e.g. --model sonnet)
2. Environment variables (incl. those loaded from .env files)
3. .aider.conf.yml (YAML config file)
4. Built-in defaults
Two subtleties from the startup sequence:
- Args are parsed multiple times: once to discover the git root and
--env-file, then.envfiles are loaded (which may define variables referenced by other flags), then args are parsed again so.env-provided values take effect. - Every flag maps to a YAML key (the flag name without
--) and an environment variable (AIDER_+ the flag name uppercased with underscores, perconfigargparse'sauto_env_var_prefix).
Config-file search order
.aider.conf.yml is searched for in this order (later overrides earlier are reconciled by configargparse):
- Current working directory
- Git repository root
- Home directory (
~/.aider.conf.yml)
Override entirely with --config <file> / -c.
.env search order
.env files are loaded with override=True from (first loaded wins precedence as loaded last in list, after reversal):
~/.aider/oauth-keys.env(always loaded first)- Home directory
~/.env - Git root
.env - Current directory
.env --env-file <file>
Worked example: a team baseline
A team can ship a repo-root .aider.conf.yml that everyone inherits, while individuals override per-machine via their home config or CLI flags:
# <repo>/.aider.conf.yml — checked into the project
model: sonnet
weak-model: haiku
auto-lint: true
lint-cmd:
- "python: flake8 --select=E9,F"
test-cmd: pytest -q
auto-test: false
attribute-co-authored-by: true
# A developer overrides the model for a heavy task, without touching the repo config:
aider --model opus
Important
Secrets do not belong in
.aider.conf.yml(it's often committed). Put API keys in.envor the environment. Aider auto-adds.envto.gitignore.
Caution
The YAML key for "always confirm yes" is
yes-always:, notyes:. Aider detects a strayyes:line on startup and refuses to run until you fix it (an old footgun that broke arg parsing).
Configuration domains
The ~110 flags group into these domains (see §16 for the full list, and the User Guide for usage):
| Domain | Representative keys |
|---|---|
| Model selection | model, weak-model, editor-model, edit-format, alias, reasoning-effort, thinking-tokens |
| Credentials | api-key, openai-api-key, anthropic-api-key, openai-api-base, set-env, verify-ssl |
| Git | git, auto-commits, dirty-commits, attribute-*, commit-prompt, subtree-only, git-commit-verify |
| Repo map | map-tokens, map-refresh, map-multiplier-no-files |
| Lint/test | lint-cmd, auto-lint, test-cmd, auto-test |
| History/cache | max-chat-history-tokens, cache-prompts, cache-keepalive-pings, restore-chat-history |
| UI | pretty, stream, dark-mode, light-mode, *-color, code-theme, vim, multiline, fancy-input, notifications |
| Files | file, read, aiderignore, add-gitignore-files, encoding, line-endings |
| Voice | voice-format, voice-language, voice-input-device |
| Analytics | analytics, analytics-disable, analytics-posthog-host, analytics-posthog-project-api-key, analytics-log |
| Updates | check-update, upgrade, install-main-branch, just-check-update |
6. Model Subsystem
Model roles
| Role | Flag | Purpose |
|---|---|---|
| Main | --model |
Primary reasoning + code generation. Default gpt-4o. |
| Weak | --weak-model |
Commit messages, chat-history summarization. Cheaper. |
| Editor | --editor-model |
Applies edits proposed by the architect in architect mode. |
Model.commit_message_models() returns the weak model (falling back to main) for commit generation — which is why commit messages can appear to "ignore" --model (see User Guide troubleshooting). Set --weak-model to control this.
Aliases (MODEL_ALIASES in models.py)
| Alias | Target | Alias | Target |
|---|---|---|---|
sonnet |
claude-sonnet-4-6 |
deepseek |
deepseek/deepseek-chat |
opus |
claude-opus-4-7 |
r1 |
deepseek/deepseek-reasoner |
haiku |
claude-haiku-4-5 |
flash |
gemini/gemini-flash-latest |
4o |
gpt-4o |
flash-lite |
gemini/gemini-2.5-flash-lite |
4 |
gpt-4-0613 |
gemini |
gemini/gemini-3-pro-preview |
4-turbo |
gpt-4-1106-preview |
gemini-2.5-pro |
gemini/gemini-2.5-pro |
35turbo/3 |
gpt-3.5-turbo |
grok3 |
xai/grok-3-beta |
Add custom aliases at runtime with --alias name:model-id (repeatable).
Model metadata & settings
Aider needs to know each model's context window, costs, and capabilities:
- Built-in — bundled
aider/resources/model-metadata.jsonplus LiteLLM's database. .aider.model.metadata.json(--model-metadata-file) — context size & cost for models Aider doesn't recognize..aider.model.settings.yml(--model-settings-file) — capability settings (default edit format, whether it acceptsreasoning_effort/thinking_tokens, prompt-cache support, etc.).
ModelInfoManager fetches/caches model info (including OpenRouter model info) and honors --no-verify-ssl. Model.sanity_check_models() warns when a model lacks metadata; suppress with --no-show-model-warnings.
Capability gating
--check-model-accepts-settings (default on) makes Aider skip applying reasoning_effort/thinking_tokens to models that don't declare support, warning instead of erroring. Force the setting with --no-check-model-accepts-settings.
Note
Because Aider delegates transport to LiteLLM, provider support is whatever LiteLLM supports — OpenAI, Anthropic, Gemini, Azure, Bedrock (
AWS_PROFILEhonored), DeepSeek, xAI, OpenRouter, GitHub Copilot (GITHUB_COPILOT_TOKEN), Ollama / any OpenAI-compatible base URL. Aider does not maintain its own provider integrations.
7. The Edit-Format / Coder System
Aider's central design idea: ask the model to emit changes in a structured edit format, then deterministically parse and apply them. Each format is implemented as a Coder subclass selected by Coder.create(edit_format=...).
edit_format |
Coder class | Notes |
|---|---|---|
diff |
EditBlockCoder |
SEARCH/REPLACE blocks — the default for most strong models. |
diff-fenced |
EditBlockFencedCoder |
SEARCH/REPLACE inside a fence (used by some Gemini models). |
whole |
WholeFileCoder |
Model returns entire files. Robust for weaker models. |
udiff |
UnifiedDiffCoder |
Unified-diff hunks. |
udiff-simple |
UnifiedDiffSimpleCoder |
Simplified unified diff. |
patch |
PatchCoder |
Structured add/update/delete/move patch actions. |
editor-diff |
EditorEditBlockCoder |
Edit-only variant for architect mode. |
editor-diff-fenced |
EditorDiffFencedCoder |
Fenced editor variant. |
editor-whole |
EditorWholeFileCoder |
Whole-file editor variant. |
architect |
ArchitectCoder |
Two-model: plan (main) + apply (editor). |
ask |
AskCoder |
Q&A, no edits. |
context |
ContextCoder |
Surfaces relevant surrounding context. |
help |
HelpCoder |
Answers questions about Aider. |
func |
(function-call coders) | Tool/function-calling variants (mostly internal/experimental). |
Each coder pairs with a prompt class (*Prompts, e.g. EditBlockPrompts) that supplies the system prompt and format instructions. The default format per model comes from the model's settings; override with --edit-format.
Edit-application failure modes
When a model emits a malformed edit, the coder raises a descriptive error and asks the model to retry. Representative errors (from source):
EditBlockCoder:Unpaired ``` in SEARCH/REPLACE block,Expected DIVIDER,Expected UPDATED or DIVIDER.PatchCoder(DiffError):Empty patch section found.,Could not find patch context ...,Overlapping or out-of-order chunk detected.,File referenced in patch not found ....UnknownEditFormat(base_coder.py): raised when an invalid--edit-formatis supplied.
These are the principal correctness-critical paths; they are heavily unit-tested (see §14).
8. The Repo Map
RepoMap (repomap.py) is what lets Aider work on large codebases without loading every file.
- Symbol extraction — for each tracked file,
grep-ast+tree-sitter-language-packparse the source and extract definitions/references (functions, classes, signatures). Tag-query files live underaider/queries/(per-language.scmfiles adapted from upstream tree-sitter grammars). - Ranking — references form a graph;
networkxPageRank ranks symbols by importance and by relevance to the files currently in the chat (personalization). - Budgeting — the top-ranked symbols are rendered into a compact map sized to
--map-tokens(default derived per model viaget_repo_map_tokens();0disables). - Caching — parsed tags are cached on disk (
diskcache) so subsequent runs are fast.--map-refreshcontrols rebuild frequency:auto(default),always,files(refresh when the in-chat file set changes),manual(/map-refreshonly).
Tip
When
--cache-promptsis enabled and--map-refreshisauto, Aider switches the refresh policy tofilesso the cached prompt prefix stays stable (set inmain.py). This is a cost optimization worth knowing about.
--map-multiplier-no-files (default 2) gives the map a larger budget when no files have been explicitly added, since the map is then the model's primary view of the code.
9. Git Subsystem
GitRepo (repo.py) wraps GitPython. Key behaviors:
- Discovery —
get_git_root()searches parent directories; if the cwd is the home directory, Aider warns rather than treating$HOMEas a repo. With no repo, Aider offers togit init. - Tracked files —
get_tracked_files()is the file universe Aider considers (minus.aiderignore). - Auto-commit — after applying edits,
commit()generates a message (weak model, or--commit-prompt) and commits. Disable with--no-auto-commits. - Dirty commits — by default Aider commits pre-existing dirty changes before editing (
--no-dirty-commitsto disable). - Attribution — author/committer name rewriting and
Co-authored-bytrailers, plus optionalaider:commit-message prefixes (--attribute-*).--attribute-co-authored-bytakes precedence over author/committer attribution unless those are explicitly set. - Pre-commit hooks — bypassed with
--no-verifyunless--git-commit-verifyis set. - Ignore files —
.aiderignore(configurable via--aiderignore) plus.gitignoreare honored viapathspec. - Sanity check —
sanity_check_repo()rejects unreadable/corrupt repos and unsupported index versions (1 or 2 only), offeringgit update-index --index-version=2.
Important
/undoonly reverts commits Aider itself authored, identified via attribution — it will not touch a developer's own commits. This is the core safety guarantee for unattended runs.
10. Security
Aider runs with the developer's full local privileges and talks to external model providers. Treat it as you would any developer tool that can execute code and reach the network.
Credentials
- API keys are read from CLI flags (
--api-key,--openai-api-key,--anthropic-api-key), environment variables, or.env/~/.aider/oauth-keys.env. - Aider sets provider keys into
os.environat startup so LiteLLM can use them. scrub_sensitive_info()redacts keys from the logged command line.- Keep keys out of
.aider.conf.yml(commonly committed). Aider auto-adds.envand.aider*to.gitignore.
Caution
--api-key provider=KEYand--openai-api-key KEYplace secrets in your shell history and process list. Prefer environment variables or.envfiles for anything beyond a quick test.
Code execution surface
Aider can execute commands on your machine in several ways — each gated by a confirmation prompt unless --yes-always is set:
| Vector | Control |
|---|---|
/run and ! shell commands |
Interactive confirmation; output optionally fed to the model. |
--test-cmd / --lint-cmd |
Run automatically when --auto-test/--auto-lint. You define these commands. |
| Suggested shell commands | The model may suggest commands; gated by --suggest-shell-commands (default on) and a confirmation. |
| File edits | Applied to disk, then committed; reversible via git. |
Warning
--yes-alwaysremoves all confirmation prompts, including before running shell/test commands. Only use it in trusted, sandboxed automation. Aider's own benchmark harness runs inside Docker precisely because it executes model-generated code unsupervised.
Network egress
- LLM provider — your prompts (system prompt, repo map, in-chat file contents, chat history) are sent to the configured provider. Anything in a file you
/addis shared with the model. /webscraping — fetches arbitrary URLs you request (Playwright or httpx).--disable-playwrightprevents installing/using a headless browser.- Version check — contacts PyPI on launch unless
--no-check-update. - Analytics — opt-in (see below).
- SSL — verified by default;
--no-verify-ssldisables verification and is set into the environment (SSL_VERIFY) and litellm's HTTP clients — use only against trusted internal endpoints.
Analytics & privacy
Analytics (analytics.py) sends opt-in, anonymous usage events to PostHog/Mixpanel. The product explicitly never collects code, chat messages, API keys, or personal info. On first run Aider asks for consent; users can:
- accept/decline at the prompt,
- pass
--analytics/--no-analyticsper session, --analytics-disableto permanently disable,- redirect to a self-hosted PostHog with
--analytics-posthog-host/--analytics-posthog-project-api-key, - log events locally with
--analytics-log.
Tip
For enterprise rollouts, set
analytics-disable: truein a shared.aider.conf.yml, or set--no-verify-sslonly for explicitly approved internal model proxies (never globally).
Reported security items
- Private Vulnerability Reporting request: issue #5217.
- A user-reported "AI guardrails" safety report: issue #5201 — a reminder that model-generated edits and shell commands require human review unless sandboxed.
11. Deployment Patterns
Aider is a local CLI, so "deployment" means provisioning it consistently across developers or CI.
Individual developer
aider-install (uv-based isolated environment) or pipx install aider-chat. Keep keys in ~/.env or the shell environment; keep team defaults in a repo-root .aider.conf.yml.
Team / shared baseline
- Commit a repo-root
.aider.conf.ymlwith model, lint/test commands, and attribution policy (no secrets). - Distribute a
~/.aider.conf.ymltemplate for per-user overrides. - Pin the Aider version in your environment manifest if you need reproducibility (
aider-chat==<version>).
CI / automation
pip install aider-chat
aider --model sonnet \
--yes-always --no-check-update --no-analytics \
--message "Apply the changes described in CHANGE.md" \
--message-file CHANGE.md
- Use
--message/--message-filefor one-shot, non-interactive runs. --yes-alwaysto skip prompts;--no-check-updateto avoid PyPI calls; disable analytics.- Provide keys via CI secrets → environment variables.
- Sandbox the runner — model-generated edits and any
--test-cmdexecute real code.
Docker (the benchmark harness pattern)
The repo's benchmark/Dockerfile shows the recommended isolation approach for running model-generated code unsupervised: a container with the toolchains installed and aider[dev] installed editable. Adapt it for any automation that runs Aider without a human in the loop.
Containerized via published image
AIDER_DOCKER_IMAGE is honored for version-check behavior, indicating Aider is commonly run from a container image; mount your repo as a volume and pass keys as environment variables.
12. Operations
Updates
| Command | Action |
|---|---|
aider --upgrade |
Upgrade to the latest PyPI release. |
aider --install-main-branch |
Install the latest from the main branch. |
aider --just-check-update |
Exit-code-only update check (good for scripts). |
aider --no-check-update |
Skip the launch-time update check. |
First-run-per-version state is tracked in ~/.aider/installs.json; release notes are offered on first run of a new version.
Logs & state on disk
| Path | Contents |
|---|---|
.aider.chat.history.md |
Human-readable transcript (in git root). |
.aider.input.history |
Input history. |
.aider.llm.history |
Raw LLM request/response log (only if --llm-history-file set). |
~/.aider/installs.json |
Per-version first-run tracking. |
~/.aider/analytics.json |
Analytics opt-in state + anonymous UUID. |
~/.aider/oauth-keys.env |
OAuth-obtained keys (e.g. OpenRouter). |
| Repo-map tag cache | diskcache directory (auto-managed). |
Diagnostics
aider --verbose # print config search order, loaded .env files, model metadata/settings
aider --settings # (in-chat /settings) dump effective settings
aider --show-repo-map # print the repo map and exit
aider --show-prompts # print the system prompts and exit
aider --exit # do all startup work then exit (smoke test)
--verbose is the first stop for "why did it pick that config/model?" questions — it prints the config-file search order with existence flags, the loaded .env files, and the full resolved model metadata and settings.
Cost & throughput controls
--weak-modelfor cheap commit/summary work.--cache-prompts+--cache-keepalive-pingsto reuse prompt prefixes.--map-tokensto bound repo-map cost;/dropunused files.--max-chat-history-tokensto control when summarization kicks in.
Warning
Streaming + caching together makes cost estimates inaccurate (Aider warns). For precise accounting, disable one.
Crash reporting
report_uncaught_exceptions() installs a handler that, on an uncaught exception, points users to file a GitHub issue with environment details. /report does the same on demand.
13. Developer Internals
Repository layout
aider/
__main__.py # `python -m aider` → main()
main.py # entry point & orchestration
args.py # configargparse parser (the config schema)
args_formatter.py # YAML/.env/markdown help renderers
coders/ # 14 edit-format coders + prompt classes + base_coder
commands.py # /slash command registry
models.py # Model/ModelSettings/aliases/metadata
repomap.py # tree-sitter + networkx repo map
repo.py # GitRepo
io.py # InputOutput, AutoCompleter
history.py # ChatSummary
linter.py # Linter
watch.py # FileWatcher (IDE mode)
scrape.py # /web Scraper
voice.py # /voice
gui.py # Streamlit GUI
analytics.py # opt-in analytics
onboarding.py # OpenRouter OAuth + default model
help.py # /help knowledge base
exceptions.py # LiteLLM exception mapping
mdstream.py # streamed markdown rendering
queries/ # per-language tree-sitter tag queries (.scm)
resources/ # bundled model-metadata.json, etc.
website/ # docs site (Jekyll/just-the-docs) + install scripts
benchmark/ # Exercism-based benchmark harness (Docker)
tests/ # unit/integration tests
requirements/ # pinned requirement sets + extras
The Coder base class
coders/base_coder.py:Coder (≈80 methods) is the heart of the system. Notable responsibilities:
create()/clone()— factory dispatch byedit_format;clone()rebuilds a coder for mode switches.format_messages()/format_chat_chunks()— assemble system prompt, repo map, files, history intoChatChunks(which also manages prompt-cache control headers viaadd_cache_control_headers()).send_message()/run()— the model-call loop, streaming, retries.get_edits()/apply_edits()— overridden per edit format.auto_commit(),lint_edited(),run_tests()— post-edit pipeline.get_repo_map()— delegates toRepoMap.
SwitchCoder (an exception in commands.py) is the mechanism for changing model/mode/edit-format mid-session: it's raised, caught in main.py's loop, and used to rebuild the coder while carrying over state.
Adding a new edit format (extension point)
- Create
coders/myformat_coder.pywith aMyFormatCoder(Coder)settingedit_format = "myformat"and implementingget_edits()/apply_edits(). - Add a matching
MyFormatPrompts(CoderPrompts)with the system/format prompt. - Register the class in
coders/__init__.py's imports and__all__. Coder.create()will then dispatch--edit-format myformatto it.
Adding a new slash command (extension point)
Add a cmd_<name>(self, args) method to Commands in commands.py. The first-line docstring becomes the help text shown in /help. Optionally add completions_<name>() for tab-completion. The command is auto-discovered — no registration table to edit.
Exception handling
exceptions.py:LiteLLMExceptions maps each LiteLLM exception type to an ExInfo(retry, ...) record so the send loop knows whether to retry (e.g. rate limits) or surface the error. A sanity check asserts every LiteLLM exception is accounted for ({var} is in litellm but not in aider's exceptions list), so upgrading LiteLLM may require updating this map.
14. Testing Strategy
Tests live in tests/ and use unittest/pytest.
| Test module | Covers |
|---|---|
tests/basic/test_main.py (TestMain, ~80 tests) |
End-to-end CLI: arg handling, git discovery, config, env loading. |
tests/basic/test_coder.py (TestCoder) |
Edit permissions, file mentions, context assembly. |
tests/basic/test_commands.py (TestCommands, ~70 tests) |
Every /slash command. |
tests/basic/test_editblock.py, test_udiff.py, test_wholefile.py, test_find_or_blocks.py |
Edit-format parsing/application (the correctness-critical paths). |
tests/basic/test_repomap.py (TestRepoMapAllLanguages) |
Repo map across 40+ languages. |
tests/basic/test_models.py, test_model_info_manager.py |
Aliases, metadata, sanity checks. |
tests/basic/test_repo.py (TestRepo) |
Git operations, commit messages, diffs. |
tests/basic/test_sendchat.py, test_reasoning.py |
Model send loop, retries, reasoning content handling. |
tests/basic/test_onboarding.py |
OpenRouter OAuth and default-model selection. |
tests/basic/test_ssl_verification.py, test_aws_credentials.py |
Security-relevant config paths. |
tests/scrape/, tests/browser/, tests/help/ |
/web scraping, GUI import, /help. |
Run the suite with the dev extra installed:
pip install -e .[dev]
pytest
Benchmark harness
benchmark/ contains an Exercism-based evaluation that measures how well Aider + a given model can translate natural-language requests into passing code. It runs inside Docker (it executes model-generated code) and emits a YAML report (pass_rate_1, pass_rate_2, percent_cases_well_formed, cost, timing). Results feed the public leaderboards. Not for end users.
./benchmark/benchmark.py a-run-name --model sonnet --edit-format diff --threads 10 \
--exercises-dir polyglot-benchmark
15. Potential Improvements
Observations from the source, framed for maintainers/operators:
base_coder.pyis very large (≈80 methods on one class). Extracting the prompt-cache, message-assembly, and post-edit (lint/test/commit) responsibilities into collaborators would improve testability.commands.pyis a single ~3,100-line module with 40+ commands. The auto-discovery pattern is clean, but grouping commands into mixins by domain (files/git/models/io) would aid navigation.- LiteLLM exception coupling — the assertion that every LiteLLM exception is mapped means a dependency bump can break startup; consider a softer fallback (treat unknown exceptions as non-retryable + warn) to decouple upgrade cadence.
- Deprecated edit-format coders (
editblock_func_coder,wholefile_func_coder) raiseRuntimeError("Deprecated, needs to be refactored ...")— dead code that could be removed. - Config precedence is parsed in several passes in
main.py(to resolve git root →.env→ re-parse). Documenting/centralizing this would reduce the risk of subtle precedence bugs. - Windows PATH friction on install (issue #3123) — the
aider-installpath is the mitigation; surfacing it more prominently for Windows users would cut support load.
16. Quick Reference
Files Aider reads/writes
| File | Role |
|---|---|
.aider.conf.yml |
Config (YAML). Search: cwd → git root → home. |
.env |
Secrets/env. Search includes ~/.aider/oauth-keys.env. |
.aiderignore |
Paths Aider must not read/edit. |
.aider.model.settings.yml |
Capabilities for unknown models. |
.aider.model.metadata.json |
Context/cost for unknown models. |
.aider.input.history, .aider.chat.history.md |
Session history. |
~/.aider/{installs,analytics}.json, oauth-keys.env |
Per-user state. |
Environment variables of note
| Variable | Effect |
|---|---|
OPENAI_API_KEY, ANTHROPIC_API_KEY, OPENROUTER_API_KEY, GEMINI_API_KEY, DEEPSEEK_API_KEY |
Provider credentials. |
OPENAI_API_BASE |
Custom/proxy/local endpoint. |
AWS_PROFILE |
Used for Bedrock models. |
GITHUB_COPILOT_TOKEN |
Required for Copilot models. |
AIDER_* |
Any flag as an env var (e.g. AIDER_MODEL). |
AIDER_DOCKER_IMAGE |
Signals containerized run (version-check behavior). |
AIDER_CACHE_KEEPALIVE_DELAY |
Cache keepalive timing. |
SSL_VERIFY |
Set empty by --no-verify-ssl. |
EDITOR / VISUAL |
External editor for /editor. |
NO_COLOR |
Disable colored output. |
One-shot / non-interactive modes
| Flag | Exits after |
|---|---|
--message / -m, --message-file / -f |
Processing one message. |
--lint, --test, --commit |
The named action. |
--apply <file> |
Applying edits from a file. |
--show-repo-map, --show-prompts, --exit |
Printing/setup (debug). |
--list-models, --just-check-update, --version |
Reporting. |
Edit formats
diff · diff-fenced · whole · udiff · udiff-simple · patch · editor-diff · editor-diff-fenced · editor-whole · architect · ask · context · help · func
Chat modes
code (default) · ask · architect · context · help