chad-code 0.1.0__tar.gz → 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chad_code-1.0.0/PKG-INFO +223 -0
- chad_code-1.0.0/README.md +186 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/pyproject.toml +33 -11
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/__init__.py +1 -1
- chad_code-1.0.0/src/chad/agent.py +1934 -0
- chad_code-1.0.0/src/chad/atif.py +243 -0
- chad_code-1.0.0/src/chad/base_engine.py +163 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/bench.py +11 -2
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/cli.py +326 -74
- chad_code-1.0.0/src/chad/compaction.py +289 -0
- chad_code-1.0.0/src/chad/completion_engine.py +396 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/config.py +3 -3
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/engine.py +554 -51
- chad_code-1.0.0/src/chad/guardrails.py +1201 -0
- chad_code-1.0.0/src/chad/levers.py +352 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/mcp.py +4 -4
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/mcp_oauth.py +8 -8
- chad_code-1.0.0/src/chad/mlx_fastpath.py +370 -0
- chad_code-1.0.0/src/chad/mlx_qsdpa.py +481 -0
- chad_code-1.0.0/src/chad/profiles.py +81 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/prompt.py +60 -15
- chad_code-1.0.0/src/chad/prove.py +370 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/render.py +6 -5
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/repomap.py +14 -2
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/session.py +1 -1
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/skills.py +7 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/symbols.py +13 -1
- chad_code-1.0.0/src/chad/syntaxgate.py +603 -0
- chad_code-1.0.0/src/chad/toolcall_parse.py +201 -0
- chad_code-1.0.0/src/chad/tools.py +1916 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/tui.py +86 -23
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/validate.py +11 -0
- chad_code-1.0.0/src/chad_code.egg-info/PKG-INFO +223 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad_code.egg-info/SOURCES.txt +22 -2
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad_code.egg-info/requires.txt +8 -5
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_agent.py +1 -1
- chad_code-1.0.0/tests/test_agent_e2e.py +796 -0
- chad_code-1.0.0/tests/test_agent_guards.py +1004 -0
- chad_code-1.0.0/tests/test_atif.py +198 -0
- chad_code-1.0.0/tests/test_bench.py +48 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_cli.py +107 -10
- chad_code-1.0.0/tests/test_compact_notice.py +195 -0
- chad_code-1.0.0/tests/test_compaction.py +346 -0
- chad_code-1.0.0/tests/test_completion_engine.py +369 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_config.py +2 -2
- chad_code-1.0.0/tests/test_done_audit.py +408 -0
- chad_code-1.0.0/tests/test_drift_warn.py +127 -0
- chad_code-1.0.0/tests/test_edit.py +247 -0
- chad_code-1.0.0/tests/test_edit_corruption.py +154 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_engine.py +567 -14
- chad_code-1.0.0/tests/test_engine_kvquant.py +188 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_feel_pack.py +1 -1
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_gate.py +2 -2
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_ignore.py +1 -1
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_intent.py +86 -1
- chad_code-1.0.0/tests/test_lever_bite.py +638 -0
- chad_code-1.0.0/tests/test_levers.py +121 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_mcp.py +1 -1
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_mcp_oauth.py +2 -2
- chad_code-1.0.0/tests/test_mlx_fastpath.py +128 -0
- chad_code-1.0.0/tests/test_mlx_qsdpa.py +217 -0
- chad_code-1.0.0/tests/test_plan_review.py +95 -0
- chad_code-1.0.0/tests/test_prove.py +164 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_render.py +19 -2
- chad_code-1.0.0/tests/test_replace_lines.py +424 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_repomap.py +46 -1
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_session.py +1 -1
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_subagent.py +85 -5
- chad_code-1.0.0/tests/test_syntaxgate.py +166 -0
- chad_code-1.0.0/tests/test_toolcall_parse.py +230 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_tools.py +156 -6
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_tui.py +113 -6
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_validate.py +51 -0
- chad_code-0.1.0/PKG-INFO +0 -370
- chad_code-0.1.0/README.md +0 -334
- chad_code-0.1.0/src/chad/agent.py +0 -1093
- chad_code-0.1.0/src/chad/base_engine.py +0 -109
- chad_code-0.1.0/src/chad/compaction.py +0 -108
- chad_code-0.1.0/src/chad/guardrails.py +0 -373
- chad_code-0.1.0/src/chad/openai_engine.py +0 -252
- chad_code-0.1.0/src/chad/syntaxgate.py +0 -105
- chad_code-0.1.0/src/chad/toolcall_parse.py +0 -115
- chad_code-0.1.0/src/chad/tools.py +0 -962
- chad_code-0.1.0/src/chad_code.egg-info/PKG-INFO +0 -370
- chad_code-0.1.0/tests/test_agent_e2e.py +0 -184
- chad_code-0.1.0/tests/test_agent_guards.py +0 -478
- chad_code-0.1.0/tests/test_compaction.py +0 -189
- chad_code-0.1.0/tests/test_edit.py +0 -107
- chad_code-0.1.0/tests/test_openai_engine.py +0 -183
- chad_code-0.1.0/tests/test_syntaxgate.py +0 -98
- chad_code-0.1.0/tests/test_toolcall_parse.py +0 -127
- {chad_code-0.1.0 → chad_code-1.0.0}/LICENSE +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/setup.cfg +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/diag.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/ignore.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad/lsp.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad_code.egg-info/dependency_links.txt +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad_code.egg-info/entry_points.txt +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/src/chad_code.egg-info/top_level.txt +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_confirm_preview.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_log_redaction.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_lsp.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_skills.py +0 -0
- {chad_code-0.1.0 → chad_code-1.0.0}/tests/test_symbols.py +0 -0
chad_code-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chad-code
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Local MLX-backed, Claude-Code-style coding agent (Apple Silicon, Ornith 35B/9B)
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Repository, https://github.com/nathansutton/chad
|
|
7
|
+
Project-URL: Issues, https://github.com/nathansutton/chad/issues
|
|
8
|
+
Keywords: mlx,coding-agent,apple-silicon,local-llm,tui
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Operating System :: MacOS
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: mlx<0.33,>=0.32.0; sys_platform == "darwin"
|
|
21
|
+
Requires-Dist: mlx-lm<0.32,>=0.31.3; sys_platform == "darwin"
|
|
22
|
+
Requires-Dist: transformers<5.13,>=5.0
|
|
23
|
+
Requires-Dist: jinja2>=3.1
|
|
24
|
+
Requires-Dist: numpy<2,>=1.26
|
|
25
|
+
Requires-Dist: huggingface-hub>=1.22.0
|
|
26
|
+
Requires-Dist: jedi>=0.19.2
|
|
27
|
+
Requires-Dist: prompt_toolkit>=3.0.43
|
|
28
|
+
Requires-Dist: tree-sitter>=0.26.0
|
|
29
|
+
Requires-Dist: tree-sitter-language-pack>=1.12.2
|
|
30
|
+
Requires-Dist: rustworkx>=0.15
|
|
31
|
+
Requires-Dist: mcp>=1.27
|
|
32
|
+
Provides-Extra: highlight
|
|
33
|
+
Requires-Dist: pygments>=2.17; extra == "highlight"
|
|
34
|
+
Provides-Extra: lsp
|
|
35
|
+
Requires-Dist: serena-agent>=1.5; extra == "lsp"
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
|
|
38
|
+
# chad — a local, MLX-backed coding agent
|
|
39
|
+
|
|
40
|
+
[](https://github.com/nathansutton/chad/actions/workflows/tests.yml)
|
|
41
|
+
|
|
42
|
+
<img src="docs/tbench-size-vs-score.png" width="840" alt="Terminal-Bench 2.0: accuracy vs. model size. chad + Ornith (a 35B MoE) lands on the Claude Sonnet 4.5 line, matching open models many times its size and standing alone in the laptop-class ≤40B band.">
|
|
43
|
+
|
|
44
|
+
> Claude can do anything, for anyone, anywhere. chad does one thing. 🗿
|
|
45
|
+
> *Coding under supervision.*
|
|
46
|
+
|
|
47
|
+
A single-user coding agent that runs **entirely locally on Apple Silicon** via
|
|
48
|
+
[MLX](https://github.com/ml-explore/mlx). Claude-Code-style tool use (bash, read, write,
|
|
49
|
+
edit, glob, grep), plan mode, and a full-screen TUI — driven by a local model on your
|
|
50
|
+
laptop instead of a frontier model in a datacenter. No Docker, no API key, no model picker.
|
|
51
|
+
|
|
52
|
+
## Quickstart
|
|
53
|
+
|
|
54
|
+
Apple Silicon Mac + [uv](https://docs.astral.sh/uv/). One command — no clone, no config:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uvx chad-code # runs chad anywhere — the command is still `chad`
|
|
58
|
+
uvx chad-code prove # 2-min offline smoke test: 4 tiny fix-it tasks, verified, timed 🗿
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
First run picks the right Ornith model for your RAM (9B under 32 GB, 35B at 32 GB+), asks,
|
|
62
|
+
and downloads it once (~5 GB / ~13 GB, resumable) into the shared Hugging Face cache. While
|
|
63
|
+
it downloads, `cd` into a project and think of a scoped first ask — *"fix the failing test
|
|
64
|
+
in `tests/test_x.py`"* lands; *"improve my codebase"* flails.
|
|
65
|
+
|
|
66
|
+
> The PyPI package is **`chad-code`**; bare `chad` is an unrelated squatted package. Other
|
|
67
|
+
> ways in (PATH install, bleeding-edge `main`, dev clone) are in
|
|
68
|
+
> [Installing & upgrading](#installing--upgrading).
|
|
69
|
+
|
|
70
|
+
## chad is not a baby Claude
|
|
71
|
+
|
|
72
|
+
He has some of the same moves — tool use, plan mode, a real TUI — but he's a blunter
|
|
73
|
+
instrument:
|
|
74
|
+
|
|
75
|
+
| | **Claude** | **chad** 🗿 |
|
|
76
|
+
|-----------------|-------------------------------------------------|---------------------------------|
|
|
77
|
+
| **Range** | every workflow, every person, incredible nuance | one job: code, on your machine |
|
|
78
|
+
| **Runs** | anywhere — cloud, IDE, terminal, phone | your mac. that's it. |
|
|
79
|
+
| **Brain** | a frontier model in a datacenter | Ornith model |
|
|
80
|
+
| **Disposition** | understands what you *meant* | does what you *said* |
|
|
81
|
+
| **Harness** | open-ended, anything you can imagine | plan. execute. nothing else. |
|
|
82
|
+
| **When wrong** | reasons a way out | already shipped |
|
|
83
|
+
|
|
84
|
+

|
|
85
|
+
|
|
86
|
+
> Real session, unedited (cold model load cut): a local 35B reasons through the failure,
|
|
87
|
+
> edits the file, reruns the tests, confirms green.
|
|
88
|
+
|
|
89
|
+
## Sonnet on your laptop
|
|
90
|
+
|
|
91
|
+
The exam is [Terminal-Bench](https://www.tbench.ai/leaderboard), the standard benchmark for
|
|
92
|
+
CLI coding agents. chad won't top it — the A students are frontier models in datacenters.
|
|
93
|
+
The number worth looking at is **how much capability chad wrings out of 35B parameters**:
|
|
94
|
+
on Terminal-Bench 2.1, chad + Ornith lands around the **Claude Sonnet 4.5** line, matching
|
|
95
|
+
open models many times its size and beating every open model in its own weight class by a
|
|
96
|
+
wide margin. On a laptop, *capability per parameter* is the axis you actually compete on —
|
|
97
|
+
and that's where chad + Ornith is out on the frontier alone.
|
|
98
|
+
|
|
99
|
+
> **Placeholder number** — the full verified run is still in flight (`≈50%`, k=1). The whole
|
|
100
|
+
> benchmark is **publicly reproducible from a Mac**: the exact Harbor adapter, the runner,
|
|
101
|
+
> and the recipe live in [`benchmarks/tb2/`](benchmarks/tb2/README.md). Check it, don't
|
|
102
|
+
> trust it.
|
|
103
|
+
|
|
104
|
+
## The bet: at this end of the report card, the harness beats the model
|
|
105
|
+
|
|
106
|
+
Every serious coding harness was built for a frontier model behind a datacenter API — which
|
|
107
|
+
bakes in two assumptions that are both false on a laptop: the model is an A student, and
|
|
108
|
+
prefill is somebody else's electricity. A C+ student emits tool calls with typos, quotes
|
|
109
|
+
edits it never applies, and rambles — and every token of transcript it drags around must be
|
|
110
|
+
re-read by *your* GPU at a few hundred tokens a second.
|
|
111
|
+
|
|
112
|
+
So chad's thesis isn't "run a model locally" — plenty of tools do that. It's that **for a
|
|
113
|
+
small model, harness quality is worth more than a model upgrade**, and the harness and
|
|
114
|
+
inference engine have to be designed *together*. The failure modes are all nameable: the
|
|
115
|
+
model pours its edit into the reasoning channel and the harness drops it; asks for a tool
|
|
116
|
+
the harness doesn't ship; balloons the context until cache reuse hits 0% and decode falls to
|
|
117
|
+
2 tok/s. chad handles each *inside* the harness — tool calls parsed in four dialects and
|
|
118
|
+
repaired, arguments schema-coerced with a self-repair loop, edits run through a forgiveness
|
|
119
|
+
cascade, and above all the transcript kept a **strict token-prefix of the live KV cache** so
|
|
120
|
+
prefill never re-reads what it already read. That co-design is the whole moat. The full
|
|
121
|
+
story is in [Design & internals](docs/design.md).
|
|
122
|
+
|
|
123
|
+
## Installing & upgrading
|
|
124
|
+
|
|
125
|
+
The one-line quickstart (`uvx chad-code`) is up top. The other ways in:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
uv tool install chad-code # install for good — then it's just `chad`
|
|
129
|
+
uvx --from git+https://github.com/nathansutton/chad chad # bleeding-edge main, no clone
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Or from a clone (the dev path):
|
|
133
|
+
|
|
134
|
+
```bash
|
|
135
|
+
uv sync # install deps + the `chad` entrypoint (one time)
|
|
136
|
+
uv run chad # full-screen TUI
|
|
137
|
+
uv run chad "add a --json flag to main.py and update the tests" # one-shot, headless
|
|
138
|
+
uv run chad -c # resume this directory's last conversation
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
**The model.** chad picks one for you by RAM and downloads it once into the shared Hugging
|
|
142
|
+
Face cache (`~/.cache/huggingface`, reused across every project). No picker, no flags —
|
|
143
|
+
override with `CHAD_MODEL=<repo or local dir>` if you must.
|
|
144
|
+
|
|
145
|
+
| Your Mac | Model | Footprint |
|
|
146
|
+
|---|---|---|
|
|
147
|
+
| **≥ 32 GB** | [Ornith-1.0-35B `UD-Q2_K_XL`](https://huggingface.co/nathansutton/Ornith-1.0-35B-UD-Q2_K_XL-MLX) — 35B MoE, 2-bit experts | ~13 GB resident (~16 GB with KV) |
|
|
148
|
+
| **16 / 18 / 24 GB** | [Ornith-1.0-9B `UD-Q4_K_XL`](https://huggingface.co/nathansutton/Ornith-1.0-9B-UD-Q4_K_XL-MLX) — 4-bit AWQ | ~5 GB |
|
|
149
|
+
|
|
150
|
+
The 35B's working set needs headroom a 24 GB Mac doesn't have (it SIGKILLs mid-turn), so its
|
|
151
|
+
floor is **32 GB**; 24 GB and below run the 9B. Quant names follow
|
|
152
|
+
[Unsloth's dynamic-quant convention](https://docs.unsloth.ai/) (`UD-…`).
|
|
153
|
+
|
|
154
|
+
**Upgrading** — depends on how you installed: `uv tool upgrade chad-code`; `uvx --refresh
|
|
155
|
+
chad-code`; or `git pull && uv sync` for a clone. What changed lands in
|
|
156
|
+
[`CHANGELOG.md`](CHANGELOG.md). Model weights are versioned separately — a code upgrade never
|
|
157
|
+
re-downloads the model.
|
|
158
|
+
|
|
159
|
+
**Development.** `uv sync` once, then `uv run pytest -q` — the fast unit gate loads **no
|
|
160
|
+
model weights**, runs in seconds, and is what CI runs. Throughput on your own machine:
|
|
161
|
+
`uv run chad-bench` (see [Throughput & performance](docs/benchmarks.md)). LSP-precise
|
|
162
|
+
find-references / rename need the `lsp` extra (`uv tool install 'chad-code[lsp]'`); without
|
|
163
|
+
it chad uses the tree-sitter fallback automatically.
|
|
164
|
+
|
|
165
|
+
## Interactive UX
|
|
166
|
+
|
|
167
|
+
`uv run chad` launches a full-screen terminal UI (built on prompt_toolkit):
|
|
168
|
+
|
|
169
|
+
- **shift-tab cycles permission modes** — `normal` (confirm each bash/write/edit) →
|
|
170
|
+
`auto-accept edits` → `plan mode` (read-only: investigate + propose a numbered plan) → back.
|
|
171
|
+
- **type-ahead message queue** — keep typing while the agent works; messages run in order.
|
|
172
|
+
- **ctrl-c interrupts the running turn** without killing the session.
|
|
173
|
+
- **live status line** — model, mode, context %, a state glyph + verb, elapsed seconds, and
|
|
174
|
+
**↑prefilled / ↓generated** token counts (with an advancing **%** on an unavoidable full
|
|
175
|
+
re-prefill, so it's never silent).
|
|
176
|
+
- **slash commands** — `/init`, `/skills`, `/mcp`, `/accept`, `/resume`, `/compact`,
|
|
177
|
+
`/model`, `/mode`, `/help`, `/exit`. Same set in the `--repl` line interface.
|
|
178
|
+
- **`@file` / `@dir` mentions** and **`!command` shell passthrough** — pull a file into
|
|
179
|
+
context inline, or run a shell command without invoking the model.
|
|
180
|
+
|
|
181
|
+
**Usage.** `uv run chad --help` is the source of truth:
|
|
182
|
+
|
|
183
|
+
| Flag | What it does |
|
|
184
|
+
|---|---|
|
|
185
|
+
| `-c, --continue` | resume this directory's **most recent** session (non-destructive) |
|
|
186
|
+
| `--resume` | list recent sessions, pick one by number (interactive TTY only) |
|
|
187
|
+
| `--plan` | start in read-only plan mode (investigate + propose, edits blocked) |
|
|
188
|
+
| `--yolo` | auto-approve bash/write/edit (skip confirm prompts) |
|
|
189
|
+
| `--no-think` | skip Ornith's `<think>` blocks — faster on well-scoped work |
|
|
190
|
+
| `--repl` | plain line REPL instead of the TUI |
|
|
191
|
+
|
|
192
|
+
A headless task (positional, or piped with no TTY) auto-approves mutating tools; the model
|
|
193
|
+
runs greedy (temp 0). Every conversation is persisted under `~/.chad/sessions/`, and every
|
|
194
|
+
resume forks a new branch rather than overwriting — details in
|
|
195
|
+
[Configuration](docs/configuration.md#sessions). The rarely-touched tuning knobs
|
|
196
|
+
(`CHAD_MAX_CONTEXT`, `CHAD_KV_BITS`, turn-budget/think-cap, safety opt-outs) all live in
|
|
197
|
+
environment variables, fully documented there.
|
|
198
|
+
|
|
199
|
+
## Extending chad
|
|
200
|
+
|
|
201
|
+
chad speaks the same two extension formats as Claude Code:
|
|
202
|
+
|
|
203
|
+
- **[Agent Skills](https://agentskills.io)** — drop a `SKILL.md` folder in `./.claude/skills/`
|
|
204
|
+
and chad discovers it, loading the full instructions only when a task matches.
|
|
205
|
+
- **[MCP servers](https://modelcontextprotocol.io)** — configure stdio or HTTP servers in
|
|
206
|
+
`./.mcp.json` to expose external tools (GitHub, Postgres, Linear, Slack, …) alongside
|
|
207
|
+
chad's builtins, with static-token and OAuth auth.
|
|
208
|
+
|
|
209
|
+
Both are covered in full in the [Configuration reference](docs/configuration.md).
|
|
210
|
+
|
|
211
|
+
## Documentation
|
|
212
|
+
|
|
213
|
+
- **[Design & internals](docs/design.md)** — why prefill is the bill, the persistent prefix
|
|
214
|
+
cache, the trimmable/append-only trade, and the ideas borrowed from other agents.
|
|
215
|
+
- **[Throughput & performance](docs/benchmarks.md)** — prefill / decode / warm-step numbers
|
|
216
|
+
you can reproduce with `chad-bench`.
|
|
217
|
+
- **[Terminal-Bench 2.0 reproduction](benchmarks/tb2/README.md)** — the exact Harbor adapter
|
|
218
|
+
and runner behind the chart; serve Ornith yourself and check the number.
|
|
219
|
+
- **[Configuration reference](docs/configuration.md)** — Agent Skills, MCP servers, the
|
|
220
|
+
context window, every environment variable, and the safety opt-outs.
|
|
221
|
+
- **[Troubleshooting](docs/troubleshooting.md)** — when a session rambles, loops, or slows:
|
|
222
|
+
the symptom→knob map for a small local model.
|
|
223
|
+
- **[Contributing](CONTRIBUTING.md)** — what lands easily, and what needs a conversation first.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# chad — a local, MLX-backed coding agent
|
|
2
|
+
|
|
3
|
+
[](https://github.com/nathansutton/chad/actions/workflows/tests.yml)
|
|
4
|
+
|
|
5
|
+
<img src="docs/tbench-size-vs-score.png" width="840" alt="Terminal-Bench 2.0: accuracy vs. model size. chad + Ornith (a 35B MoE) lands on the Claude Sonnet 4.5 line, matching open models many times its size and standing alone in the laptop-class ≤40B band.">
|
|
6
|
+
|
|
7
|
+
> Claude can do anything, for anyone, anywhere. chad does one thing. 🗿
|
|
8
|
+
> *Coding under supervision.*
|
|
9
|
+
|
|
10
|
+
A single-user coding agent that runs **entirely locally on Apple Silicon** via
|
|
11
|
+
[MLX](https://github.com/ml-explore/mlx). Claude-Code-style tool use (bash, read, write,
|
|
12
|
+
edit, glob, grep), plan mode, and a full-screen TUI — driven by a local model on your
|
|
13
|
+
laptop instead of a frontier model in a datacenter. No Docker, no API key, no model picker.
|
|
14
|
+
|
|
15
|
+
## Quickstart
|
|
16
|
+
|
|
17
|
+
Apple Silicon Mac + [uv](https://docs.astral.sh/uv/). One command — no clone, no config:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uvx chad-code # runs chad anywhere — the command is still `chad`
|
|
21
|
+
uvx chad-code prove # 2-min offline smoke test: 4 tiny fix-it tasks, verified, timed 🗿
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
First run picks the right Ornith model for your RAM (9B under 32 GB, 35B at 32 GB+), asks,
|
|
25
|
+
and downloads it once (~5 GB / ~13 GB, resumable) into the shared Hugging Face cache. While
|
|
26
|
+
it downloads, `cd` into a project and think of a scoped first ask — *"fix the failing test
|
|
27
|
+
in `tests/test_x.py`"* lands; *"improve my codebase"* flails.
|
|
28
|
+
|
|
29
|
+
> The PyPI package is **`chad-code`**; bare `chad` is an unrelated squatted package. Other
|
|
30
|
+
> ways in (PATH install, bleeding-edge `main`, dev clone) are in
|
|
31
|
+
> [Installing & upgrading](#installing--upgrading).
|
|
32
|
+
|
|
33
|
+
## chad is not a baby Claude
|
|
34
|
+
|
|
35
|
+
He has some of the same moves — tool use, plan mode, a real TUI — but he's a blunter
|
|
36
|
+
instrument:
|
|
37
|
+
|
|
38
|
+
| | **Claude** | **chad** 🗿 |
|
|
39
|
+
|-----------------|-------------------------------------------------|---------------------------------|
|
|
40
|
+
| **Range** | every workflow, every person, incredible nuance | one job: code, on your machine |
|
|
41
|
+
| **Runs** | anywhere — cloud, IDE, terminal, phone | your mac. that's it. |
|
|
42
|
+
| **Brain** | a frontier model in a datacenter | Ornith model |
|
|
43
|
+
| **Disposition** | understands what you *meant* | does what you *said* |
|
|
44
|
+
| **Harness** | open-ended, anything you can imagine | plan. execute. nothing else. |
|
|
45
|
+
| **When wrong** | reasons a way out | already shipped |
|
|
46
|
+
|
|
47
|
+

|
|
48
|
+
|
|
49
|
+
> Real session, unedited (cold model load cut): a local 35B reasons through the failure,
|
|
50
|
+
> edits the file, reruns the tests, confirms green.
|
|
51
|
+
|
|
52
|
+
## Sonnet on your laptop
|
|
53
|
+
|
|
54
|
+
The exam is [Terminal-Bench](https://www.tbench.ai/leaderboard), the standard benchmark for
|
|
55
|
+
CLI coding agents. chad won't top it — the A students are frontier models in datacenters.
|
|
56
|
+
The number worth looking at is **how much capability chad wrings out of 35B parameters**:
|
|
57
|
+
on Terminal-Bench 2.1, chad + Ornith lands around the **Claude Sonnet 4.5** line, matching
|
|
58
|
+
open models many times its size and beating every open model in its own weight class by a
|
|
59
|
+
wide margin. On a laptop, *capability per parameter* is the axis you actually compete on —
|
|
60
|
+
and that's where chad + Ornith is out on the frontier alone.
|
|
61
|
+
|
|
62
|
+
> **Placeholder number** — the full verified run is still in flight (`≈50%`, k=1). The whole
|
|
63
|
+
> benchmark is **publicly reproducible from a Mac**: the exact Harbor adapter, the runner,
|
|
64
|
+
> and the recipe live in [`benchmarks/tb2/`](benchmarks/tb2/README.md). Check it, don't
|
|
65
|
+
> trust it.
|
|
66
|
+
|
|
67
|
+
## The bet: at this end of the report card, the harness beats the model
|
|
68
|
+
|
|
69
|
+
Every serious coding harness was built for a frontier model behind a datacenter API — which
|
|
70
|
+
bakes in two assumptions that are both false on a laptop: the model is an A student, and
|
|
71
|
+
prefill is somebody else's electricity. A C+ student emits tool calls with typos, quotes
|
|
72
|
+
edits it never applies, and rambles — and every token of transcript it drags around must be
|
|
73
|
+
re-read by *your* GPU at a few hundred tokens a second.
|
|
74
|
+
|
|
75
|
+
So chad's thesis isn't "run a model locally" — plenty of tools do that. It's that **for a
|
|
76
|
+
small model, harness quality is worth more than a model upgrade**, and the harness and
|
|
77
|
+
inference engine have to be designed *together*. The failure modes are all nameable: the
|
|
78
|
+
model pours its edit into the reasoning channel and the harness drops it; asks for a tool
|
|
79
|
+
the harness doesn't ship; balloons the context until cache reuse hits 0% and decode falls to
|
|
80
|
+
2 tok/s. chad handles each *inside* the harness — tool calls parsed in four dialects and
|
|
81
|
+
repaired, arguments schema-coerced with a self-repair loop, edits run through a forgiveness
|
|
82
|
+
cascade, and above all the transcript kept a **strict token-prefix of the live KV cache** so
|
|
83
|
+
prefill never re-reads what it already read. That co-design is the whole moat. The full
|
|
84
|
+
story is in [Design & internals](docs/design.md).
|
|
85
|
+
|
|
86
|
+
## Installing & upgrading
|
|
87
|
+
|
|
88
|
+
The one-line quickstart (`uvx chad-code`) is up top. The other ways in:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
uv tool install chad-code # install for good — then it's just `chad`
|
|
92
|
+
uvx --from git+https://github.com/nathansutton/chad chad # bleeding-edge main, no clone
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Or from a clone (the dev path):
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
uv sync # install deps + the `chad` entrypoint (one time)
|
|
99
|
+
uv run chad # full-screen TUI
|
|
100
|
+
uv run chad "add a --json flag to main.py and update the tests" # one-shot, headless
|
|
101
|
+
uv run chad -c # resume this directory's last conversation
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**The model.** chad picks one for you by RAM and downloads it once into the shared Hugging
|
|
105
|
+
Face cache (`~/.cache/huggingface`, reused across every project). No picker, no flags —
|
|
106
|
+
override with `CHAD_MODEL=<repo or local dir>` if you must.
|
|
107
|
+
|
|
108
|
+
| Your Mac | Model | Footprint |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| **≥ 32 GB** | [Ornith-1.0-35B `UD-Q2_K_XL`](https://huggingface.co/nathansutton/Ornith-1.0-35B-UD-Q2_K_XL-MLX) — 35B MoE, 2-bit experts | ~13 GB resident (~16 GB with KV) |
|
|
111
|
+
| **16 / 18 / 24 GB** | [Ornith-1.0-9B `UD-Q4_K_XL`](https://huggingface.co/nathansutton/Ornith-1.0-9B-UD-Q4_K_XL-MLX) — 4-bit AWQ | ~5 GB |
|
|
112
|
+
|
|
113
|
+
The 35B's working set needs headroom a 24 GB Mac doesn't have (it SIGKILLs mid-turn), so its
|
|
114
|
+
floor is **32 GB**; 24 GB and below run the 9B. Quant names follow
|
|
115
|
+
[Unsloth's dynamic-quant convention](https://docs.unsloth.ai/) (`UD-…`).
|
|
116
|
+
|
|
117
|
+
**Upgrading** — depends on how you installed: `uv tool upgrade chad-code`; `uvx --refresh
|
|
118
|
+
chad-code`; or `git pull && uv sync` for a clone. What changed lands in
|
|
119
|
+
[`CHANGELOG.md`](CHANGELOG.md). Model weights are versioned separately — a code upgrade never
|
|
120
|
+
re-downloads the model.
|
|
121
|
+
|
|
122
|
+
**Development.** `uv sync` once, then `uv run pytest -q` — the fast unit gate loads **no
|
|
123
|
+
model weights**, runs in seconds, and is what CI runs. Throughput on your own machine:
|
|
124
|
+
`uv run chad-bench` (see [Throughput & performance](docs/benchmarks.md)). LSP-precise
|
|
125
|
+
find-references / rename need the `lsp` extra (`uv tool install 'chad-code[lsp]'`); without
|
|
126
|
+
it chad uses the tree-sitter fallback automatically.
|
|
127
|
+
|
|
128
|
+
## Interactive UX
|
|
129
|
+
|
|
130
|
+
`uv run chad` launches a full-screen terminal UI (built on prompt_toolkit):
|
|
131
|
+
|
|
132
|
+
- **shift-tab cycles permission modes** — `normal` (confirm each bash/write/edit) →
|
|
133
|
+
`auto-accept edits` → `plan mode` (read-only: investigate + propose a numbered plan) → back.
|
|
134
|
+
- **type-ahead message queue** — keep typing while the agent works; messages run in order.
|
|
135
|
+
- **ctrl-c interrupts the running turn** without killing the session.
|
|
136
|
+
- **live status line** — model, mode, context %, a state glyph + verb, elapsed seconds, and
|
|
137
|
+
**↑prefilled / ↓generated** token counts (with an advancing **%** on an unavoidable full
|
|
138
|
+
re-prefill, so it's never silent).
|
|
139
|
+
- **slash commands** — `/init`, `/skills`, `/mcp`, `/accept`, `/resume`, `/compact`,
|
|
140
|
+
`/model`, `/mode`, `/help`, `/exit`. Same set in the `--repl` line interface.
|
|
141
|
+
- **`@file` / `@dir` mentions** and **`!command` shell passthrough** — pull a file into
|
|
142
|
+
context inline, or run a shell command without invoking the model.
|
|
143
|
+
|
|
144
|
+
**Usage.** `uv run chad --help` is the source of truth:
|
|
145
|
+
|
|
146
|
+
| Flag | What it does |
|
|
147
|
+
|---|---|
|
|
148
|
+
| `-c, --continue` | resume this directory's **most recent** session (non-destructive) |
|
|
149
|
+
| `--resume` | list recent sessions, pick one by number (interactive TTY only) |
|
|
150
|
+
| `--plan` | start in read-only plan mode (investigate + propose, edits blocked) |
|
|
151
|
+
| `--yolo` | auto-approve bash/write/edit (skip confirm prompts) |
|
|
152
|
+
| `--no-think` | skip Ornith's `<think>` blocks — faster on well-scoped work |
|
|
153
|
+
| `--repl` | plain line REPL instead of the TUI |
|
|
154
|
+
|
|
155
|
+
A headless task (positional, or piped with no TTY) auto-approves mutating tools; the model
|
|
156
|
+
runs greedy (temp 0). Every conversation is persisted under `~/.chad/sessions/`, and every
|
|
157
|
+
resume forks a new branch rather than overwriting — details in
|
|
158
|
+
[Configuration](docs/configuration.md#sessions). The rarely-touched tuning knobs
|
|
159
|
+
(`CHAD_MAX_CONTEXT`, `CHAD_KV_BITS`, turn-budget/think-cap, safety opt-outs) all live in
|
|
160
|
+
environment variables, fully documented there.
|
|
161
|
+
|
|
162
|
+
## Extending chad
|
|
163
|
+
|
|
164
|
+
chad speaks the same two extension formats as Claude Code:
|
|
165
|
+
|
|
166
|
+
- **[Agent Skills](https://agentskills.io)** — drop a `SKILL.md` folder in `./.claude/skills/`
|
|
167
|
+
and chad discovers it, loading the full instructions only when a task matches.
|
|
168
|
+
- **[MCP servers](https://modelcontextprotocol.io)** — configure stdio or HTTP servers in
|
|
169
|
+
`./.mcp.json` to expose external tools (GitHub, Postgres, Linear, Slack, …) alongside
|
|
170
|
+
chad's builtins, with static-token and OAuth auth.
|
|
171
|
+
|
|
172
|
+
Both are covered in full in the [Configuration reference](docs/configuration.md).
|
|
173
|
+
|
|
174
|
+
## Documentation
|
|
175
|
+
|
|
176
|
+
- **[Design & internals](docs/design.md)** — why prefill is the bill, the persistent prefix
|
|
177
|
+
cache, the trimmable/append-only trade, and the ideas borrowed from other agents.
|
|
178
|
+
- **[Throughput & performance](docs/benchmarks.md)** — prefill / decode / warm-step numbers
|
|
179
|
+
you can reproduce with `chad-bench`.
|
|
180
|
+
- **[Terminal-Bench 2.0 reproduction](benchmarks/tb2/README.md)** — the exact Harbor adapter
|
|
181
|
+
and runner behind the chart; serve Ornith yourself and check the number.
|
|
182
|
+
- **[Configuration reference](docs/configuration.md)** — Agent Skills, MCP servers, the
|
|
183
|
+
context window, every environment variable, and the safety opt-outs.
|
|
184
|
+
- **[Troubleshooting](docs/troubleshooting.md)** — when a session rambles, loops, or slows:
|
|
185
|
+
the symptom→knob map for a small local model.
|
|
186
|
+
- **[Contributing](CONTRIBUTING.md)** — what lands easily, and what needs a conversation first.
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
# Distribution name is `chad-code` (bare `chad` on PyPI is a squatted 2014 package).
|
|
3
3
|
# The IMPORT package (src/chad/) and the `chad` command are unchanged — dist name,
|
|
4
4
|
# import name, and command name are independent. `uvx chad-code` runs the alias
|
|
5
|
-
# script added under [project.scripts].
|
|
5
|
+
# script added under [project.scripts].
|
|
6
6
|
name = "chad-code"
|
|
7
|
-
version = "
|
|
7
|
+
version = "1.0.0"
|
|
8
8
|
description = "Local MLX-backed, Claude-Code-style coding agent (Apple Silicon, Ornith 35B/9B)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
@@ -30,19 +30,31 @@ dependencies = [
|
|
|
30
30
|
# loader (expert-weight stacking + vision-tower strip). A minor bump could refactor it
|
|
31
31
|
# and silently break the `uvx --from git+…` install path, which resolves fresh from
|
|
32
32
|
# PyPI and ignores uv.lock. Stay on the known-good 0.31 line; bump deliberately.
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
# Apple-only: no CPU/CUDA build exists. Platform-gated so `pip install chad-code`
|
|
34
|
+
# succeeds on Linux (e.g. a benchmark container) for the remote `--backend llama`
|
|
35
|
+
# path, which loads no MLX. On macOS the default in-process engine installs it as before.
|
|
36
|
+
# mlx 0.32.0: Metal command-buffer failures (incl. OOM) become catchable Python
|
|
37
|
+
# exceptions instead of a SIGABRT (ml-explore/mlx#3523) — engine._prefill/generate
|
|
38
|
+
# lean on that for the 35B-on-24GB recovery path. Perf re-gated
|
|
39
|
+
# on the bump: prefill/decode within noise of 0.31.2 on both shipped models.
|
|
40
|
+
"mlx>=0.32.0,<0.33; sys_platform == 'darwin'",
|
|
41
|
+
"mlx-lm>=0.31.3,<0.32; sys_platform == 'darwin'",
|
|
35
42
|
# mlx-lm requires transformers>=5.0, but 5.13.0 regressed AutoTokenizer.register:
|
|
36
43
|
# it now does `key.__module__.startswith("transformers.")` with no hasattr guard, and
|
|
37
44
|
# mlx-lm's tokenizer_utils passes the *string* "NewlineTokenizer" as config_class →
|
|
38
45
|
# AttributeError at import. Cap below 5.13 until mlx-lm or transformers fixes it.
|
|
39
46
|
"transformers>=5.0,<5.13",
|
|
47
|
+
# transformers needs jinja2 to render `apply_chat_template` (chad's `_render` on every
|
|
48
|
+
# backend). It's an OPTIONAL transformers dep — present transitively on a dev Mac but
|
|
49
|
+
# absent in a clean install (e.g. chad inside a Linux benchmark container), where its
|
|
50
|
+
# absence crashes the first turn. Declare it directly.
|
|
51
|
+
"jinja2>=3.1",
|
|
40
52
|
"numpy>=1.26,<2",
|
|
41
|
-
"huggingface-hub>=0
|
|
53
|
+
"huggingface-hub>=1.22.0", # first-run model download (cli.py/engine.py); imported directly, not just transitively
|
|
42
54
|
"jedi>=0.19.2", # in-process symbolic code intelligence (read/search/edit)
|
|
43
55
|
"prompt_toolkit>=3.0.43",
|
|
44
|
-
"tree-sitter>=0.
|
|
45
|
-
"tree-sitter-language-pack>=1.
|
|
56
|
+
"tree-sitter>=0.26.0",
|
|
57
|
+
"tree-sitter-language-pack>=1.12.2",
|
|
46
58
|
# repomap's personalized PageRank. Native and battle-tested where the previous
|
|
47
59
|
# hand-rolled power iteration was ours to maintain; single self-contained Rust
|
|
48
60
|
# wheel, zero transitive deps. Benchmarked vs scipy/networkx/igraph on a real
|
|
@@ -54,7 +66,7 @@ dependencies = [
|
|
|
54
66
|
"mcp>=1.27",
|
|
55
67
|
]
|
|
56
68
|
|
|
57
|
-
# Optional syntax highlighting in diffs / confirm previews
|
|
69
|
+
# Optional syntax highlighting in diffs / confirm previews. Pure-Python,
|
|
58
70
|
# import-guarded in render.py: absent, output is byte-identical to the un-highlighted path.
|
|
59
71
|
# Install with `uv sync --extra highlight`.
|
|
60
72
|
# NOTE: no PyPI release exists; 'pip install chad' installs an unrelated squatted
|
|
@@ -86,7 +98,7 @@ Issues = "https://github.com/nathansutton/chad/issues"
|
|
|
86
98
|
dev = ["pytest>=8", "ruff>=0.6", "mypy>=1.11", "types-pyyaml>=6"]
|
|
87
99
|
|
|
88
100
|
[build-system]
|
|
89
|
-
requires = ["setuptools>=
|
|
101
|
+
requires = ["setuptools>=83.0.0"]
|
|
90
102
|
build-backend = "setuptools.build_meta"
|
|
91
103
|
|
|
92
104
|
# Standard src layout: the importable package lives at src/chad/. setuptools
|
|
@@ -97,8 +109,9 @@ where = ["src"]
|
|
|
97
109
|
|
|
98
110
|
[tool.pytest.ini_options]
|
|
99
111
|
testpaths = ["tests"]
|
|
100
|
-
#
|
|
101
|
-
|
|
112
|
+
# No "-q" here: CI/docs/agents all invoke `pytest -q` explicitly, and stacking the two
|
|
113
|
+
# (verbosity -2) suppresses the final "N passed" summary line entirely — an agent
|
|
114
|
+
# running the gate then gets output with no verdict in it.
|
|
102
115
|
|
|
103
116
|
[tool.ruff]
|
|
104
117
|
line-length = 100
|
|
@@ -128,3 +141,12 @@ implicit_optional = true # `x: T = None` defaults are idiomatic throughou
|
|
|
128
141
|
disable_error_code = ["var-annotated"]
|
|
129
142
|
# Tighten (disallow_untyped_defs, re-enable var-annotated, add B/UP/SIM in ruff) in a
|
|
130
143
|
# follow-up once this baseline holds.
|
|
144
|
+
|
|
145
|
+
[[tool.mypy.overrides]]
|
|
146
|
+
# mlx_fastpath patches mlx_lm classes that only exist on macOS: with mlx installed
|
|
147
|
+
# (dev Macs, the macos CI jobs) its `type: ignore[method-assign]` comments are load-
|
|
148
|
+
# bearing; on the Linux lint runner mlx_lm is absent, the patched classes type as Any,
|
|
149
|
+
# and warn_unused_ignores flags those same comments — unfixable in the code for both
|
|
150
|
+
# platforms at once, so the unused-ignore warning alone is relaxed for this module.
|
|
151
|
+
module = "chad.mlx_fastpath"
|
|
152
|
+
warn_unused_ignores = false
|