# Aider-AI/aider

> Python terminal pair-programmer: plain-text SEARCH/REPLACE edits, a tree-sitter repo map ranked by PageRank, and git auto-commits.

- Category: [Open-source coding agents](https://llms-technical-reviews.com/coding-agents/)
- Repository: https://github.com/Aider-AI/aider (reviewed at commit `5dc9490bb35f9729ef2c95d00a19ccd30c26339c`, 2026-05-22)
- Stars: 49395 · Language: Python · License: Apache-2.0
- Canonical page: https://llms-technical-reviews.com/p/aider/

## Overview

Aider is a chat-driven coding assistant that runs in your terminal, inside a git repository. You add files to the chat, describe a change, and the model answers with edits written in a plain-text format. Aider parses those edits, writes them to disk and commits them to git. It is a single Python package (`aider/`, about 20,000 lines) installed as the `aider` command ([pyproject.toml](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/pyproject.toml#L20-L27)).

The design is human-in-the-loop. Aider has no tool-calling agent loop and does not explore the repository by itself. The model sees the files you added, plus a compact "repo map" of the rest of the codebase. If it needs another file, it names it and Aider asks you whether to add it. Every model call goes through LiteLLM, so any provider LiteLLM supports works. Because edits are parsed from text, a model without native function calling can still edit code.

This suits developers who want to stay in control of each step and keep a clean, reviewable git history. It does not suit people who want an agent that plans, runs tests and iterates on its own for long stretches.

## Architecture

```mermaid
flowchart LR
  U["User (terminal / --message)"] --> M["main.py main()"]
  M --> C["Coder.create()"]
  C --> EF["Edit-format coder<br/>(diff, whole, udiff, patch, architect...)"]
  EF --> CH["format_chat_chunks()"]
  RM["RepoMap (tree-sitter + PageRank)"] --> CH
  HS["ChatSummary (weak model)"] --> CH
  CH --> MD["Model.send_completion()"]
  MD --> LL["LiteLLM"]
  LL --> P["LLM provider"]
  EF --> AP["apply_updates()"]
  AP --> FS["Files on disk"]
  AP --> GR["GitRepo.commit()"]
  AP --> LT["Linter / test cmd"]
  LT -- "errors" --> EF
```

| Component | Path | Role |
|---|---|---|
| Entry point | `aider/main.py` | Parses args, builds `Model`, `GitRepo`, `Coder`; restarts the coder on mode switches |
| Base coder | `aider/coders/base_coder.py` | Chat loop, prompt assembly, streaming, retries, edit/commit/lint/test cycle |
| Edit formats | `aider/coders/*_coder.py` | One subclass per edit format (`diff`, `whole`, `udiff`, `patch`, `architect`, ...) |
| Repo map | `aider/repomap.py` | Tree-sitter tags, PageRank ranking, token-budgeted map |
| Models | `aider/models.py`, `aider/resources/model-settings.yml` | Per-model settings, aliases, LiteLLM call |
| History | `aider/history.py` | Recursive summarisation of older chat turns |
| Git | `aider/repo.py` | Commits, generated commit messages, attribution |
| Commands | `aider/commands.py` | `/add`, `/drop`, `/undo`, `/run`, `/test`, `/architect`, ... |
| Linter | `aider/linter.py` | Tree-sitter syntax check, plus `compile` and flake8 for Python |
| Watcher | `aider/watch.py` | Turns `AI!` / `AI?` comments in source files into requests |

## How a request flows

1. `main()` builds the model, the git repo and the coder with `Coder.create()`, then calls `coder.run()` in a loop. A `SwitchCoder` exception (from `/chat-mode`, `/architect`, etc.) rebuilds the coder and carries the files and history over ([main.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/main.py#L1155-L1185), [base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L124-L201)).
2. `run()` reads input. `run_one()` handles slash commands, file mentions and URLs, then calls `send_message()`. If a step sets `reflected_message`, it loops again, up to `max_reflections = 3` ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L876-L944)).
3. `format_chat_chunks()` builds the prompt in a fixed order: system prompt, example conversations, read-only files, repo map, summarised history, chat files, the current turn and a reminder ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1226-L1331), [chat_chunks.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/chat_chunks.py#L5-L40)). This order keeps the stable parts first, so prompt caching works.
4. `send_message()` streams the reply. It retries with exponential backoff on retryable LiteLLM errors. When the output hits the token limit, it continues with assistant prefill if the model supports it ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1419-L1520)).
5. If the reply names files that are not in the chat, Aider asks to add them and resends. Otherwise `apply_updates()` parses the edits, does a dry run, asks before editing new or un-added files, and writes the files ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2269-L2336)).
6. `auto_commit()` commits the edited files. Then the linter runs, and if `--auto-test` is set, the test command runs too. On errors Aider asks "Attempt to fix lint errors?" and feeds the output back as the next message ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1587-L1623)).

