# Aider Local AI Coding Assistant: 12 Steps, 75 Min [2026]

> Source: <https://shattered.io/aider-local-ai-coding-assistant-ollama-git-2026/>
> Published: 2026-10-24 23:20:31+00:00

Aider is still the cleanest way to run an open-source AI coding assistant against a model on your own machine, but you should pin it. Reviews such as [Toolbit’s state-of-Aider write-up](https://www.toolbit.ai/blog/aider-cli-vs-github-copilot-workspace-agentic) report that the upstream default branch last took a commit on May 22, 2026, and the version people still report running is 0.86.2. That stability cuts both ways. Nothing shifts under you between sessions, and nothing gets fixed unless you fix it or switch to a fork.

Pair Aider 0.86.2 with Ollama v0.40.10 and you get a terminal agent that runs on your hardware, edits files inside a Git repository, and commits every change so you can read it, test it, or throw it away. No subscription, no account, and no code leaving the machine. Plan on about 75 minutes, most of it model download time.

Aider is one of several free agents worth knowing. The [OpenCode and Ollama setup](https://shattered.io/open-source-ai-coding-assistant-opencode-ollama-2026/) covers a different terminal agent with its own permission model. Aider’s edge is narrower and sharper: it treats Git as the interface. Each AI edit becomes a commit, each commit can be undone with one command, and your test suite acts as the referee.

Don't miss new tech stories on Google

Add Shattered once in the Google app and our stories show up in your news suggestions.

[Add now](https://www.google.com/preferences/source?hl=en&q=https%3A%2F%2Fshattered.io%2F)

## What Aider Is and Where It Stands in October 2026

Aider is an open-source, terminal-based coding assistant that works inside a Git repository and shows you diffs for every change it makes. It is model-agnostic. Aider’s own homepage says it can connect to almost any LLM, including local models, and the same page says Aider now writes about 70% of the new code in each of its own releases. Those two claims explain its appeal. You pick the brain, and the tool handles repository context, edit formats, and commits.

The homepage also shows how long ago some of the documentation was last touched. It still lists Claude 3.7 Sonnet, DeepSeek R1, and o1 as the models Aider works best with, which reads like a snapshot from a year or more ago. Treat the docs as a guide to flags and mechanics, and treat the model advice as stale.

Project health deserves an honest paragraph. The repository is online and not archived, and GitHub automation was still running on October 24, 2026, including a pull request about OpenAI-compatible API base paths. Yet multiple reviews put the last main-branch commit at May 22, 2026, and secondary coverage says the last feature release was 0.86.0 in August 2025, with 0.86.2 arriving as a point release in February 2026. Those dates come from single secondary sources, so read them as reported rather than confirmed. A community fork called cecli, formerly Aider CE, was reportedly at version 1.6.0 on September 19, 2026, and no official page confirms that figure. No announcement of a rename, retirement, or acquisition exists, and Paul Gauthier is still named as the creator and maintainer.

Aider itself costs nothing. One report lists the project as Apache-2.0 licensed, and you only pay for whatever model you attach. With a local model through Ollama, that number is zero. Ollama’s local runtime is free, MIT-licensed, and works without an account.

The contrast with hosted assistants matters for this build. GitHub Copilot is a plan-controlled hosted service, and GitHub deprecated a group of Copilot models on October 19, 2026, with more removals announced for November 16. A pinned local model cannot be switched off by a vendor calendar. For teams that value reproducible results, that alone justifies the setup time. The broader shift toward open weights is tracked in the [open-source AI bet coverage](https://shattered.io/ai-open-source-bet-86-percent-beam-mistral-2026/), which explains why local coding models keep improving.

## Prerequisites and Versions

Gather these before you start. Versions matter here because Aider is pinned and Ollama ships fast.

- **Operating system:** macOS, Linux, or Windows. The commands below use a POSIX shell. On Windows, run them in WSL2.
- **Python 3.12:** Aider’s installer uses uv to provision Python 3.12 in an isolated environment, per Aider’s own homepage. Use 3.12 for the sample project as well so the two environments match.
- **uv (latest):** installs Aider without touching your system Python.
- **Aider 0.86.2:** the newest version directly evidenced as of October 24, 2026.
- **Ollama v0.40.10:** the latest stable release on the official releases page as of October 23, 2026. A v0.40.11 release candidate was reportedly cut on October 24, so skip it for a conservative setup.
- **Git:** Git 2.56.0 is reportedly the newest source release, listed since September 28, 2026. Every command below works on Git 2.30 or newer.
- **pytest (latest):** installed inside the project virtual environment.
- **Disk and memory:** enough free disk for the model download and enough memory to hold its weights. Check the size on the model’s Ollama page before pulling.
- **Time:** about 75 minutes, dominated by the download.

You do not need a GPU in every case. Apple Silicon Macs, Linux boxes with a recent NVIDIA card, and CPU-only machines with plenty of RAM can all run Ollama. The model you choose, not the tool, sets the hardware floor.

## Choose a Local Model: Flint, Nemotron or Gemma

Pick the model before you install anything, because the model decides how long the downloads run and how good the first diffs look. Four options matter in late October 2026, and the table lists what the official pages state about each.

| Option | Design | Context | Runs where | Account needed | 
|---|---|---|---|---|
| Recursive Flint | 40B mixture-of-experts, about 2.5B active per token | 262K tokens | Local through Ollama | No | 
| NVIDIA Nemotron 3.5 Lightning | 30B parameters, 3B active | Not stated in the Ollama note | Local through Ollama | No | 
| Gemma 4 | Size not stated in the Ollama note | Not stated | Local through Ollama, MLX path on Apple Silicon | No | 
| Hosted model with your own API key | Provider’s choice | Provider’s choice | Provider’s servers | Provider account | 

Flint is the first pick for this build. Ollama’s release notes describe it as designed for repository-scale coding-agent work, including code understanding, bug tracing, implementation, and tests. Recursive Flint reportedly arrived through Ollama in v0.40.8, which makes it the newest clearly coding-focused model in the recent release results. The deeper background sits in the [Flint model write-up](https://shattered.io/recursive-flint-40b-moe-coding-model-2026/).

One caution belongs here. A mixture-of-experts model activates only a few billion parameters per token, which keeps generation fast, but all 40B parameters must still sit in memory. Active size sets speed, and total size sets the memory bill. The Ollama release notes also said macOS support came first with other GPU support to follow, and the v0.40.9 notes reportedly added Linux and Windows support. If your platform fails to load Flint, fall back to Nemotron 3.5 Lightning, which Ollama describes as built for agents that stay running, gather context, call tools, and work through multi-step tasks on your own hardware.

Apple Silicon owners have a further reason to look at Gemma 4. An Ollama release note says Gemma 4 became up to 90% faster for coding agents on Apple Silicon through multi-token prediction, measured with the Aider polyglot benchmark. That note refers to an earlier Ollama release, so expect your own numbers to differ, but it shows Aider-style workloads are what Ollama tunes for. For a wider view of open-weight options and pricing, the [best open-source LLM roundup](https://shattered.io/best-open-source-llm-october-2026-kimi-glm-deepseek/) compares five models.

Every command here uses `flint` as the Flint tag. Ollama’s library page for the model gives the exact tag and file size. If it differs, replace `flint` everywhere below with the tag that `ollama list` prints after you pull it.

## Steps 1 to 3: Install Ollama, Pull the Model, Install Aider

### Step 1: Install Ollama and confirm the version

On Linux, the official install script sets up the service. On macOS and Windows, download the installer from the Ollama site. [Ollama’s blog](https://ollama.com/blog?trk=public_post_main-feed-card-text) carries the release notes for every version listed above, which helps when a flag behaves differently than described.

```
curl -fsSL https://ollama.com/install.sh | sh
ollama --version
curl -s http://127.0.0.1:11434/api/version
```

Expected output:

```
ollama version is 0.40.10
{"version":"0.40.10"}
```

If the second number is lower than 0.40.8, upgrade before continuing. Flint arrived in v0.40.8, and older builds will not find it. Port 11434 is Ollama’s default API port. Aider talks to it directly, so the server must be reachable on that address.

Mac users on Apple Silicon get MLX-backed paths inside Ollama, and the story of Apple’s open-source MLX framework is covered in the [Muse Spark on Mac Studio report](https://shattered.io/apple-meta-muse-spark-open-mac-studio-2026/).

### Step 2: Pull the model and raise its context window

Set a shell variable so every later command uses the same tag, then pull the model. Expect a long wait.

```
export CODER_MODEL=flint
ollama pull "$CODER_MODEL"
ollama list
```

The list output shows a table with name, ID, size, and modified time. Run a smoke test next, so you know the model answers before Aider enters the picture:

```
ollama run "$CODER_MODEL" "Write a Python function that returns the n-th Fibonacci number. Code only."
```

You should see a short function within a minute on a healthy setup. If the first reply takes far longer, the model is loading into memory, and the second reply will be faster.

Context needs one deliberate decision. Ollama applies a modest default context window unless you override it, and Aider’s repo map plus a few files can overflow it silently. The model then loses the start of the conversation, and its diffs get worse without any error. Step 6 fixes this per model inside Aider. If you prefer a server-wide setting, Ollama also reads the `OLLAMA_CONTEXT_LENGTH` environment variable.

### Step 3: Install Aider 0.86.2 with uv

Install uv first if you lack it, then install Aider into its own tool environment, pinned to the version this build was written against.

```
curl -LsSf https://astral.sh/uv/install.sh | sh
uv tool install --python python3.12 [email protected]
aider --version
```

Expected output:

```
aider 0.86.2
```

Pinning matters more than usual here. With a project that has not shipped on the main branch since May, an unpinned install gives you nothing newer, while a pinned install guarantees that a future rebuild of your toolchain produces the same agent. Aider’s [documentation](https://aider.chat/docs/) lists every flag used below, and the CLI’s built-in help matches version 0.86.2.

Never install Aider with the system pip on a machine that runs other Python projects. Dependency conflicts there are the most common reason a working Aider breaks overnight.

## Steps 4 and 5: Build the Sample Repo and See Red Tests

Aider shines when the repository already has tests, so the next two steps build a small project called logtally. It parses web server access logs in the Common Log Format and prints a summary. The seed code contains a deliberate bug. Lines with a dash instead of a byte count, which servers write for responses such as 304 Not Modified, fail to parse.

### Step 4: Create the repository and the seed code

```
mkdir logtally && cd logtally
git init -b main
python3.12 -m venv .venv
.venv/bin/pip install pytest
mkdir -p logtally tests/fixtures
printf '.venv/\n__pycache__/\n.pytest_cache/\n.aider.chat.history.md\n.aider.input.history\n.aider.tags.cache.v*\n' > .gitignore
```

Create the package files. The parser holds the bug on the size pattern, which only accepts digits.

``` python
# logtally/parser.py
import re
from dataclasses import dataclass
from datetime import datetime

LINE_RE = re.compile(
    r'^(?P<ip>\S+) \S+ \S+ \[(?P<ts>[^\]]+)\] '
    r'"(?P<method>[A-Z]+) (?P<path>\S+) [^"]*" '
    r'(?P<status>\d{3}) (?P<size>\d+)'
)

@dataclass(frozen=True)
class Entry:
    ip: str
    timestamp: datetime
    method: str
    path: str
    status: int
    size: int

def parse_line(line):
    match = LINE_RE.match(line.strip())
    if match is None:
        return None
    return Entry(
        ip=match["ip"],
        timestamp=datetime.strptime(match["ts"], "%d/%b/%Y:%H:%M:%S %z"),
        method=match["method"],
        path=match["path"],
        status=int(match["status"]),
        size=int(match["size"]),
    )

def parse_file(path):
    entries, malformed = [], 0
    with open(path, encoding="utf-8") as handle:
        for line in handle:
            if not line.strip():
                continue
            entry = parse_line(line)
            if entry is None:
                malformed += 1
            else:
                entries.append(entry)
    return entries, malformed
```

The report module, the command-line entry point, and the package marker come next.

``` python
# logtally/report.py
from collections import Counter

def summarize(entries):
    classes = Counter(f"{e.status // 100}xx" for e in entries)
    return {
        "requests": len(entries),
        "bytes": sum(e.size for e in entries),
        "status_classes": dict(sorted(classes.items())),
    }
python
# logtally/cli.py
import argparse
import sys

from .parser import parse_file
from .report import summarize

def main(argv=None):
    parser = argparse.ArgumentParser(prog="logtally", description="Summarize an access log.")
    parser.add_argument("logfile")
    args = parser.parse_args(argv)

    entries, malformed = parse_file(args.logfile)
    summary = summarize(entries)

    print(f"requests:  {summary['requests']}")
    print(f"bytes:     {summary['bytes']}")
    for name, count in summary["status_classes"].items():
        print(f"{name}:       {count}")
    print(f"malformed: {malformed}")
    return 0

if __name__ == "__main__":
    sys.exit(main())
```

Add the two small helper files and the fixture log. The fixture has seven valid lines, one of them with a dash for its size, plus one junk line.

``` python
echo '__version__ = "0.1.0"' > logtally/__init__.py
printf 'import sys\n\nfrom .cli import main\n\nsys.exit(main())\n' > logtally/__main__.py
cat > tests/fixtures/sample.log <<'EOF'
203.0.113.5 - - [24/Oct/2026:10:00:01 +0000] "GET /index.html HTTP/1.1" 200 5120
203.0.113.5 - - [24/Oct/2026:10:00:02 +0000] "GET /pricing HTTP/1.1" 200 2048
198.51.100.7 - - [24/Oct/2026:10:00:05 +0000] "GET /index.html HTTP/1.1" 304 -
198.51.100.7 - - [24/Oct/2026:10:00:09 +0000] "POST /login HTTP/1.1" 302 0
192.0.2.44 - - [24/Oct/2026:10:01:13 +0000] "GET /missing HTTP/1.1" 404 512
192.0.2.44 - - [24/Oct/2026:10:01:15 +0000] "GET /index.html HTTP/1.1" 200 5120
this line is not a log entry
203.0.113.9 - - [24/Oct/2026:10:02:40 +0000] "GET /api/items HTTP/1.1" 500 128
EOF
```

Now the tests. Three describe the intended behavior, and one of them already passes.

``` python
# tests/test_parser.py
from pathlib import Path

from logtally.parser import parse_file, parse_line

FIXTURE = Path(__file__).parent / "fixtures" / "sample.log"

def test_parse_line_basic():
    line = '203.0.113.5 - - [24/Oct/2026:10:00:01 +0000] "GET /index.html HTTP/1.1" 200 5120'
    entry = parse_line(line)
    assert entry.ip == "203.0.113.5"
    assert entry.path == "/index.html"
    assert entry.status == 200
    assert entry.size == 5120

def test_parse_line_dash_size_is_zero():
    line = '198.51.100.7 - - [24/Oct/2026:10:00:05 +0000] "GET /index.html HTTP/1.1" 304 -'
    entry = parse_line(line)
    assert entry is not None
    assert entry.size == 0

def test_parse_file_counts_malformed():
    entries, malformed = parse_file(FIXTURE)
    assert len(entries) == 7
    assert malformed == 1
python
# tests/test_report.py
from pathlib import Path

from logtally.parser import parse_file
from logtally.report import summarize

FIXTURE = Path(__file__).parent / "fixtures" / "sample.log"

def test_summarize_status_classes():
    entries, _ = parse_file(FIXTURE)
    summary = summarize(entries)
    assert summary["requests"] == 7
    assert summary["status_classes"] == {"2xx": 3, "3xx": 2, "4xx": 1, "5xx": 1}
```

### Step 5: Run the tests and commit the red baseline

```
.venv/bin/python -m pytest -q
git add -A
git commit -m "Initial logtally with failing dash-size tests"
```

Use `python -m pytest` rather than the bare `pytest` command. The module form adds the project root to the import path, so `import logtally` works without packaging the project. Expected output on the seed code:

```
FAILED tests/test_parser.py::test_parse_line_dash_size_is_zero - assert None is not None
FAILED tests/test_parser.py::test_parse_file_counts_malformed - AssertionError: assert 6 == 7
FAILED tests/test_report.py::test_summarize_status_classes - assert 6 == 7
3 failed, 1 passed in 0.05s
```

Three failures, one passing test, and a clean commit. This is the starting line. A failing suite gives the model an objective target, which beats any natural-language description of what you want.

## Steps 6 and 7: Configure Aider Safely and Start the First Session

### Step 6: Put settings in your home directory, not the repo

Aider reads configuration from YAML files and command-line flags. For this build, keep model and behavior settings in files under your home directory, and keep project-specific commands in a small wrapper script that you wrote and can read. The reason is security. VulnCheck published an [advisory on Aider through 0.86.2](https://www.vulncheck.com/advisories/aider-through-0.86.2-command-execution-via-repository-aider-conf-yml-settings) describing command execution through repository-controlled `.aider.conf.yml` settings. Since 0.86.2 is the newest version in circulation, no upstream fix is confirmed. My reading of the risk follows from how Aider works. Several settings, such as a test or lint command, name shell commands. A config file inside a cloned repository can therefore make Aider run commands you never reviewed.

The rule that follows: never run Aider inside a repository you did not write or audit without first listing hidden files and reading any `.aider.conf.yml` you find.

Create the global config:

```
# ~/.aider.conf.yml
model: ollama_chat/flint
analytics-disable: true
check-update: false
show-release-notes: false
pretty: true
```

Create the model settings file that raises the context window and fixes the edit format. The model name must match the one in the config, including the `ollama_chat/` prefix.

```
# ~/.aider.model.settings.yml
- name: ollama_chat/flint
  edit_format: diff
  use_repo_map: true
  extra_params:
    num_ctx: 32768
```

A 32K window is a sensible middle. Flint supports 262K tokens, but every extra token of context grows memory use, and the repo map in a small project rarely needs more. Raise `num_ctx` only when Aider reports it is truncating.

Then add the wrapper script in the project root:

``` bash
cat > ai.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
export OLLAMA_API_BASE="${OLLAMA_API_BASE:-http://127.0.0.1:11434}"
exec aider \
  --config "$HOME/.aider.conf.yml" \
  --model-settings-file "$HOME/.aider.model.settings.yml" \
  --test-cmd ".venv/bin/python -m pytest -q" \
  "$@"
EOF
chmod +x ai.sh
git add ai.sh && git commit -m "Add Aider wrapper script"
```

Passing `--config` explicitly leaves no doubt about which file Aider loads first. Run `aider --verbose` once and read the lines about config files, so you know whether others in the repository were also picked up.

### Step 7: Run the first session and ask before editing

Start the wrapper and add only the file that holds the bug. Fewer files means a smaller prompt and sharper edits.

```
./ai.sh logtally/parser.py tests/test_parser.py
```

The startup banner should look like this. Your exact line counts will differ.

```
Aider v0.86.2
Model: ollama_chat/flint with diff edit format
Git repo: .git with 9 files
Repo-map: using 1024 tokens, auto refresh
Added logtally/parser.py to the chat.
Added tests/test_parser.py to the chat.
```

If Aider prints a warning about unknown context size or costs for the model, ignore it. Those numbers come from a built-in table that does not know a model released this month, and your settings file already supplies what matters.

Ask a question first. The `/ask` command answers without touching any file:

```
> /ask Why does the 304 line in tests/fixtures/sample.log fail to parse?
```

A good answer points to the size group in `LINE_RE`, which accepts digits only, while servers write a dash when no body was sent. If the answer blames the timestamp or the method, the model has not understood the file, and you should check your context setting before trusting any edit. This question-first habit costs ten seconds and catches a weak model before it writes code.

If you prefer an editor-based agent for this kind of work, the [Qwen Code self-hosted setup](https://shattered.io/self-hosted-ai-coding-assistant-qwen-code-2026/) walks through another free option with its own audit controls.

## Steps 8 and 9: Wire the Test Loop, Then Review and Undo

### Step 8: Let the tests referee every edit

Add `--auto-test` to the wrapper, on its own line before `"$@"`:

```
  --auto-test \
```

With that flag, Aider runs your test command after every edit and feeds failures back to the model for another attempt. Restart the session, then give the instruction:

```
> Fix the parser so a dash in the response size field is treated as zero bytes. Do not change the tests.
```

Aider proposes a search-and-replace edit, applies it, runs pytest, and commits. A healthy run prints the diff, then the test result, then a commit line. The expected edit looks like this:

```
-    r'(?P<status>\d{3}) (?P<size>\d+)'
+    r'(?P<status>\d{3}) (?P<size>\d+|-)'
...
-        size=int(match["size"]),
+        size=0 if match["size"] == "-" else int(match["size"]),
```

And the test output after the edit:

```
....                                                                     [100%]
4 passed in 0.02s
```

The instruction includes the sentence `Do not change the tests` on purpose. Weak models sometimes make a red suite green by editing the assertions. Stating the constraint, and reading the diff afterward, closes that loophole. Models vary, so your wording and the exact diff will differ from the example above, but the tests decide whether the result is acceptable.

### Step 9: Review the commit and practice rollback

Every Aider edit is a normal Git commit. Read it like any pull request:

```
git log --format='%h %an %s' -3
git show --stat HEAD
```

Expected output, with illustrative hashes and your own name:

```
7e03aa8 Your Name (aider) fix: treat '-' response size as zero bytes in parser
a1c44f0 Your Name Add Aider wrapper script
1f9d2c0 Your Name Initial logtally with failing dash-size tests
```

The (aider) suffix on the author name marks machine-written commits, which makes later audits trivial: `git log --author='(aider)'` lists everything the model touched. Inside the session, three commands cover most review needs.

| Command | What it does | 
|---|---|
| `/diff` | Shows the changes from the last message | 
| `/undo` | Reverts the last Aider commit if it has not been pushed | 
| `/run <cmd>` | Runs a shell command and offers to add its output to the chat | 
| `/test` | Runs the configured test command and offers to fix failures | 
| `/ask` ,`/architect` ,`/code` | Switch between question, planning, and editing modes | 
| `/drop <file>` | Removes a file from the chat to shrink the prompt | 

Practice the undo once while nothing depends on it. Type `/undo` and watch the fix disappear, then run `git status` to confirm the tree is clean. Ask for the fix again. Knowing that rollback works takes the fear out of letting a model edit files.

For work you want to keep isolated, create a branch before the session with `git switch -c ai/fix-dash-size`, and merge only after review. The branch costs nothing and gives you a one-command exit with `git switch main` followed by `git branch -D`.

## Steps 10 to 12: Architect Mode, Conventions, and Scripted Runs

### Step 10: Add a feature with architect mode

The bug fix is done. Now ask for something new: a `--top N` flag that prints the N most requested paths. Start a session in architect mode, which splits the work into a planning pass and an editing pass:

```
./ai.sh --architect logtally/report.py logtally/cli.py tests/test_report.py
```

Give a precise instruction:

```
> Add top_paths(entries, n=5) to report.py, returning (path, count) pairs sorted by count descending then path ascending. Add a --top option to the CLI that defaults to 5 and prints the result under a "top paths:" heading. Add a test for top_paths using the sample fixture.
```

With one local model, the planner and the editor are the same model, so architect mode mainly forces a plan before edits. If you also have a hosted key, you can assign a stronger planner and keep Flint as the editor with `--editor-model`. Either way, the instruction names function signatures, sort order, and a test. Specific prompts produce reviewable diffs, and vague ones produce surprises.

After the commit, run the tool yourself:

```
.venv/bin/python -m logtally tests/fixtures/sample.log --top 3
requests:  7
bytes:     12928
2xx:       3
3xx:       2
4xx:       1
5xx:       1
malformed: 1
top paths:
     3  /index.html
     1  /api/items
     1  /login
```

The suite should now report five passing tests. If the model forgot the sort tiebreaker, `/api/items` and `/login` may swap places, and the new test would catch it. That is exactly why the instruction asked for a test.

### Step 11: Teach it your conventions with a read-only file

Models repeat your codebase’s habits only if you state them. Create a short conventions file and load it read-only, so Aider sees it on every turn but never edits it.

```
# CONVENTIONS.md
- Python 3.12, standard library only for runtime code. pytest for tests.
- Every public function gets a test in tests/. Use the fixture in tests/fixtures/.
- Keep functions under 30 lines. No new dependencies without asking.
- Never edit existing test assertions to make a failure pass.
- Print user-facing output only inside cli.py.
```

Add the flag to the wrapper, again before `"$@"`:

```
  --read CONVENTIONS.md \
```

Keep the file under a page. Every token of it rides along in each prompt, and a local model with a 32K window feels that cost. Add a `.aiderignore` file for generated or secret paths as well, using the same syntax as `.gitignore`, so the repo map never indexes them.

The [Kilo Code local setup](https://shattered.io/kilo-code-local-setup-ollama-vs-code-2026/) uses a similar rules-file idea inside VS Code, which makes it a useful reference if your team splits between terminal and editor.

### Step 12: Run Aider unattended for a single change

Aider also works as a one-shot command. The `--message` flag sends a single instruction and exits, which fits scripts and pre-review checks.

```
git switch -c ai/docstrings
./ai.sh --message "Add one-line docstrings to every public function in logtally/." --yes-always logtally/parser.py logtally/report.py logtally/cli.py
.venv/bin/python -m pytest -q && git log --oneline -3
```

The `--yes-always` flag approves every prompt automatically, including shell commands, so use it only on a branch, in a repository you wrote, with a test suite that can fail the run. If pytest fails, the `&&` stops the chain and the branch stays unmerged. Review the diff with `git diff main...ai/docstrings` before merging.

## The Complete Project: Final Code and Tree

After steps 8 and 10, the repository holds the files below. The fixed parser changes two lines compared with the seed.

```
logtally/
  ai.sh
  CONVENTIONS.md
  .gitignore
  logtally/
    __init__.py
    __main__.py
    cli.py
    parser.py
    report.py
  tests/
    fixtures/sample.log
    test_parser.py
    test_report.py
```

The parser diff from Step 8 was shown above. The report module gains one function:

``` python
# logtally/report.py (final)
from collections import Counter

def summarize(entries):
    classes = Counter(f"{e.status // 100}xx" for e in entries)
    return {
        "requests": len(entries),
        "bytes": sum(e.size for e in entries),
        "status_classes": dict(sorted(classes.items())),
    }

def top_paths(entries, n=5):
    counts = Counter(e.path for e in entries)
    return sorted(counts.items(), key=lambda item: (-item[1], item[0]))[:n]
```

The CLI gains the option and the loop that prints it:

``` python
# logtally/cli.py (final)
import argparse
import sys

from .parser import parse_file
from .report import summarize, top_paths

def main(argv=None):
    parser = argparse.ArgumentParser(prog="logtally", description="Summarize an access log.")
    parser.add_argument("logfile")
    parser.add_argument("--top", type=int, default=5, help="show the N most requested paths")
    args = parser.parse_args(argv)

    entries, malformed = parse_file(args.logfile)
    summary = summarize(entries)

    print(f"requests:  {summary['requests']}")
    print(f"bytes:     {summary['bytes']}")
    for name, count in summary["status_classes"].items():
        print(f"{name}:       {count}")
    print(f"malformed: {malformed}")
    print("top paths:")
    for path, count in top_paths(entries, args.top):
        print(f"  {count:>4}  {path}")
    return 0

if __name__ == "__main__":
    sys.exit(main())
```

And the extra test appended to `tests/test_report.py`, with the import line changed to `from logtally.report import summarize, top_paths`:

``` python
def test_top_paths_orders_by_count_then_name():
    entries, _ = parse_file(FIXTURE)
    assert top_paths(entries, 2) == [("/index.html", 3), ("/api/items", 1)]
```

The final wrapper, with all flags from steps 6, 8, and 11, reads:

``` bash
#!/usr/bin/env bash
set -euo pipefail
export OLLAMA_API_BASE="${OLLAMA_API_BASE:-http://127.0.0.1:11434}"
exec aider \
  --config "$HOME/.aider.conf.yml" \
  --model-settings-file "$HOME/.aider.model.settings.yml" \
  --test-cmd ".venv/bin/python -m pytest -q" \
  --auto-test \
  --read CONVENTIONS.md \
  "$@"
```

Run the full suite one last time to confirm the end state:

```
.venv/bin/python -m pytest -q
.....                                                                    [100%]
5 passed in 0.02s
```

The project is deliberately small. The value is in the loop you just built: failing tests, a scoped prompt, an automatic test run, a commit, a review, and a rollback path. The same loop scales to a real codebase if you keep the chat to the files that matter.

## Local, Hosted, or Subscription: What Each Route Costs

Aider with a local model is one of several ways to get an AI coding assistant. The table below compares the routes on cost, data flow, and model control, using only prices and behavior stated on official pages.

| Route | Software cost | Where your code goes | Who controls the model | 
|---|---|---|---|
| Aider with Ollama, local model | $0 (Aider and local Ollama are free) | Stays on your machine | You, pinned by tag | 
| Aider with a hosted API key | $0 for Aider, provider bills per token | Provider’s servers | Provider, can change or retire models | 
| GitHub Copilot Pro | $10 per month, 1,000 base AI credits | GitHub-hosted models | GitHub, with scheduled deprecations | 
| GitHub Copilot Business | $19 per assigned seat per month | GitHub-hosted models | GitHub, admin-controlled policies | 

Copilot’s credit model deserves a note. GitHub says every plan includes a monthly allowance of AI credits, and one credit equals $0.01. A $10 extra budget therefore covers 1,000 additional credits. That is a metered system, so heavy agent use can cost more than the headline plan price. The [GitHub changelog for October 2026](https://github.blog/changelog/month/10-2026/page/2/) records the model deprecations that took effect on October 19.

Local inference trades money for hardware and patience. You pay once for a machine that can hold the model, and then each additional prompt costs electricity. Hosted models usually produce stronger first drafts on hard problems, and a head-to-head of current frontier models is available in the [Claude Opus 5.5 versus GPT-6 Astra comparison](https://shattered.io/claude-opus-5-5-vs-gpt-6-astra-terminal-bench-2026/). OpenAI’s GPT-6 variants are sold as ChatGPT and API services, and no official result shows them as downloadable weights, so they cannot take part in a fully local setup.

A practical split works for many teams. Use the local model for refactors, test writing, and anything touching proprietary code. Use a hosted model, through Aider’s `--model` flag with your own key, for the occasional problem the local model cannot crack. Aider makes that switch a single flag, which is the real strength of a model-agnostic client.

## Common Pitfalls That Break This Setup

Six mistakes account for most failed first runs. Check them in order before blaming the model.

1. **Leaving the context window at its default.** Aider sends a repo map and file contents, and a small default window silently drops the oldest part. Symptoms are edits that ignore instructions you gave earlier. Set`num_ctx` in the model settings file, as in Step 6, and raise it only when needed.
2. **Using the wrong model prefix.** Aider routes requests by prefix. With Ollama,`ollama_chat/<tag>` is the form that works for chat-style editing, and a bare tag or an`ollama/` prefix can produce odd formatting or failed edits. The name in your settings file must match the name in your config character for character.
3. **Pointing the OpenAI-compatible base URL at the wrong path.** If you use Ollama’s OpenAI-compatible endpoint instead of the native one, the base URL needs the`/v1` suffix, for example`http://127.0.0.1:11434/v1` , and the model name needs an`openai/` prefix. A missing suffix produces 404 errors. Aider’s own repository recorded a pull request about OpenAI-compatible API base paths on October 24, 2026, so the area is still being sorted out.
4. **Adding too many files.** Every file in the chat is sent on every turn. Add the two or three files you want changed and let the repo map cover the rest. If a session slows down, use`/drop` to shrink it.
5. **Starting with a dirty working tree.** Uncommitted changes mix with Aider’s commits and make`/undo` unreliable. Commit or stash your own work first, so the history shows only your commits and the model’s.
6. **Trusting repository config files.** A`.aider.conf.yml` in a project you did not write can name commands that Aider runs. Read hidden files before your first session in any unfamiliar repository.

A seventh habit prevents most regret: never run Aider on your main branch for exploratory work. A branch turns every mistake into a deleted branch rather than a repair job.

## Troubleshooting: 10 Failures and Their Fixes

When something breaks, match the symptom in the table first.

| Symptom | Likely cause | Fix | 
|---|---|---|
| `aider: command not found` after install | uv’s tool directory is missing from PATH | Run `uv tool update-shell` , then open a new terminal | 
| Connection refused to 127.0.0.1:11434 | Ollama service is not running | Start the Ollama app or run `ollama serve` , then retry the version check | 
| Model not found | Tag typo, or Ollama older than v0.40.8 for Flint | Run `ollama list` , correct the tag, and upgrade Ollama if needed | 
| Aider ignores your model settings | Name mismatch between config and settings file | Make both say `ollama_chat/flint` exactly, and pass`--model-settings-file` | 
| Edits fail with search/replace errors | Model cannot produce the diff format reliably | Set `edit_format: whole` in the settings file for that model | 
| Replies cut off or lose earlier instructions | Context window too small | Raise `num_ctx` , drop files, or shorten CONVENTIONS.md | 
| Very slow first token | Model loading, or weights spilling out of GPU memory | Wait for the load, use a smaller model, or lower the context | 
| Tests pass but behavior is wrong | Tests do not cover the change | Add a failing test first, then rerun the instruction | 
| Aider edits the tests to pass | Prompt did not forbid it | State that the tests must stay untouched and review the diff of `tests/` | 
| Auto-commit created a messy history | Many small commits in one session | Squash on your branch with `git reset --soft` before merging | 

Two failures need more than a one-line fix.

If the model returns garbled or empty answers, test the model alone with `ollama run` before blaming Aider. A broken download or a runtime that does not yet support your GPU shows up there first. Ollama merged a llama.cpp runtime update on October 22, 2026, and release notes said Flint’s initial platform support was macOS, so check the notes for your platform if the model loads but behaves strangely.

If a long session degrades, restart it. Aider’s chat history grows, and local models handle long histories worse than hosted ones. Commit, quit, and start a fresh session on the next task. The commits carry the state, so nothing is lost.

## Advanced Tips for Faster and Safer Sessions

Once the basic loop works, these adjustments pay off.

**Quantize the KV cache.** Ollama supports flash attention and a quantized key-value cache through environment variables such as `OLLAMA_FLASH_ATTENTION=1` and `OLLAMA_KV_CACHE_TYPE=q8_0`. Together they cut memory used by long contexts, which lets you raise `num_ctx` without buying hardware. Test quality afterward on your own tasks, since quantizing the cache can nudge output.

**Keep the model warm.** The `OLLAMA_KEEP_ALIVE` variable controls how long a model stays loaded after the last request. A longer value removes the load delay between turns during a work session, at the cost of holding memory.

**Split planner and editor.** Architect mode accepts different models for each role. Pair a stronger hosted planner with a local editor, and your private code still only reaches the hosted side as the snippets in the chat. Decide whether that exposure is acceptable before using it.

**Use watch mode.** Aider’s `--watch-files` option monitors your files for comments ending in an AI marker. You write an instruction as a comment inside your editor, save, and Aider acts on it. It suits people who live in an editor but want Aider’s Git handling.

**Wire a pre-commit check.** A Git pre-commit hook that runs your test command catches a bad commit from a model or from you. Aider respects hooks by default, so a failing hook blocks the commit rather than letting it through. If you disable hook checks to move faster, you remove a safety net.

**Run parallel sessions with worktrees.** `git worktree add ../logtally-feature ai/feature` gives a second checkout on its own branch. Two Aider sessions can then work on separate tasks without sharing a dirty tree.

**Evaluate the fork.** If you hit a bug that upstream will not fix, look at the cecli fork, which reportedly released more actively than upstream during September 2026. Pin its version, read its changelog, and rerun your tests. Moving to a fork is a normal response to an inactive upstream, but treat it as a new dependency. The wider [software and developer tools hub](https://shattered.io/category/software/) tracks other agents that may suit you better.

**Try Ollama’s launch integrations.** Ollama’s `ollama launch` command can start supported coding tools against local models, and [Codex CLI](https://shattered.io/claude-code-codex-cli-opencode-update-4-days-2026/) began listing locally installed Ollama models in its picker on October 16, 2026. Aider does not need that route, but it shows how many agents now treat Ollama as the default local backend.

## Security and Privacy Notes for a Local Coding Agent

Running locally removes one risk, which is sending code to a third party. It does not remove the others.

The VulnCheck advisory is the main one. Because the issue concerns repository-controlled configuration in Aider through 0.86.2, treat every cloned repository as untrusted until you inspect its hidden files. Keep your own settings in your home directory, as in Step 6. Prefer a wrapper script you wrote over a config file that travels with a repo. If a future release fixes the issue, upgrade deliberately, rerun your tests, and update the pin.

Secrets need explicit handling. Anything in a file you add to the chat enters the prompt. Keep `.env` files out of the chat, and list them in `.aiderignore` so the repo map skips them. Aider’s analytics are disabled by the config in this tutorial. Check the flag in your own setup, since a local model protects nothing if a tool reports usage elsewhere.

Generated code needs the same review as human code. A passing test suite shows that the code satisfies your tests, not that it is safe. Read diffs for new network calls, new file writes, and shell invocations, and run a dependency check when the model adds a package. The conventions file helps by stating that no new dependencies may be added without asking.

Version control stays the foundation. You may have seen reports that Anthropic moved one internal monorepo from Git to an in-house system called Scion after daily merges reportedly grew past 30,000. That change applies to one company’s internal repository, driven by machine-written volume. Git itself remains a free, maintained, open-source system, and it remains the safety net this whole workflow rests on.

## Frequently Asked Questions

### Is Aider still maintained in October 2026?

Aider remains online and unarchived, and its GitHub automation was active on October 24, 2026. Reviews report the last main-branch commit on May 22, 2026, and the newest version evidenced is 0.86.2. Treat it as stable but slow-moving, pin the version, and keep a fallback.

### Is Aider free?

Yes. The client has no subscription fee. You pay only for the model you connect. With a local model through Ollama, the cost is your hardware and electricity.

### Which local model should I run with Aider?

Start with Recursive Flint if your platform supports it. Ollama describes it as a 40B mixture-of-experts model with about 2.5B active parameters and a 262K-token context window, built for repository-scale work. Fall back to NVIDIA Nemotron 3.5 Lightning if Flint will not load on your hardware.

### Does my code leave my machine?

Not with a local model through Ollama. The code goes from Aider to the Ollama server on your own computer. If you add a hosted model, the chat contents go to that provider, so keep the two setups separate for sensitive repositories.

### How is Aider different from GitHub Copilot?

Copilot is a hosted, plan-controlled service with credit-based billing, and GitHub retires models on its own schedule. Aider is a Git-centered terminal client that works with any model you point it at, including pinned local ones. Copilot suits teams that want a managed product. Aider suits people who want control over the model and the history.

### How do I undo an edit I do not like?

Type `/undo` inside the session to revert the last Aider commit if it has not been pushed. Outside the session, use `git revert` for a pushed commit or `git reset --hard HEAD~1` on a private branch. Working on a branch makes all of these low-risk.

### How do I stop Aider from running commands I did not approve?

Keep your settings in your home directory and pass them with the config flag. Before the first session in any repository you did not write, list hidden files and read any Aider config inside. Run the agent on a branch, and skip the auto-approve flag outside repositories you trust.

### Can I use this with Python projects only?

No. Aider works on any language Git can track. The tutorial used Python because pytest gives a quick, clear referee. Swap in your language’s test runner in the `--test-cmd` flag, and the loop works the same way.
