Aider Architecture Index
Aider Architecture Index
Section titled “Aider Architecture Index”Reference Pin
Section titled “Reference Pin”- Repository:
references/aider - Commit SHA:
b9050e1d5faf8096eae7a46a9ecc05a86231384b - Indexed scope: Full
aider/source tree
Table of Contents
Section titled “Table of Contents”- System Overview
- RepoMap & Context Selection
- Coders Subsystem (Edit Formats)
- LLM Interaction Layer
- Commands System
- Terminal I/O
- Git Repository Management
- File Watching
- Linting
- Chat History & Summarization
- Main Entry Point & Bootstrap
- Supporting Modules
- Cross-Cutting Patterns
System Overview
Section titled “System Overview”Aider follows a classical MVC pattern:
- Model: LLM abstraction (
models.py), git repo state (repo.py), file content tracking. - View:
InputOutputclass inio.py— terminal I/O with Rich formatting, prompt_toolkit completions. - Controller:
Commands(commands.py) for slash commands,Codersubclasses (coders/) for edit strategies.
Key architectural decisions:
- Strategy pattern for edit formats (14+ registered coder subclasses).
- Exception-based control flow for model/format switching (
SwitchCoder). - Lazy LLM import to keep startup fast (~1.5s saved by deferring
litellm). - Background thread file watching with AI-comment detection.
- Multi-layer caching (disk-backed tags, in-memory maps, git tree file lists).
RepoMap & Context Selection
Section titled “RepoMap & Context Selection”What This Subsystem Is (aider/repomap.py)
Section titled “What This Subsystem Is (aider/repomap.py)”Aider’s repo map is a token-budgeted structural synopsis of files not currently in-chat. It is not a plain file list. It performs:
- Symbol extraction (tree-sitter query captures per language).
- Reference/definition graph ranking (PageRank over referencer->definer edges).
- Mention-aware personalization (current prompt words + filename/identifier hints).
- Token-fit packing (binary search for a map that fits a target context budget).
- Multi-layer caching (disk-backed tags cache + in-memory map cache keyed by inputs/refresh mode).
This is the core context-efficiency mechanism that lets Aider expose whole-repo structure without blasting full file contents.
Runtime Entry Path (How RepoMap Gets Into Prompts)
Section titled “Runtime Entry Path (How RepoMap Gets Into Prompts)”1) Main CLI path (aider/main.py)
Section titled “1) Main CLI path (aider/main.py)”- Aider constructs the coder object with repo map knobs (
map_tokens,map_refresh,map_multiplier_no_files). - If
--cache-promptsis enabled and refresh isauto, it rewrites refresh mode tofilesto favor stable cached prompts. --show-repo-mapcallscoder.get_repo_map()directly and prints the generated map.
2) Coder initialization (aider/coders/base_coder.py)
Section titled “2) Coder initialization (aider/coders/base_coder.py)”RepoMap(...)is instantiated only when all are true:- repo map is enabled (
use_repo_map), - running inside a repo,
- prompt template supports repo map prefix (
gpt_prompts.repo_content_prefix).
- repo map is enabled (
- Constructor wiring passes model/token context and policy:
map_tokens,root,main_model,io, prompt prefix, verbosity,max_input_tokens,map_mul_no_files,map_refresh.
3) Per-turn map request (BaseCoder.get_repo_map)
Section titled “3) Per-turn map request (BaseCoder.get_repo_map)”Flow:
- Build hint sets from current user text:
- filename mentions from
get_file_mentions(...). - identifier mentions from regex split (
get_ident_mentions). - filename stem matches from identifier matches (
get_ident_filename_matches).
- filename mentions from
- Partition files:
chat_files= editable in-chat files + repo files added as read-only.other_files= all repo files minuschat_files.
- Ask
RepoMap.get_repo_map(chat_files, other_files, mentioned_fnames, mentioned_idents, force_refresh). - Fallback strategy if map returns empty:
- try global map with no chat files but same mention hints,
- then try completely unhinted global map.
4) Prompt embedding (BaseCoder.get_repo_messages)
Section titled “4) Prompt embedding (BaseCoder.get_repo_messages)”- Repo map is injected as a user message plus a fixed assistant acknowledgment:
- user: repo map content
- assistant: “Ok, I won’t try and edit those files without asking first.”
- This keeps model behavior aligned: map files are context, not implicit edit targets.
RepoMap Core Pipeline (aider/repomap.py)
Section titled “RepoMap Core Pipeline (aider/repomap.py)”A) Initialization and cache setup
Section titled “A) Initialization and cache setup”- Cache version is tied to parser backend (
USING_TSL_PACK):- v3 normally, v4 with tree-sitter-language-pack.
- Tags cache directory:
.aider.tags.cache.v{CACHE_VERSION}in repo root. - In-memory runtime caches:
tree_cache: rendered snippets keyed by(rel_fname, lines_of_interest, mtime).tree_context_cache: parsedTreeContextkeyed by filename + mtime.map_cache: final map strings keyed by file sets and options.
B) Top-level API: get_repo_map(...)
Section titled “B) Top-level API: get_repo_map(...)”- Early outs: disabled map budget or empty
other_files. - Dynamic budget expansion when no files are in chat:
- computes enlarged target from
max_map_tokens * map_mul_no_files, - clamps against model context window minus 4096 safety padding,
- uses enlarged budget only in no-chat scenario.
- computes enlarged target from
- Calls
get_ranked_tags_map(...)and prepends optional prompt prefix. - Uses sampled token counting for large strings (
token_count):- exact count for short text,
- line-sampling estimate for long text to avoid repeated expensive tokenization.
C) Tag extraction stage
Section titled “C) Tag extraction stage”get_tags(...) (mtime-gated cached extraction)
Section titled “get_tags(...) (mtime-gated cached extraction)”- Uses file mtime as validity check.
- On cache hit with same mtime, returns stored tags.
- On miss, runs
get_tags_raw(...), stores{mtime, data}in disk cache. - Any SQLite/diskcache errors trigger
tags_cache_error(...):- attempts cache directory rebuild,
- if rebuild fails, degrades to in-memory dict cache.
get_tags_raw(...) (tree-sitter + fallback lexer)
Section titled “get_tags_raw(...) (tree-sitter + fallback lexer)”Flow:
- Detect language from filename (
grep_ast.filename_to_lang). - Load tree-sitter language/parser (
get_language,get_parser). - Load tags query file (
get_scm_fname(lang)), preferringtree-sitter-language-packand falling back totree-sitter-languages. - Parse file code and run query captures.
- Emit
Tag(rel_fname, fname, line, name, kind)for captures:name.definition.*->kind="def"name.reference.*->kind="ref"
- If parser yields defs but no refs, backfill refs using Pygments
Token.Namelexing (line = -1).
Compatibility detail:
_run_capturessupports both old and new tree-sitter Python APIs (query.capturesvsQueryCursor(query).captures).
D) Ranking stage: get_ranked_tags(...)
Section titled “D) Ranking stage: get_ranked_tags(...)”Data structures assembled
Section titled “Data structures assembled”defines[ident] -> set(defining files)references[ident] -> list(referencing files)definitions[(file, ident)] -> set(Tag defs)personalization[file] -> floatfor PageRank personalization vector.
Personalization signals
Section titled “Personalization signals”- Base per-file weight from chat file membership.
- Mentioned file paths get prioritized.
- Mentioned identifiers matching path components/basenames increase weight.
Graph construction and weighting
Section titled “Graph construction and weighting”- Graph type:
networkx.MultiDiGraph. - Self-edge fallback (
weight=0.1) added for symbols with defs but no refs. - For each
(referencer -> definer)symbol relation, edge weight combines:- symbol-level multiplier (
mul):- x10 if ident explicitly mentioned,
- x10 if likely semantic name (snake/kebab/camel and len>=8),
- x0.1 if private-like (
_prefix), - x0.1 if symbol defined in many files (>5) to de-noise broad/common names,
- chat proximity boost (x50 if referencer is in chat),
- frequency scaling (
sqrt(num_refs)) to damp high-frequency symbol spam.
- symbol-level multiplier (
PageRank and rank redistribution
Section titled “PageRank and rank redistribution”- Runs
nx.pagerank(..., weight="weight", personalization=..., dangling=...)when personalization exists. - If personalization path zero-divides, retries plain weighted PageRank.
- Node rank is redistributed over out-edges proportionally by edge weight.
- Accumulates rank into
(destination_file, ident)pairs and sorts descending. - Converts ranked pairs into ordered
Taglist, skipping files already in chat. - Ensures non-tagged files are still represented by appending
(fname,)entries.
E) Token-fit map assembly
Section titled “E) Token-fit map assembly”get_ranked_tags_map(...) cache layer
Section titled “get_ranked_tags_map(...) cache layer”- Cache key includes sorted chat/other files and token budget.
- In
autorefresh mode, key also includes mentioned filenames/identifiers. - Refresh mode behavior:
manual: returnlast_mapunless forced.always: bypass map cache every call.files: use map cache (stable for same file sets).auto: use cache only if previous uncached computation took >1s.
get_ranked_tags_map_uncached(...)
Section titled “get_ranked_tags_map_uncached(...)”Flow:
- Compute ranked tags.
- Prepend important project root files (
filter_important_filesfromaider/special.py) that were not already ranked. - Binary search over prefix length of ranked items to fit
max_map_tokens. - Render each candidate via
to_tree(...)and estimate token count. - Accept best under budget, or early-stop if within 15% of target.
This is the key throughput trick: large repos get near-optimal budget utilization without linear trial-and-error.
F) Rendering stage (to_tree + render_tree)
Section titled “F) Rendering stage (to_tree + render_tree)”- Group tags by file and render each file once.
- If file has symbol lines-of-interest, render focused context via
TreeContextwith:- no color,
- no line numbers,
- no extra child/top-of-file parent context,
- explicit LOI expansion using
add_context().
- If file has no selected tags, include filename-only entry.
- Hard truncate every output line to 100 chars to avoid minified-file explosions.
RepoMap Supporting Modules
Section titled “RepoMap Supporting Modules”aider/special.py
Section titled “aider/special.py”- Maintains root-level “important file” allowlist (
README*,LICENSE*, lockfiles, Docker/CI configs, etc.). - Repo map prepends these files so project intent/config remains visible even when symbol ranking is sparse.
aider/queries/*/*-tags.scm
Section titled “aider/queries/*/*-tags.scm”- Language-specific capture patterns for
name.definition.*andname.reference.*. - Dual query trees support both parser backends.
tests/basic/test_repomap.py
Section titled “tests/basic/test_repomap.py”Key invariants covered:
- map contains all files in simple fixture repos,
- map excludes files already added to chat,
- refresh modes (
files,auto, force refresh) behave correctly, - multi-language symbol extraction works across broad fixture matrix,
- sample-code-base output is deterministic against golden file.
RepoMap Pitfalls and Hard Lessons
Section titled “RepoMap Pitfalls and Hard Lessons”- Tree-sitter API churn required compatibility shim for query captures.
- Disk cache can fail (SQLite/path/permissions); robust fallback to memory cache is mandatory.
- Definition-only grammars need lexical fallback refs (Pygments) or ranking quality collapses.
- Without damping (
sqrt(num_refs)) and heuristic multipliers, noisy high-frequency identifiers dominate graph rank. - Token-fit via binary search is materially better than fixed top-N slicing in heterogeneous repos.
- Refresh policy materially changes UX/cost:
manual/filesimprove stability;alwaysimproves freshness at higher cost.
Coders Subsystem
Section titled “Coders Subsystem”Architecture (aider/coders/)
Section titled “Architecture (aider/coders/)”The coders subsystem implements the Strategy pattern for edit formats. Each coder type defines how LLM responses are parsed into file edits and what prompt templates instruct the model.
Class Hierarchy
Section titled “Class Hierarchy”Coder (base_coder.py:88) — Abstract base, ~2400 lines├─ EditBlockCoder (editblock_coder.py:18) — edit_format="diff"│ ├─ EditBlockFencedCoder (editblock_fenced_coder.py:9) — edit_format="diff-fenced"│ └─ EditorEditBlockCoder (editor_editblock_coder.py:7) — edit_format="editor-diff"├─ WholeFileCoder (wholefile_coder.py:13) — edit_format="whole"│ └─ EditorWholeFileCoder (editor_whole_coder.py:7) — edit_format="editor-whole"├─ UnifiedDiffCoder (udiff_coder.py:49) — edit_format="udiff"│ └─ UnifiedDiffSimpleCoder (udiff_simple.py:12) — edit_format="udiff-simple"├─ EditorDiffFencedCoder (editor_diff_fenced_coder.py:8) — edit_format="editor-diff-fenced"├─ PatchCoder (patch_coder.py:217) — edit_format="patch"├─ SingleWholeFileFuncCoder (single_wholefile_func_coder.py:9) — edit_format="func"├─ AskCoder (ask_coder.py:8) — edit_format="ask" (read-only, no edits)├─ HelpCoder (help_coder.py:9) — edit_format="help"├─ ArchitectCoder (architect_coder.py:7) — edit_format="architect" (two-stage: plan then implement)└─ ContextCoder (context_coder.py:8) — edit_format="context" (context-only, no edits)Each coder has a paired *_prompts.py file containing the system/user prompt templates.
Factory: Coder.create() (base_coder.py:128-200)
Section titled “Factory: Coder.create() (base_coder.py:128-200)”- Resolves
edit_formatfrom: explicit arg >from_coder.edit_format>main_model.edit_format. - Iterates all registered coder classes in
aider.coders.__all__, matching onedit_formatattribute. - Raises
UnknownEditFormatif no match. from_coderparameter transfers state (chat history, file list, repo) between coder instances.
Base Coder Core Attributes (base_coder.py)
Section titled “Base Coder Core Attributes (base_coder.py)”abs_fnames = set() # Editable files (absolute paths)abs_read_only_fnames = set() # Context-only filesrepo = None # GitRepo instancemain_model = None # Primary LLMweak_model = None # Summarization/commit LLMcur_messages = [] # Current turn messagesdone_messages = [] # Completed turn historypartial_response_content = "" # Streaming accumulatormulti_response_content = "" # Multi-response accumulatoredit_format = None # Subclass sets thisgpt_prompts = None # Prompt template objectstream = True # Streaming responsesauto_lint = True # Lint after editsauto_test = False # Test after editsauto_commits = True # Git commit after editsdirty_commits = True # Commit dirty files before editsMain Loop: run(with_message=None) (base_coder.py)
Section titled “Main Loop: run(with_message=None) (base_coder.py)”The main conversation loop:
- Get user input (or use
with_messagefor programmatic invocation). - Check for
/commands — dispatch toCommands. - Build full message context via
format_chat_chunks(). - Call
send_message(inp)which: a. Formats all chat chunks into a message list. b. Callssend(messages, model, functions)to hit the LLM. c. Processes the streaming/non-streaming response. d. Callsapply_updates()to parse and apply edits. - Post-edit operations: auto-lint, auto-test, auto-commit.
- Handle reflected messages (error feedback to LLM for retry).
Message Construction: format_chat_chunks() (base_coder.py:1226)
Section titled “Message Construction: format_chat_chunks() (base_coder.py:1226)”Builds the full prompt in sections:
- System prompt:
gpt_prompts.main_systemwith optionalsystem_prompt_prefixfrom model. - Example messages: Optional few-shot examples (
gpt_prompts.example_messages). - Done messages: Summarized history of previous turns.
- Repo map: Injected via
get_repo_messages(). - Read-only file content: Files in
abs_read_only_fnames. - Editable file content: Files in
abs_fnames. - System reminder: Appended to last message or as separate system message depending on
reminderpolicy (sys,user, or disabled). - Current user message: The actual user input.
Edit Application Pipeline: apply_updates() (base_coder.py:2296)
Section titled “Edit Application Pipeline: apply_updates() (base_coder.py:2296)”Flow:
get_edits()— subclass-specific parsing of LLM response into edit tuples.apply_edits_dry_run(edits)— validates edits can be applied.prepare_to_edit(edits)— checks permissions, dirty-commits files before editing.apply_edits(edits)— writes changes to disk.- On
ValueError(malformed response): incrementsnum_malformed_responses, setsreflected_messagefor retry.
File Permission: allowed_to_edit(path) (base_coder.py:2191)
Section titled “File Permission: allowed_to_edit(path) (base_coder.py:2191)”- If file is in
abs_fnames: allowed (check for dirty commit first). - If file is git-ignored: skip with warning.
- If file doesn’t exist: prompt user to create.
- If file exists but not in chat: prompt user to add.
- Handles
need_to_addfor untracked files viarepo.git.add().
EditBlock Coder Detail (editblock_coder.py + search_replace.py)
Section titled “EditBlock Coder Detail (editblock_coder.py + search_replace.py)”The most commonly used edit format. LLM produces search/replace blocks:
<<<<<<< filename.pyoriginal code=======replacement code>>>>>>> filename.pyParsing: find_original_update_blocks() (search_replace.py)
Section titled “Parsing: find_original_update_blocks() (search_replace.py)”- Regex-based parser for fenced edit blocks.
- Extracts filename, original text, replacement text.
- Handles edge cases: empty originals (new file content), shell commands.
Fuzzy Matching (search_replace.py)
Section titled “Fuzzy Matching (search_replace.py)”When exact match fails, applies strategies in order:
- Exact match: Direct line-by-line comparison.
- Whitespace-normalized: Ignore blank line differences.
- Indent-corrected: Add/remove leading whitespace to align.
- Edit distance: Levenshtein distance for closest match (finds most similar block in file).
This is critical because LLMs often produce slightly different whitespace or context lines than the actual file.
WholeFile Coder (wholefile_coder.py)
Section titled “WholeFile Coder (wholefile_coder.py)”Simplest format: LLM returns complete file content in a fenced code block. Parser extracts filename from fence header and replaces entire file.
Unified Diff Coder (udiff_coder.py)
Section titled “Unified Diff Coder (udiff_coder.py)”LLM produces standard unified diff format with ---/+++ headers and @@ hunks. Parser applies hunks with context matching.
Architect Coder (architect_coder.py)
Section titled “Architect Coder (architect_coder.py)”Two-stage edit process:
- Plan stage: LLM generates a plan (what to change and why).
- Implementation stage: Creates a secondary coder (EditBlock or other) to execute the plan.
Uses a separate editor model if configured (
--editor-model).
LLM Interaction Layer
Section titled “LLM Interaction Layer”Model Configuration (aider/models.py, ~1324 lines)
Section titled “Model Configuration (aider/models.py, ~1324 lines)”ModelSettings dataclass
Section titled “ModelSettings dataclass”@dataclassclass ModelSettings: name: str edit_format: str = "whole" weak_model_name: Optional[str] = None use_repo_map: bool = False reasoning_tag: Optional[str] = None # "thinking", "think", etc. streaming: bool = True use_temperature: Union[bool, float] = True cache_control: bool = False use_system_prompt: bool = True extra_params: Optional[dict] = None accepts_settings: Optional[list] = None editor_edit_format: Optional[str] = None reminder: str = "sys" # "sys", "user", or disabled examples_as_sys_msg: bool = False lazy: bool = False system_prompt_prefix: Optional[str] = NoneModel Registration (two-tier)
Section titled “Model Registration (two-tier)”- YAML settings (
aider/resources/model-settings.yml, ~900 lines): Per-model overrides loaded viaimportlib.resources. - Canonical aliases (
MODEL_ALIASESdict,models.py:87-111): User-friendly shortcuts (e.g.,"sonnet"→"claude-4-6-sonnet").
Model Metadata Resolution
Section titled “Model Metadata Resolution”- Primary: Fetch from LiteLLM’s
model_prices_and_context_window.json. - Fallback: OpenRouter API scraping for
"openrouter/"prefixed models. - Cache: 24-hour TTL at
~/.aider/caches/model_prices_and_context_window.json. - Token limits:
max_chat_history_tokens = min(max(max_input_tokens / 16, 1024), 8192).
Generic Model Settings (models.py:421-584)
Section titled “Generic Model Settings (models.py:421-584)”Auto-detected patterns by model name prefix:
o1-*: No system prompt, no temperature, no streaming.deepseek-r1/*: diff format, repo map,reasoning_tag: "think".claude-4-6-sonnet/*: diff format, acceptsthinking_tokens.qwq-32b/*: diff format,reasoning_tag: "think",use_temperature: 0.6.
Thinking/Reasoning Token Configuration (models.py:823-889)
Section titled “Thinking/Reasoning Token Configuration (models.py:823-889)”- Parses values:
8096,"8k","10.5k","0.5M". - Sets
extra_params["thinking"] = {"type": "enabled", "budget_tokens": N}. - OpenRouter format:
extra_params["extra_body"]["reasoning"]["max_tokens"].
Lazy LiteLLM Loading (aider/llm.py, 48 lines)
Section titled “Lazy LiteLLM Loading (aider/llm.py, 48 lines)”class LazyLiteLLM: _lazy_module = None def __getattr__(self, name): self._load_litellm() return getattr(self._lazy_module, name) def _load_litellm(self): if self._lazy_module is not None: return self._lazy_module = importlib.import_module("litellm") self._lazy_module.suppress_debug_info = True self._lazy_module.set_verbose = False self._lazy_module.drop_params = TrueDefers import litellm (~1.5s) until first actual LLM call.
Environment vars set:
LITELLM_MODE = "PRODUCTION"OR_SITE_URL = "https://aider.chat"OR_APP_NAME = "Aider"
API Call Layer: send_completion() (models.py:970-1022)
Section titled “API Call Layer: send_completion() (models.py:970-1022)”Request construction:
- Base kwargs:
model,stream. - Temperature: skip if
use_temperature=False; use float value if set; default to 0. - Function/tool formatting: wraps single function schema into OpenAI tools format.
- Extra parameters deep merge from
self.extra_params. - Ollama auto-context:
num_ctx = int(token_count(messages) * 1.25) + 8192. - GitHub Copilot token exchange headers if
GITHUB_COPILOT_TOKENset. - Request signature SHA1 for deduplication.
- Default timeout: 600s.
- Final call:
litellm.completion(**kwargs).
Retry Logic: simple_send_with_retries() (models.py:1024-1067)
Section titled “Retry Logic: simple_send_with_retries() (models.py:1024-1067)”- Starting delay: 125ms, doubles each retry.
- Max delay before giving up: 60s.
- Retryable errors:
APIConnectionError,RateLimitError,InternalServerError,ServiceUnavailableError, etc. - Non-retryable:
AuthenticationError,ContextWindowExceededError,NotFoundError. - Uses
LiteLLMExceptionsclass (aider/exceptions.py) withExInfo(name, retry, description)dataclass.
Message Validation (aider/sendchat.py, 62 lines)
Section titled “Message Validation (aider/sendchat.py, 62 lines)”sanity_check_messages(): validates user/assistant alternation, last message is user.ensure_alternating_roles(): auto-fixes by inserting empty opposite-role messages. Used for deepseek-reasoner, deepseek-r1, o1 models.
Streaming Response Processing (base_coder.py:1900-1972)
Section titled “Streaming Response Processing (base_coder.py:1900-1972)”- Iterates
completiongenerator chunk by chunk. - Extracts
chunk.choices[0].delta.contentanddelta.reasoning_content. - Wraps reasoning content in
<thinking-content-...>tags. - Live display via
self.mdstream.update()(Rich markdown stream) or rawsys.stdout.write(). - Raises
FinishReasonLengthiffinish_reason == "length"(context limit hit).
Token & Cost Tracking (base_coder.py:1994-2100)
Section titled “Token & Cost Tracking (base_coder.py:1994-2100)”- Uses
completion.usagewhen available (prompt_tokens, completion_tokens). - Handles Anthropic cache fields:
cache_read_input_tokens,cache_creation_input_tokens. - Handles DeepSeek cache:
prompt_cache_hit_tokens. - Falls back to manual
model.token_count()if usage not available. - Cost calculation: tries
litellm.completion_cost()first, then manualinput_cost_per_token * tokens.
Reasoning Tags (aider/reasoning_tags.py, 83 lines)
Section titled “Reasoning Tags (aider/reasoning_tags.py, 83 lines)”REASONING_TAG = "thinking-content-" + unique_hash— internal marker.remove_reasoning_content(res, tag): strips<tag>...</tag>from response. Handles unclosed tags.replace_reasoning_tags(text, tag): converts to formatted display with► THINKING/► ANSWERheaders.format_reasoning_content(content, tag): wraps reasoning in tags for injection.
Commands System
Section titled “Commands System”Architecture (aider/commands.py, ~1700 lines)
Section titled “Architecture (aider/commands.py, ~1700 lines)”Slash commands are dispatched via method reflection: every cmd_<name> method on the Commands class becomes a /name command. Underscores convert to dashes (cmd_chat_mode → /chat-mode).
SwitchCoder Exception (commands.py:30)
Section titled “SwitchCoder Exception (commands.py:30)”Custom exception carrying kwargs for creating a new coder instance:
class SwitchCoder(Exception): def __init__(self, placeholder=None, **kwargs): self.kwargs = kwargs self.placeholder = placeholderCaught in main.py event loop to create a new Coder while preserving session state.
Command Categories
Section titled “Command Categories”Model/Format Management
Section titled “Model/Format Management”cmd_model(args): Switch LLM model. RaisesSwitchCoder(main_model=Model(args)).cmd_chat_mode(args): Switch edit format (editblock, whole, udiff, etc.). RaisesSwitchCoder.cmd_editor_model(args): Switch secondary editor LLM.cmd_weak_model(args): Switch summarization/commit LLM.cmd_reasoning_effort(args): Set reasoning effort level.cmd_think_tokens(args): Set thinking token budget.
File Management
Section titled “File Management”cmd_add(args): Add files to editable scope. Supports glob patterns, git-filtered.cmd_drop(args): Remove files from editable scope.cmd_read_only(args)/cmd_read(args): Add files as read-only context.cmd_no_read(args): Remove read-only context.cmd_ls(): List tracked files and their chat status.
Code Quality
Section titled “Code Quality”cmd_lint(fnames=None): Run linter on files. Creates temporarylint_coderto auto-fix.cmd_test(test_cmd): Run tests. On failure, feeds output back to LLM for fixing.cmd_run(args): Run arbitrary shell command. Output available as context.
Git Operations
Section titled “Git Operations”cmd_commit(args): Create git commit with AI-generated or provided message.cmd_undo(): Rollback last aider commit (git checkout HEAD~1 -- files && git reset --soft HEAD~1). Validates commit was made by aider and not pushed.cmd_diff(): Show diff of current changes.
Web & Context
Section titled “Web & Context”cmd_web(args): Scrape URL and add content as context.cmd_map(): Show current repo map.cmd_map_refresh(): Force repo map refresh.cmd_tokens(): Show token usage report.
Editing
Section titled “Editing”cmd_editor(args): Open file in external editor ($VISUAL/$EDITOR).cmd_ask(args): Switch to ask mode (no edits, just discussion).cmd_code(args): Switch back to code editing mode.cmd_architect(args): Switch to architect mode (plan then implement).
Session
Section titled “Session”cmd_clear(): Clear chat history.cmd_reset(): Clear and drop all files.cmd_copy(args): Copy last assistant message to clipboard.cmd_paste(): Paste clipboard as user message.cmd_save(fname)/cmd_load(fname): Save/load commands to file.cmd_settings(): Show current settings.cmd_help(args): Show help.cmd_quit()/cmd_exit(): Exit aider.
Command Completion
Section titled “Command Completion”Each command can have completions_<cmd>() (returns list) and/or completions_raw_<cmd>() (returns path completions) methods. These feed into AutoCompleter in io.py.
Path Handling
Section titled “Path Handling”parse_quoted_filenames(args)(commands.py:1680): Regexr'"(.+?)"|(\\S+)'for quoted paths with spaces.glob_filtered_to_repo(pattern)(commands.py:765): Glob expansion filtered to git-tracked files.expand_subdir(file_path)(commands.py:1669): Recursively yields files from directories.
Terminal I/O
Section titled “Terminal I/O”Architecture (aider/io.py, ~1192 lines)
Section titled “Architecture (aider/io.py, ~1192 lines)”Manages all terminal I/O with Rich formatting, prompt_toolkit completions, and history.
AutoCompleter class (io.py:91-227)
Section titled “AutoCompleter class (io.py:91-227)”Extends prompt_toolkit.completion.Completer.
Tokenization (io.py:127-146):
- Lazy-loads on first completion call.
- Parses all chat files with Pygments lexer.
- Extracts tokens marked with
Token.Name. - Caches word/backtick-word pairs.
Completion logic (io.py:186-227):
- Tokenize files on first use.
- If starts with
/: command completion fromCommands. - Else: word completion from code tokens.
- Requires >= 3 character prefix.
- Case-insensitive partial matching.
InputOutput class (io.py:230-1192)
Section titled “InputOutput class (io.py:230-1192)”Core I/O manager.
Initialization (io.py:237-372)
Section titled “Initialization (io.py:237-372)”Key parameters:
pretty: Rich formatting (disabled ifNO_COLORenv var).yes: Auto-answer prompts.input_history_file: Persisted readline history (~/.aider_history).chat_history_file: Markdown log of all interactions.multiline_mode: Toggle Enter/Alt+Enter behavior.code_theme: Syntax highlighting theme.editingmode: VI or EMACS editing mode.
Uses prompt_toolkit.PromptSession with dumb-terminal detection fallback.
Main Input Loop: get_input() (io.py:523-734)
Section titled “Main Input Loop: get_input() (io.py:523-734)”- Ring bell if LLM response finished.
- Build prompt showing file list.
- Enter multiline loop:
{starts multiline mode.{tagstarts tagged multiline (closed withtag}).}closes multiline.
- Display user input to chat log.
- Return accumulated text.
Key Bindings (io.py:575-634)
Section titled “Key Bindings (io.py:575-634)”- Ctrl+Z: Suspend to background.
- Ctrl+Space: Insert space literally.
- Ctrl+Up/Down: History navigation.
- Ctrl+X, Ctrl+E: Open external editor.
- Enter: Submit in normal mode / newline in multiline.
- Alt+Enter: Opposite behavior.
Output Methods
Section titled “Output Methods”tool_output(text)(io.py:995-1012): Informational messages, optional bold, colored.tool_error(text)(io.py:988-990): Red error messages, increments error counter.tool_warning(text)(io.py:992-993): Orange warning messages.assistant_output(message)(io.py:1023-1041): Renders markdown with syntax highlighting via Rich.get_assistant_mdstream()(io.py:1014-1021): ReturnsMarkdownStreamfor streaming display.
Confirmation: confirm_ask() (io.py:806-925)
Section titled “Confirmation: confirm_ask() (io.py:806-925)”- Group-based batch operations (Y/N, (A)ll, (S)kip all, (D)on’t ask again).
- Persistent “Don’t ask again” per question.
- Bell rings before prompting.
File I/O
Section titled “File I/O”read_text(filename)(io.py:453-476): Safe read with encoding detection. Returns base64 for images.write_text(filename, content)(io.py:478-507): Exponential backoff (max 5 retries) for locked files. Respectsdry_run.
Chat History Logging
Section titled “Chat History Logging”append_chat_history()(io.py:1117-1136): Appends to markdown chat log with blockquote wrapping.log_llm_history()(io.py:754-765): Separate raw LLM request/response log with ISO timestamps.
Git Repository Management
Section titled “Git Repository Management”Architecture (aider/repo.py, 622 lines)
Section titled “Architecture (aider/repo.py, 622 lines)”GitPython wrapper for Aider-specific git operations.
GitRepo class (repo.py:52-622)
Section titled “GitRepo class (repo.py:52-622)”Initialization (repo.py:62-130)
Section titled “Initialization (repo.py:62-130)”- Finds git repo from provided filenames or explicit
git_dname. - Searches parent directories for
.git. - Validates all files in same repo.
- Stores attribution flags:
attribute_author,attribute_committer,attribute_co_authored_by.
Commit with Attribution (repo.py:131-318)
Section titled “Commit with Attribution (repo.py:131-318)”The commit() method handles authorship:
aider_edits=True(AI changes):- If
co-authored-by: Don’t modify author/committer (trailer sufficient). - Else: Modify both author and committer to
"You (aider)".
- If
aider_edits=False(user changes via/commit):- Never modify author.
- Always modify committer (aider is running git).
Implementation:
- Generate commit message (LLM or provided).
- Set
GIT_AUTHOR_NAMEandGIT_COMMITTER_NAMEenv vars via context manager. - Call
git commit -a -m. - Return
(commit_hash, commit_message).
Commit Message Generation (repo.py:326-373)
Section titled “Commit Message Generation (repo.py:326-373)”- Builds prompt with diffs and conversation context.
- Tries each model in order (weak model first).
- Checks token limits before sending.
- Strips quotes from LLM response.
Diff Operations
Section titled “Diff Operations”get_diffs(fnames)(repo.py:375-417):git diff HEADfor files. Handles initial commit (no HEAD) withgit diff --cached+ working dir diff.diff_commits(from, to)(repo.py:419-431):git diff from_commit to_commit.
File Tracking (repo.py:433-488)
Section titled “File Tracking (repo.py:433-488)”get_tracked_files(): Traverses HEAD commit tree for blobs. Caches per commit. Adds staged files from index. Filters withignored_file().path_in_repo(path)(repo.py:567-574): Checks tracked files set.get_dirty_files()(repo.py:580-595): Returns staged + unstaged files.
Ignore Management
Section titled “Ignore Management”refresh_aider_ignore()(repo.py:500-521): Watches.aiderignorefor changes, recompiles PathSpec. Rate-limits to 1x/second.ignored_file(fname)(repo.py:532-540): Checks both git ignore and.aiderignore. Caches results.git_ignored_file(path)(repo.py:523-530): Uses GitPythonrepo.ignored().
Error Handling
Section titled “Error Handling”ANY_GIT_ERRORtuple (repo.py:9-36): GitPython exceptions + generic errors. Used in try/except throughout.set_git_env(var, value, original)(repo.py:39-49): Context manager for temporary env var changes.
File Watching
Section titled “File Watching”Architecture (aider/watch.py, 319 lines)
Section titled “Architecture (aider/watch.py, 319 lines)”Background file monitor that detects AI comments and triggers prompts.
AI Comment Pattern (watch.py:68-71)
Section titled “AI Comment Pattern (watch.py:68-71)”ai_comment_pattern = re.compile( r"(?:#|//|--|;+) *(ai\b.*|ai\b.*|.*\bai[?!]?) *$", re.IGNORECASE)Matches: # ai, // AI, /* ai! */, ai?, etc.
FileWatcher class (watch.py:65-282)
Section titled “FileWatcher class (watch.py:65-282)”Initialization (watch.py:73-88)
Section titled “Initialization (watch.py:73-88)”- References
coderinstance for file tracking. root: directory to watch.gitignore_spec: loaded PathSpec patterns.changed_files: set of detected changes.
Default Ignore Patterns (watch.py:15-62)
Section titled “Default Ignore Patterns (watch.py:15-62)”Loaded via load_gitignores():
- Editor temp:
*~,*.swp,*.bak,.#*. - Build artifacts:
__pycache__/,.pytest_cache/,*.pyc. - IDE config:
.vscode/,.idea/,.sublime-*. - Environment:
.env,.venv/,node_modules/,vendor/. - OS:
.DS_Store,Thumbs.db. - Special:
.aider*,.git.
Uses pathspec.GitWildMatchPattern for glob semantics.
Filter Predicate (watch.py:90-119)
Section titled “Filter Predicate (watch.py:90-119)”- Check path is under root.
- Apply gitignore rules.
- Skip files > 1MB.
- Search for AI comment pattern.
- Return True if AI comments found.
Threading Model (watch.py:145-179)
Section titled “Threading Model (watch.py:145-179)”- Daemon thread running
watch_files(). - Uses
watchfileslibrary for efficient OS-level file monitoring. stop_event = threading.Event()for clean shutdown.- On change: sets
changed_files, callsio.interrupt_input(), returns to stop watching.
Processing Changes (watch.py:181-255)
Section titled “Processing Changes (watch.py:181-255)”- For each changed file: extract AI comments via
get_ai_comments(). - If file not in chat: auto-add to
coder.abs_fnames. - Detect action:
"!"(execute immediately) or"?"(ask question). - Build
TreeContextfor each file showing lines with AI comments. - Return formatted prompt using
watch_code_promptorwatch_ask_prompt.
Watch Prompts (aider/watch_prompts.py, 13 lines)
Section titled “Watch Prompts (aider/watch_prompts.py, 13 lines)”watch_code_prompt: “Find AI comments in shared files, follow their instructions, then remove the comments.”watch_ask_prompt: “/ask\nFind the AI comments below… They contain my questions…”
Linting
Section titled “Linting”Architecture (aider/linter.py, 305 lines)
Section titled “Architecture (aider/linter.py, 305 lines)”Pluggable linting using tree-sitter for syntax errors and language-specific tools.
Linter class (linter.py:21-168)
Section titled “Linter class (linter.py:21-168)”Linting Pipeline (linter.py:82-116)
Section titled “Linting Pipeline (linter.py:82-116)”- Read file content.
- Detect language from extension.
- Resolve linter: explicit cmd >
all_lint_cmd> per-language lookup >basic_lint(tree-sitter). - Execute linter (callable or shell command).
- Convert errors to
LintResult(text, lines). - Wrap with
TreeContextfor display (marks error lines with█).
Python Linting (linter.py:118-134)
Section titled “Python Linting (linter.py:118-134)”Three-stage pipeline:
basic_lint(): Tree-sitter syntax errors (traverse AST for ERROR nodes).lint_python_compile():compile(code, fname, "exec")for SyntaxError.flake8_lint(): Fatal errors only —--select=E9,F821,F823,F831,F406,F407,F701,F702,F704,F706.
Tree-Sitter Linting (linter.py:201-231)
Section titled “Tree-Sitter Linting (linter.py:201-231)”- Parse code to AST.
- Traverse tree for ERROR or missing nodes.
- Extract line numbers.
- Skips TypeScript (unreliable parser).
Integration with Commands
Section titled “Integration with Commands”/lintruns linter, shows issues, optionally createslint_coderto auto-fix.- Auto-lint after every edit if
--auto-lintenabled.
Chat History & Summarization
Section titled “Chat History & Summarization”Architecture (aider/history.py, 144 lines)
Section titled “Architecture (aider/history.py, 144 lines)”ChatSummary class (history.py:7-123)
Section titled “ChatSummary class (history.py:7-123)”Summarization Algorithm (history.py:33-96)
Section titled “Summarization Algorithm (history.py:33-96)”Adaptive splitting:
- If
total_tokens <= max_tokens: return unchanged. - If
len(messages) <= 4ordepth > 3: summarize entire history. - Else:
- Calculate
half_max_tokens = max_tokens // 2. - Find split_index keeping recent messages <= half_max_tokens.
- Ensure split ends with assistant message.
- Summarize old messages.
- Combine summary + recent messages.
- If still too big: recurse with
depth + 1.
- Calculate
Summarization Execution (history.py:98-123)
Section titled “Summarization Execution (history.py:98-123)”- Extract user + assistant messages.
- Build
"# USER"/"# ASSISTANT"sections. - Use
model.simple_send_with_retries()with summarization system prompt. - Prepend
summary_prefix(“I spoke to the user previously…”). - Wrap result in user message.
Integration
Section titled “Integration”base_coder.pymaintainscur_messages(current turn) anddone_messages(completed turns).- When
done_messagestoken count exceedsmax_chat_history_tokens, summarizer reduces them. - Multiple models tried in fallback order.
Main Entry Point & Bootstrap
Section titled “Main Entry Point & Bootstrap”Architecture (aider/main.py, ~1275 lines)
Section titled “Architecture (aider/main.py, ~1275 lines)”Bootstrap Sequence (main(), lines 451-1181)
Section titled “Bootstrap Sequence (main(), lines 451-1181)”Phase 1: Configuration Loading
Section titled “Phase 1: Configuration Loading”- Parse CLI args with
configargparse. - Config file precedence:
~/.aider.conf.yml→.aider.conf.yml(git root) →.aider.conf.yml(cwd) →--config. - Load
.envfiles (including~/.aider/oauth-keys.env). - Environment variable prefix:
AIDER_*.
Phase 2: Initialization
Section titled “Phase 2: Initialization”- Register LLM models from
.aider.model.settings.yml. - Initialize analytics (PostHog or file-based).
- Setup git repo (create if missing with user confirmation).
- Create
InputOutputhandler.
Phase 3: Model Selection
Section titled “Phase 3: Model Selection”- Select main model, weak_model, editor_model.
- Validate model metadata (context window, pricing, features).
- Sanity check models via
models.sanity_check_models().
Phase 4: Component Wiring
Section titled “Phase 4: Component Wiring”- Create
GitRepoinstance. - Create
Commandsinstance. - Create
Codersubclass viaCoder.create(). - Create
FileWatcherif--watch-filesenabled.
Phase 5: Event Loop
Section titled “Phase 5: Event Loop”while True: try: coder.run() return except SwitchCoder as switch: kwargs = dict(io=io, from_coder=coder) kwargs.update(switch.kwargs) coder = Coder.create(**kwargs)Startup Optimization
Section titled “Startup Optimization”load_slow_imports(): Defers heavy imports (httpx, litellm, networkx) to background thread.is_first_run_of_new_version(): Checks~/.aider/installs.jsonto skip deferred loading on first run.
Git Setup Helpers
Section titled “Git Setup Helpers”setup_git(git_root, io): Creates repo with dummyuser.name/user.emailif needed.check_gitignore(git_root, io): Adds.aider*patterns to.gitignore.sanity_check_repo(repo, io): Validates git index version (only v1 & v2 supported).
Supporting Modules
Section titled “Supporting Modules”aider/editor.py (148 lines)
Section titled “aider/editor.py (148 lines)”External editor integration for /editor command.
- Discovery chain:
editor_override>$VISUAL>$EDITOR> platform default (vim/vi/notepad). - Workflow: write temp file →
subprocess.call(editor, shell=True)→ read back → cleanup.
aider/diffs.py (129 lines)
Section titled “aider/diffs.py (129 lines)”Streaming diff visualization.
diff_partial_update(): Shows meaningful diffs mid-stream by finding last non-deleted line.- Progress bar:
[███░░░░░░] 30%based on lines processed. - Code fence wrapping with backtick count avoidance.
aider/scrape.py (285 lines)
Section titled “aider/scrape.py (285 lines)”Web content ingestion for /web command.
- Dual-path: Playwright (JS-rendered content) → httpx (static).
- Playwright: Chromium with realistic User-Agent, 5s
networkidletimeout. - HTML cleanup: BeautifulSoup strips SVGs, images, data URIs. Keeps only
href. - Markdown conversion: pypandoc (auto-downloads pandoc) → fallback to cleaned HTML.
aider/run_cmd.py (133 lines)
Section titled “aider/run_cmd.py (133 lines)”Shell command execution.
- Unix:
pexpect.spawn()with interactive TTY, output streaming, user can type input. - Windows:
subprocess.Popen()batch mode, unbuffered output. Auto-wraps PowerShell commands. - Used by:
/run, auto-lint, auto-test.
aider/utils.py (~200 lines)
Section titled “aider/utils.py (~200 lines)”IgnorantTemporaryDirectory: Suppresses cleanup errors on Windows.ChdirTemporaryDirectory: Auto-chdir into temp dir.GitTemporaryDirectory: Creates initialized git repo in temp dir (used heavily in tests).split_chat_history_markdown(text): Parses markdown chat log into structured messages (####= user,>= tool, unmarked = assistant). Used for--restore-chat-history.check_pip_install_extra(): Prompt user to install optional dependencies.
aider/args.py (~911 lines)
Section titled “aider/args.py (~911 lines)”CLI argument parsing via configargparse. Major groups:
- Main Model:
--model,--weak-model,--editor-model,--edit-format,--thinking-tokens. - API Keys:
--openai-api-key,--anthropic-api-key,--api-key provider=key. - Cache:
--cache-prompts,--cache-keepalive-pings. - Repomap:
--map-tokens,--map-refresh,--map-multiplier-no-files. - History:
--chat-history-file,--input-history-file,--restore-chat-history. - Output:
--dark-mode,--pretty,--stream,--code-theme. - Git:
--auto-commits,--dirty-commits,--watch-files,--attribute-author. - Lint/Test:
--auto-lint,--lint-cmd,--auto-test,--test-cmd.
aider/prompts.py (61 lines)
Section titled “aider/prompts.py (61 lines)”Minimal prompt templates:
commit_system: System prompt for commit message generation.undo_command_reply: Message for reverting changes.added_files: Template for added file notification.run_output: Template for command output context.summarize: System prompt for chat history summarization.summary_prefix: Prefix for continued conversations.
Model-specific prompts live in coders/*_prompts.py files.
aider/exceptions.py (109 lines)
Section titled “aider/exceptions.py (109 lines)”LiteLLM exception handling with retry classification.
ExInfo(name, retry, description)dataclass.- Retryable:
APIConnectionError,RateLimitError,InternalServerError,ServiceUnavailableError, etc. - Non-retryable:
AuthenticationError,ContextWindowExceededError,NotFoundError. - Provider-specific detection: boto3 missing, OpenRouter down, insufficient credits.
aider/special.py
Section titled “aider/special.py”- Important file allowlist:
README*,LICENSE*, lockfiles, Docker/CI configs. filter_important_files(): Returns subset of files matching the allowlist.
Cross-Cutting Patterns
Section titled “Cross-Cutting Patterns”1. Strategy Pattern (Edit Formats)
Section titled “1. Strategy Pattern (Edit Formats)”Each *_coder.py + *_prompts.py pair forms a self-contained strategy. Adding a new format means: subclass Coder, set edit_format, implement get_edits() + apply_edits(), create prompt templates. No registration code needed beyond __init__.py.
2. Exception-Based Control Flow (SwitchCoder)
Section titled “2. Exception-Based Control Flow (SwitchCoder)”Model/format switching uses non-local jumps. Commands.cmd_model() raises SwitchCoder, caught in main.py event loop, which creates a new Coder while preserving all state via from_coder.
3. Method-to-Command Reflection
Section titled “3. Method-to-Command Reflection”Every cmd_<name> method on Commands becomes a slash command. Completions via completions_<cmd>() / completions_raw_<cmd>(). No manual registration.
4. Lazy Initialization
Section titled “4. Lazy Initialization”LazyLiteLLM: defers heavy import until first API call.AutoCompleter: tokenizes files on first completion, not on init.load_slow_imports(): background thread for httpx/networkx.
5. Multi-Layer Caching
Section titled “5. Multi-Layer Caching”- Tags: disk-backed SQLite + in-memory fallback.
- Map: in-memory keyed by file sets + budget + refresh mode.
- Git tree: cached per commit hash.
- Ignore files: cached per file path.
6. Fallback Chains
Section titled “6. Fallback Chains”- Playwright → httpx (web scraping).
- pexpect → subprocess (shell commands).
- Exact match → whitespace-normalized → indent-corrected → edit distance (code matching).
- LiteLLM cost API → manual token calculation (cost tracking).
- Tree-sitter → Pygments lexer (reference extraction).
7. Git Env Variable Isolation
Section titled “7. Git Env Variable Isolation”Context managers (set_git_env()) handle GIT_AUTHOR_NAME and GIT_COMMITTER_NAME without side effects.
8. Incremental Summarization
Section titled “8. Incremental Summarization”Adaptive recursive strategy. Recent messages kept verbatim, older batches summarized by LLM. Recurses if still over budget. Multiple model fallback.
OpenOxide Blueprint (Rust Implementation Plan)
Section titled “OpenOxide Blueprint (Rust Implementation Plan)”Module Map
Section titled “Module Map”crate::context::repomap:RepoMapService,TagExtractor,Ranker,MapPacker,MapRenderer.crate::coders: Trait-based coder hierarchy.CoderTraitwithget_edits(),apply_edits(),format_messages(). Concrete types:EditBlockCoder,WholeFileCoder,UdiffCoder,ArchitectCoder.crate::llm: Model configuration, lazy provider loading, streaming response handling, retry logic.crate::commands: Command dispatch (macro-based or explicit match arms instead of Python’s getattr).crate::io: Terminal I/O via ratatui/crossterm. Async completions. History management.crate::git: Repository management viagit2-rs.crate::watch: File watching vianotifycrate with AI-comment detection.crate::lint: Tree-sitter syntax checking + external linter dispatch.crate::history: Chat summarization with token budgeting.
Crate Choices
Section titled “Crate Choices”- Parsing:
tree-sitter+ per-language grammar crates or dynamic bundles. - Graph ranking:
petgraph+ custom weighted PageRank. - Cache:
rusqlitewith schema{path, mtime, tags_blob}and safe in-memory fallback. - Token estimation: Provider tokenizer wrapper + sampled estimator.
- Terminal:
ratatui+crosstermfor TUI rendering. - HTTP:
reqwestwith streaming support for LLM calls. - Git:
git2-rsinstead of shelling out. - File watching:
notifycrate with debouncing. - Config:
serde+ TOML/YAML parsing. - CLI args:
clapwith derive macros.
Data Contracts
Section titled “Data Contracts”Tag { rel_path, abs_path, line, symbol, kind }wherekind in {Def, Ref}.RepoMapRequest { chat_files, other_files, mentioned_paths, mentioned_idents, force_refresh }.RepoMapResponse { text, estimated_tokens, sources_included }.EditBlock { path, original, replacement }.LlmMessage { role, content }.
Algorithm Parity Targets
Section titled “Algorithm Parity Targets”- Preserve mention-aware personalization and chat-file proximity boost.
- Preserve fallback-to-global behavior when disjoint file set yields empty map.
- Preserve binary-search budget fit and 100-char line truncation guard.
- Preserve refresh policies (
auto,always,files,manual) semantics. - Preserve fuzzy edit matching chain (exact → whitespace → indent → edit distance).
- Preserve adaptive chat summarization with recursive depth control.
- Preserve AI-comment detection pattern for file watching.
Rust-Specific Improvements
Section titled “Rust-Specific Improvements”- Persist cache with content hash + mtime to avoid false-stale on coarse timestamp filesystems.
- Parallel tag extraction over file batches with bounded concurrency (
tokio::spawn). - Precompile query sets and share parser pools per language to reduce warm-up latency.
- Add deterministic ranking tests with frozen graph fixtures to catch drift from heuristic edits.
- Use
async-traitfor coder hierarchy to enable concurrent operations. - Use
towermiddleware pattern for retry/timeout/rate-limiting on LLM calls. - Compile-time verification of edit format registration via proc macros.