## Key components

### Edit formats

Each format is a `Coder` subclass with its own prompts and parser. `Coder.create()` picks the class whose `edit_format` matches the model's setting. The default `diff` format (`EditBlockCoder`) parses `<<<<<<< SEARCH / ======= / >>>>>>> REPLACE` blocks. It tries an exact match, then a match that ignores leading whitespace, then a match without a spurious leading blank line, then `...` elision. The fuzzy edit-distance matcher is still in the file, but an early `return` makes it unreachable ([editblock_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/editblock_coder.py#L157-L188)). A failed block raises an error that lists the closest real lines and tells the model which blocks already applied ([editblock_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/editblock_coder.py#L41-L124)). Shell commands in fenced blocks are collected here too.

### Architect mode

`ArchitectCoder` lets one model describe the change in prose. After you confirm, it creates a second coder with the "editor" model and edit format, with no repo map, and runs it on that text ([architect_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/architect_coder.py#L6-L48)). This is Aider's only form of two-model delegation. Other non-editing modes are `ask`, `help` and `context`, which suggests the files to add.

### Repo map

`RepoMap` extracts definitions and references with tree-sitter queries and caches them on disk. It builds a graph from files that reference a name to files that define it. Edges get extra weight when the name was mentioned in the chat, when it is a long camel- or snake-case name, or when the referencing file is in the chat. Then it runs personalised PageRank ([repomap.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/repomap.py#L465-L520)). A binary search picks how many ranked tags fit the token budget ([repomap.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/repomap.py#L629-L700)). The default budget is 1/8 of the model's input window, between 1,024 and 4,096 tokens.

### History summarisation

When older turns grow past `max_chat_history_tokens`, `ChatSummary` summarises them in a background thread. It keeps the most recent half-budget of messages verbatim and recurses up to depth 3 ([history.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/history.py#L7-L60), [base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1002-L1034)). The summariser is the weak model, which falls back to the main model.

### Models

`ModelSettings` holds per-model tuning: edit format, weak and editor models, whether to send the repo map, where to put the reminder, and extra parameters. The package ships 357 entries in `model-settings.yml` ([models.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L128-L158)). `send_completion()` calls `litellm.completion`. It sets temperature 0 by default and sizes `num_ctx` for Ollama ([models.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L985-L1037)). LiteLLM is imported lazily to save start-up time ([llm.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/llm.py#L21-L45)). Cost is computed from LiteLLM's per-token prices, with explicit adjustments for Anthropic and DeepSeek cache tokens ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2070-L2100)).

### Git and undo

Before editing a file that has uncommitted changes, Aider commits it separately (`--dirty-commits`). Each edit gets its own commit, with a message written by the weak model and a `Co-authored-by: aider (<model>)` trailer by default ([base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2375-L2423)). `/undo` resets only commits that Aider made in this session. It refuses if the commit is already pushed or the files are dirty ([commands.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/commands.py#L553-L612)).

## Extending it

- Per-model behaviour: `--model-settings-file` and `--model-metadata-file` add or override YAML settings and LiteLLM metadata.
- Scope: `.aiderignore` and `--subtree-only` keep parts of a large repo out of the map and the file list.
- Conventions: there is no rules-file loader. You pass a conventions file as read-only context with `--read`.
- Linting and tests: `--lint-cmd` per language and `--test-cmd`. Output goes back to the model ([linter.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/linter.py#L82-L134)).
- Editor integration: `--watch-files` reacts to comments ending in `AI!` (make the change) or `AI?` (answer a question) in any watched file ([watch.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/watch.py#L66-L71)).
- Scripting: `aider --message "..."` runs one turn and exits. From Python, you can build a `Coder` with `Coder.create()` and call `coder.run(with_message=...)`.
- New edit formats are new `Coder` subclasses registered in `aider/coders/__init__.py`.

There is no MCP client, plugin loader or hook system in this repo.

## Running it

Install with `pip install aider-chat` (or `pipx`/`uv tool`). Python 3.10 to 3.14 is supported. Set a provider key (for example `ANTHROPIC_API_KEY`) and run `aider` inside a git repo. If you name no model, it picks one from the keys it finds: OpenRouter, then Anthropic (`sonnet`), DeepSeek, OpenAI (`gpt-4o`), Gemini and Vertex ([onboarding.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/onboarding.py#L44-L74)). Aliases like `sonnet`, `opus`, `deepseek` and `flash` map to current model IDs ([models.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L99-L123)). No server is needed. `--gui` starts an optional Streamlit browser UI. Analytics go to PostHog: Aider asks a random 10% of users to opt in, and `--analytics-disable` turns it off for good ([analytics.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/analytics.py#L119-L135)).

## Strengths and caveats

- **Strength: works with almost any model.** Text edit formats and per-model settings let weak or local models edit code. You do not need function calling.
- **Strength: git history as the safety net.** Every change is a small commit with a generated message, and `/undo` is careful about pushed or dirty state.
- **Strength: repo map.** Ranking by references gives a useful map of a large repo in about 1-4K tokens, without embeddings or an index server.
- **Caveat: you drive.** No autonomous loop, no sub-agents. Reflection is capped at 3 rounds per message, and file reads need your approval.
- **Caveat: no sandbox.** Shell commands from the model run on the host through `run_cmd` after a confirmation. With `--yes-always`, shell prompts default to *no* (`explicit_yes_required`), which is a deliberate guard ([io.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/io.py#L807-L867), [base_coder.py](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2450-L2485)).
- **Caveat: strict matching.** SEARCH blocks must match exactly, apart from whitespace and `...`. A failed block costs one more model round.
- **Caveat: linting depth.** Only Python gets a full linter (`compile` plus flake8). Other languages get a tree-sitter syntax check unless you set `--lint-cmd`.

*Sources: code at 5dc9490, deepwiki-open wiki (13 pages), OpenDeepWiki wiki (20 pages), verified Q&A.*

## How Aider-AI/aider answers the Open-source coding agents questions

### How is the agent loop implemented? (answered)

**Single loop with coder subclasses, not a separate planner.** The main loop lives in `Coder.run()` (`aider/coders/base_coder.py:876-891`). It calls `self.get_input()` to read a user message, then `self.run_one()` to process it. `run_one()` (`base_coder.py:924-944`) calls `send_message()` synchronously, then optionally loops on `self.reflected_message` (up to `max_reflections=3`) to handle multi-turn self-correction (e.g., file-mention follow-ups, lint-error feedback).

**No tool-call schema by default — edits are text-based.** The LLM returns SEARCH/REPLACE blocks in plain text for the "diff" edit format (via `EditBlockCoder.get_edits()`, `aider/coders/editblock_coder.py:21-36`). There is an alternative function-calling path: `WholeFileFunctionCoder` and `EditBlockFunctionCoder` define JSON-schema `functions` lists (e.g. `write_file` with path/content params), which are sent as `tools` in the litellm request (`aider/models.py:1006-1009`). The architect mode (`ArchitectCoder`, `aider/coders/architect_coder.py`) uses a two-stage sub-agent: the architect model plans, then spawns a separate editor sub-coder via `Coder.create()` to apply changes.

**Turn structure.** Each turn: user input is preprocessed (URL scraping, file-mention detection via `check_for_file_mentions` at base_coder.py:1761), formatted into structured message chunks (system + examples + done-messages + repo-map + readonly files + chat files + cur + reminder — see `format_chat_chunks()` at base_coder.py:1226-1331), sent to the LLM via `model.send_completion()` (`aider/models.py:985-1037`), and the response is parsed for edits or shell commands. After the response, edits are applied, auto-commits happen, lint runs, shell commands execute with user confirmation, and tests optionally run.

**Stop conditions.** The loop runs until EOF (Ctrl-D), double Ctrl-C (`keyboard_interrupt()` at base_coder.py:986-1000), or `/exit`. Within a turn, the reflection loop stops when `msg` is empty or `max_reflections` is hit.

**Sub-agents.** Only in architect mode: `ArchitectCoder.reply_completed()` creates a new `Coder` instance to execute edits (`architect_coder.py:37-44`). There is no general sub-agent framework.


Citations: [aider/coders/base_coder.py:876-944](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L876-L944) · [aider/coders/base_coder.py:1226-1331](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1226-L1331) · [aider/coders/editblock_coder.py:21-36](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/editblock_coder.py#L21-L36) · [aider/coders/architect_coder.py:6-44](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/architect_coder.py#L6-L44) · [aider/models.py:985-1037](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L985-L1037) · [aider/coders/base_coder.py:1419-1490](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1419-L1490)

### How is repository context gathered and kept within the context window? (answered)

**Repo map via tree-sitter tags.** The `RepoMap` class (`aider/repomap.py:42`) scans all tracked files in the git repo with tree-sitter to extract symbol definitions (functions, classes, etc.) as `Tag` objects. The method `get_ranked_tags_map()` (`repomap.py:576-627`) ranks tags by relevance — files already in the chat rank higher, then files with mentioned identifiers or filenames. A binary search (`repomap.py:676-700`) iteratively includes more tags until the map fills the allocated token budget (`max_map_tokens`, default 1024). The result is a compact tree showing file names and their key symbols. The map is cached in an LRU dict and also in a diskcache-backed SQLite DB at `.aider.tags.cache.v3/` for cross-session reuse.

**File content in messages.** The `Coder.format_chat_chunks()` method (`base_coder.py:1226`) assembles messages in this order: system prompt, examples, repo map (as user/assistant pair), readonly file contents, done/archived messages, editable file contents, current conversation, reminder. File contents are read from disk via `get_files_content()` and inserted as literal text. Image files are base64-encoded and sent as `image_url` content blocks (`get_images_message()` at base_coder.py:817-850).

**File-add by mention.** When the LLM mentions a file not yet in the chat, `check_for_file_mentions()` (`base_coder.py:1761-1781`) detects the mention and asks the user to add it. This is a form of dynamic context expansion.

**Summarisation of long sessions.** The `ChatSummary` class (`aider/history.py`) runs in a background thread (`summarize_start()` at base_coder.py:1002-1012). When `done_messages` exceed `max_chat_history_tokens`, the summarizer trims older messages by splitting at the halfway point and using the weak model to compress the head segment via `summarize_all()` (`history.py:98`), keeping the more recent tail intact. Depth is capped at 3 recursive splits.

**Prompt caching.** When enabled, `ChatChunks.add_cache_control_headers()` (`aider/coders/chat_chunks.py:28-55`) adds `cache_control: ephemeral` markers to the last message of cacheable segments (examples/system, repo/readonly, chat files). A background thread (`warm_cache()` at base_coder.py:1340-1394) periodically sends 1-token pings to keep the cache alive.

> **Editor's note.** Correction: the repo-map budget is not a flat 1,024 tokens. By default it is 1/8 of the model's input window, clamped to 1,024–4,096 tokens, and files are ranked with personalised PageRank.

Citations: [aider/repomap.py:42-80](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/repomap.py#L42-L80) · [aider/repomap.py:576-700](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/repomap.py#L576-L700) · [aider/coders/base_coder.py:750-815](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L750-L815) · [aider/coders/base_coder.py:1002-1034](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1002-L1034) · [aider/history.py:7-99](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/history.py#L7-L99)

### How are code edits applied? (answered)

**Four core edit formats, selected per-model.** The edit format is determined by the model settings (`aider/resources/model-settings.yml`) and can be overridden via `--edit-format`. Available formats include:

- **diff (SEARCH/REPLACE blocks)** — the default for most models. Implemented by `EditBlockCoder` (`aider/coders/editblock_coder.py`). The LLM returns blocks with `<<<<<<< SEARCH` / `=======` / `>>>>>>> REPLACE` fences. `get_edits()` parses them, `apply_edits()` performs a literal string replacement via `do_replace()`. If exact match fails, it tries other chat files, then produces a `SearchReplaceNoExactMatch` error with `find_similar_lines()` suggestions (`editblock_coder.py:38-100`).

- **whole (full file replacement)** — `WholeFileCoder` (`aider/coders/wholefile_coder.py`). The LLM returns the entire file content inside backtick fences. `get_edits()` parses the path before the opening fence and the content between fences.

- **udiff (unified diff format)** — `UnifiedDiffCoder` (`aider/coders/udiff_coder.py`). The LLM returns standard unified diff hunks. Uses `search_and_replace()` and `flexible_search_and_replace()` from `aider/coders/search_replace.py`.

- **diff-fenced** — `EditBlockFencedCoder`, like diff but with language-specific fence markers.

**Function/tool-calling variants.** `WholeFileFunctionCoder` and `EditBlockFunctionCoder` (sibling modules in `aider/coders/`) define JSON tool schemas (e.g. `write_file` with path/content) and the LLM calls them as function calls via litellm's tools parameter (`models.py:1006-1009`). These function-based coders are not in `__all__` by default but can be enabled.

**Validation and retries.** After edits are applied, if `auto_lint` is enabled, `lint_edited()` (`base_coder.py:1681-1696`) runs a linter on every changed file. If lint errors are found, the user is prompted: "Attempt to fix lint errors?" If yes, the errors become a `reflected_message` and the LLM gets another turn to fix them (`base_coder.py:1599-1607`). Similarly for test failures (`base_coder.py:1616-1623`).

**Git integration.** Before each message, `dirty_commit()` (`base_coder.py:2411-2423`) auto-commits any unstaged changes to relevant files so edits can be undone. After successful edits, `auto_commit()` (`base_coder.py:2375-2395`) creates an aider commit. `/undo` (`commands.py:553`) reverts the last aider commit.


Citations: [aider/coders/editblock_coder.py:15-100](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/editblock_coder.py#L15-L100) · [aider/coders/wholefile_coder.py:1-80](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/wholefile_coder.py#L1-L80) · [aider/coders/base_coder.py:1599-1623](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L1599-L1623) · [aider/coders/base_coder.py:2296-2336](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2296-L2336) · [aider/coders/base_coder.py:2375-2405](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L2375-L2405) · [aider/models.py:1006-1015](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L1006-L1015)

### How are shell commands and file writes kept safe? (answered)

**User approval for every shell command.** Shell commands are suggested by the LLM inside ` ```bash ` blocks. They are extracted in `EditBlockCoder.get_edits()` (`editblock_coder.py:33`) or via `run_shell_commands()` (`base_coder.py:2434-2486`). Each command goes through `handle_shell_commands()` which calls `self.io.confirm_ask("Run shell command?", explicit_yes_required=True)` (`base_coder.py:2456`) — `explicit_yes_required=True` means the user must type 'y', not just press Enter. The `ConfirmGroup` mechanism allows "Skip all"/"Allow all" within a batch. The `allow_never` flag lets users permanently reject a specific command pattern.

**`--yes-always` mode.** The flag `--yes-always` / config `yes-always: true` (`aider/args.py:760-764`) bypasses all confirmation prompts. In `confirm_ask()` (`aider/io.py:866-867`): when `self.yes is True`, the response is `"y"` unless `explicit_yes_required` is set. This is the only automatic bypass — every shell command execution and file write by default requires explicit user confirmation per operation.

**No sandboxing.** There is no container isolation, seatbelt, Landlock, or any OS-level sandbox. The `run_cmd()` function (`aider/run_cmd.py:11-23`) uses `pexpect.spawn` (on Unix TTYs) or `subprocess.Popen` with `shell=True`. The linter (`aider/linter.py`) also calls `run_cmd_subprocess` directly. The sole protection is user approval prompts.

**Network restrictions.** None enforced by aider. The LLM calls go through litellm (external APIs), and web scraping uses Playwright. There are no allow/deny lists for network access.

**File-write safety.** Writes happen through `self.io.write_text()` only after the LLM has generated them and the user has seen and implicitly accepted them by not cancelling. The `dry_run` flag (`base_coder.py:415`) skips all writes. The git workflow (`dirty_commit` before edits, `auto_commit` after) provides an undo safety net. The `aiderignore` file (`.aiderignore`, parsed in `repo.py`) can exclude files from being read or edited.

**Checkpoints via git.** Each aider edit session auto-commits before making changes (`dirty_commit`, base_coder.py:2411), and commits after applying edits (`auto_commit`, base_coder.py:2375). The `/undo` command (`commands.py:553`) reverts the last aider commit using `git revert`. The `aider_commit_hashes` set tracks which commits are aider-generated.


Citations: [aider/io.py:806-920](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/io.py#L806-L920) · [aider/run_cmd.py:11-84](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/run_cmd.py#L11-L84) · [aider/args.py:760-765](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/args.py#L760-L765) · [aider/commands.py:553-580](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/commands.py#L553-L580)

### Which models are supported and how are they called? (answered)

**Provider-agnostic via litellm.** All model calls go through `litellm.completion()` (`aider/llm.py` lazily imports litellm). This means any model that litellm supports — OpenAI, Anthropic, Gemini, DeepSeek, Ollama, Together, OpenRouter, AWS Bedrock, Azure, Cohere, Replicate, etc. — is usable. API keys are set via environment variables (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`) or the `--api-key` flag (`aider/main.py:600-630`).

**Built-in known model lists.** `aider/models.py:33-123` defines `OPENAI_MODELS` (30+ variants from gpt-3.5-turbo through o1, o3-mini, gpt-5.5), `ANTHROPIC_MODELS` (claude-2 through claude-sonnet-4-6, haiku-4-5, opus-4-7) and `MODEL_ALIASES` (e.g. "sonnet" → "claude-sonnet-4-6").

**Per-model prompt tuning.** The `ModelSettings` dataclass (`models.py:128-151`) carries per-model parameters: `edit_format` (diff/whole/udiff/etc), `use_repo_map`, `use_system_prompt`, `use_temperature`, `reminder` style ("user" or "sys"), `cache_control`, `examples_as_sys_msg`, `extra_params`, and `accepts_settings` (e.g. "thinking_tokens"). Settings are loaded from `aider/resources/model-settings.yml` (`models.py:153-158`) and can be extended with user-provided `.aider.model.settings.yml` files registered via `register_models()` (`main.py:335-358`). Additional per-model heuristics in `Model.__init_post__()` (`models.py:490-598`) set defaults based on model name patterns — DeepSeek R1 gets `reasoning_tag="think"` and `use_temperature=False`, GPT-4-turbo gets `edit_format="udiff"`, o1 models disable `use_system_prompt`.

**Tool calling vs text formats.** When a model has a function-based coder (like `WholeFileFunctionCoder`), the LLM receives `tools` in the litellm API call (`models.py:1006-1009`). Otherwise edits are text-based and parsed from the model's plain-text response. The `edit_format` field on the model determines which.

**Cost tracking.** The `ModelInfoManager` (`models.py:161`) fetches `model_prices_and_context_window.json` from the litellm GitHub repo (cached for 24h). The OpenRouter model database is also cached locally. Costs are calculated after each completion (`base_coder.py:1811`).


Citations: [aider/models.py:33-123](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L33-L123) · [aider/models.py:128-200](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L128-L200) · [aider/models.py:985-1038](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L985-L1038) · [aider/llm.py:1-47](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/llm.py#L1-L47) · [aider/models.py:490-600](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/models.py#L490-L600) · [aider/main.py:600-630](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/main.py#L600-L630)

### How can it be extended and customised? (answered)

**No MCP or plugin system.** The project has no MCP (Model Context Protocol) support and no plugin architecture. Extension is done through configuration, coders, and the custom model registry rather than a formal plugin API.

**Custom coders.** New edit formats can be added by subclassing `Coder` (`aider/coders/base_coder.py`) and registering the class in `aider/coders/__init__.py`'s `__all__` list. The dispatcher `Coder.create()` iterates `coders.__all__` and matches by `edit_format` string (`base_coder.py:190-194`). This is how all 12+ coder types (diff, whole, udiff, architect, editor variants, etc.) are registered.

**Custom models and settings.** Users can add `.aider.model.settings.yml` and `.aider.model-metadata.json` files that override model parameters. `register_models()` (`main.py:335-358`) searches in the standard git-root search path. The YAML file defines `ModelSettings` entries; the JSON file adds cost/context-window metadata.

**Conventions files.** A file like `CONVENTIONS.md` can be loaded read-only with `--read CONVENTIONS.md` or configured in `.aider.conf.yml` as `read: [CONVENTIONS.md]`. This is highlighted in the project docs as the recommended way to inject standing instructions like coding conventions.

**Headless / SDK use.** The `main()` function in `aider/main.py:451` is callable programmatically with `return_coder=True` to get a `Coder` instance back instead of running the interactive loop. The `Coder.create()` factory plus calling `coder.run(with_message=...)` allows non-interactive use. Environment variables serve as the config API (all CLI flags have corresponding `AIDER_*` env vars).

**Slash commands.** Over 30 built-in commands in `aider/commands.py` (`Commands` class) handle file management (`/add`, `/drop`, `/read-only`), git (`/commit`, `/diff`, `/undo`), model switching (`/model`, `/chat-mode`), execution (`/run`, `/test`, `/lint`), and session management (`/clear`, `/reset`, `/save`, `/load`).

**`--load` for automation.** The `--load` flag (`args.py:773`) reads and executes a file of slash commands on startup, providing a limited scripting mechanism.


Citations: [aider/coders/base_coder.py:124-201](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/coders/base_coder.py#L124-L201) · [aider/main.py:335-358](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/main.py#L335-L358) · [aider/main.py:451-460](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/main.py#L451-L460) · [aider/commands.py:36-160](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/commands.py#L36-L160) · [aider/commands.py:1013-1054](https://github.com/Aider-AI/aider/blob/5dc9490bb35f9729ef2c95d00a19ccd30c26339c/aider/commands.py#L1013-L1054)
