chad-code 2.0.3__tar.gz → 2.2.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.
Files changed (114) hide show
  1. {chad_code-2.0.3/src/chad_code.egg-info → chad_code-2.2.0}/PKG-INFO +88 -61
  2. {chad_code-2.0.3 → chad_code-2.2.0}/README.md +85 -59
  3. {chad_code-2.0.3 → chad_code-2.2.0}/pyproject.toml +56 -3
  4. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/__init__.py +1 -1
  5. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/agent.py +197 -158
  6. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/ambient.py +69 -30
  7. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/atif.py +53 -26
  8. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/base_engine.py +8 -24
  9. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/bench.py +35 -8
  10. chad_code-2.2.0/src/chad/checkpoint.py +231 -0
  11. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/cli.py +261 -193
  12. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/completion_engine.py +29 -162
  13. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/diag.py +16 -9
  14. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/engine.py +1118 -1149
  15. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/guardrails.py +50 -69
  16. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/ignore.py +0 -5
  17. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/mcp.py +169 -131
  18. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/mcp_oauth.py +61 -44
  19. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/mlx_dflash.py +175 -34
  20. chad_code-2.2.0/src/chad/mlx_fastpath.py +793 -0
  21. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/mlx_qmm_mma.py +184 -62
  22. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/mlx_qsdpa.py +133 -106
  23. chad_code-2.2.0/src/chad/prism_pack.py +253 -0
  24. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/prompt.py +6 -7
  25. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/prove.py +67 -23
  26. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/render.py +24 -17
  27. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/repomap.py +91 -21
  28. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/seatbelt.py +132 -102
  29. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/session.py +60 -19
  30. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/skills.py +9 -11
  31. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/speech.py +44 -27
  32. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/spill.py +8 -6
  33. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/toolcall_parse.py +6 -6
  34. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/tools.py +113 -25
  35. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/tui.py +100 -12
  36. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/validate.py +58 -21
  37. {chad_code-2.0.3 → chad_code-2.2.0/src/chad_code.egg-info}/PKG-INFO +88 -61
  38. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad_code.egg-info/SOURCES.txt +3 -2
  39. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad_code.egg-info/requires.txt +1 -1
  40. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_agent_e2e.py +289 -12
  41. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_agent_guards.py +42 -23
  42. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_ambient.py +66 -45
  43. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_atif.py +33 -12
  44. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_bench.py +22 -14
  45. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_checkpoint.py +73 -4
  46. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_cli.py +165 -160
  47. chad_code-2.2.0/tests/test_cli_modes.py +235 -0
  48. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_completion_engine.py +30 -15
  49. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_config.py +23 -0
  50. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_confirm_preview.py +23 -0
  51. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_edit.py +42 -30
  52. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_engine.py +240 -98
  53. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_engine_dflash.py +288 -15
  54. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_engine_kvquant.py +1 -3
  55. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_engine_pld_hybrid.py +0 -1
  56. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_engine_pld_wide.py +32 -9
  57. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_feel_pack.py +6 -8
  58. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_gate.py +45 -43
  59. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_intent.py +80 -91
  60. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_log_redaction.py +18 -0
  61. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_matrix_bench.py +104 -1
  62. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_mcp.py +70 -64
  63. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_mcp_oauth.py +9 -10
  64. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_mlx_qmm_mma.py +75 -14
  65. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_mlx_qsdpa.py +22 -30
  66. chad_code-2.2.0/tests/test_prism_pack.py +374 -0
  67. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_prove.py +68 -1
  68. chad_code-2.2.0/tests/test_repomap.py +295 -0
  69. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_seatbelt.py +101 -82
  70. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_session.py +106 -22
  71. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_skills.py +2 -3
  72. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_speech_tui.py +165 -149
  73. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_spill.py +3 -4
  74. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_toolcall_parse.py +17 -0
  75. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_tools.py +138 -0
  76. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_tui.py +41 -0
  77. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_validate.py +36 -3
  78. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_warm_prefix_tiers.py +87 -72
  79. chad_code-2.0.3/src/chad/checkpoint.py +0 -149
  80. chad_code-2.0.3/src/chad/mlx_fastpath.py +0 -409
  81. chad_code-2.0.3/src/chad/serve.py +0 -823
  82. chad_code-2.0.3/tests/test_repomap.py +0 -198
  83. chad_code-2.0.3/tests/test_serve.py +0 -815
  84. {chad_code-2.0.3 → chad_code-2.2.0}/LICENSE +0 -0
  85. {chad_code-2.0.3 → chad_code-2.2.0}/setup.cfg +0 -0
  86. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/compaction.py +0 -0
  87. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/config.py +0 -0
  88. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/levers.py +0 -0
  89. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/LICENSE +0 -0
  90. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/__init__.py +0 -0
  91. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/alignment.py +0 -0
  92. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/attention.py +0 -0
  93. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/audio.py +0 -0
  94. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/cache.py +0 -0
  95. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/conformer.py +0 -0
  96. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/ctc.py +0 -0
  97. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/parakeet.py +0 -0
  98. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/rnnt.py +0 -0
  99. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/tokenizer.py +0 -0
  100. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/parakeet/utils.py +0 -0
  101. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad/syntaxgate.py +0 -0
  102. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad_code.egg-info/dependency_links.txt +0 -0
  103. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad_code.egg-info/entry_points.txt +0 -0
  104. {chad_code-2.0.3 → chad_code-2.2.0}/src/chad_code.egg-info/top_level.txt +0 -0
  105. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_agent.py +0 -0
  106. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_compaction.py +0 -0
  107. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_ignore.py +0 -0
  108. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_levers.py +0 -0
  109. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_mlx_fastpath.py +0 -0
  110. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_render.py +0 -0
  111. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_repomap_polyglot.py +0 -0
  112. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_speech.py +0 -0
  113. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_syntaxgate.py +0 -0
  114. {chad_code-2.0.3 → chad_code-2.2.0}/tests/test_toolcall_dialect.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chad-code
3
- Version: 2.0.3
3
+ Version: 2.2.0
4
4
  Summary: Claude-Code-style coding agent that runs entirely on a 24 GB Apple Silicon Mac (MLX, Qwen3.8-27B)
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/nathansutton/chad
@@ -13,11 +13,12 @@ Classifier: Operating System :: MacOS
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
16
17
  Classifier: Topic :: Software Development :: Code Generators
17
18
  Requires-Python: >=3.11
18
19
  Description-Content-Type: text/markdown
19
20
  License-File: LICENSE
20
- Requires-Dist: mlx==0.32.0; sys_platform == "darwin"
21
+ Requires-Dist: mlx!=0.32.1,<=0.32.2,>=0.32.0; sys_platform == "darwin"
21
22
  Requires-Dist: mlx-lm<0.32,>=0.31.3; sys_platform == "darwin"
22
23
  Requires-Dist: transformers<5.13,>=5.0
23
24
  Requires-Dist: jinja2>=3.1
@@ -43,9 +44,13 @@ Dynamic: license-file
43
44
  > *Coding under supervision.*
44
45
 
45
46
  chad is a single-user coding agent that runs entirely on an Apple Silicon Mac via
46
- [MLX](https://github.com/ml-explore/mlx). One 27B model and no API key. Every other
47
- harness assumes a datacenter on the other end of a socket; chad assumes a laptop,
48
- and the whole design falls out of that. (Not affiliated with Anthropic.)
47
+ [MLX](https://github.com/ml-explore/mlx). One 27B model and no API key. (Not affiliated
48
+ with Anthropic.)
49
+
50
+ Plenty of harnesses run local models now, and [pi](https://pi.dev) is a fantastic default
51
+ for the same reason llama.cpp is: it works with everything. I was steering chad in the
52
+ opposite direction. One model and one set of silicon, taken to the max. Swap out your
53
+ `CHAD_MODEL` and it still runs, you just leave the drafter and the kernels behind.
49
54
 
50
55
  ## Try it
51
56
 
@@ -54,13 +59,13 @@ uvx chad-code # runs anywhere; the command is still `chad`
54
59
  uvx chad-code prove # offline smoke test: 4 tiny fix-it tasks, verified, timed 🗿
55
60
  ```
56
61
 
57
- The first run asks, then downloads the model once (~13 GB) into the shared Hugging
62
+ The first run asks, then downloads the model once (~8 GB) into the shared Hugging
58
63
  Face cache. While it downloads, `cd` into a project and think of a scoped first ask:
59
64
  *"fix the failing test in `tests/test_x.py`"* lands, *"improve my codebase"* flails.
60
65
 
61
66
  chad targets 24 GB and nothing smaller. It runs below that and tells you it is doing so,
62
- but 13 GB of weights sit resident before a single token of context, so a 16 GB Mac gets a
63
- window too small to work in.
67
+ but 8 GB of weights and drafter plus a ~4 GB prefill transient sit resident before a single
68
+ token of context, so a 16 GB Mac gets a window too small to work in.
64
69
 
65
70
  The PyPI package is `chad-code`. Bare `chad` is an unrelated squatted package.
66
71
 
@@ -73,32 +78,42 @@ The PyPI package is `chad-code`. Bare `chad` is an unrelated squatted package.
73
78
 
74
79
  ## Same model, same Mac, stock engine
75
80
 
76
- The question worth answering: what do you gain over pointing a generic local-model tool at
77
- the same weights? Qwen3.8-27B at the same `UD-Q3_K_XL` recipe (Unsloth's GGUF for
78
- llama.cpp, chad's MLX conversion of the same bit map), the same M4 Pro (24 GB), one engine
79
- resident at a time, each measured with its own benchmark on a 512-token prompt and a
81
+ What do you gain over pointing a generic local-model tool at the same weights?
82
+ Qwen3.8-27B at the `UD-Q3_K_XL` recipe (Unsloth's GGUF for llama.cpp, chad's MLX
83
+ conversion of the same bit map; chad runs it with `--model`), the same M4 Pro (24 GB), one
84
+ engine resident at a time, each measured with its own benchmark on a 512-token prompt and a
80
85
  128-token generation.
81
86
 
82
87
  | Engine | Prefill (512-tok prompt) | Decode (128 tok) | Speculative decoding |
83
88
  |---|---|---|---|
84
- | llama.cpp `llama-bench` (stock, build 10470) | 102 tok/s | 10.9 tok/s | none for this model |
85
- | **chad**, serial (`CHAD_NO_DFLASH=1`) | 99 tok/s | 18.1 tok/s | off |
86
- | **chad**, default | 98 tok/s | **62 tok/s**¹ | DFlash2 block drafter |
87
-
88
- A 200-token function body takes roughly 18 seconds at 10.9 tok/s and 3 at 62. That is the
89
- difference between a batch job and a pair programmer, and closing it is what the project is
90
- for.
91
-
92
- Ollama does not get its own row: it is llama.cpp underneath with no speculative decoding for
93
- this model, and on the same GGUF (0.32.15, Modelfile `FROM` only) it measures 96 tok/s
89
+ | llama.cpp `llama-bench` (stock, build 10470) | 102 tok/s | 10.9 tok/s | off in this benchmark |
90
+ | llama.cpp `llama-server` (build 10917), serial | 97 tok/s | 11.3 tok/s | off |
91
+ | llama.cpp `llama-server` (build 10917) | 95 tok/s | 11.1 tok/s² | DFlash2 drafter (Q4_K_M GGUF) |
92
+ | **chad**, serial (`CHAD_NO_DFLASH=1`) | 100 tok/s | 17.9 tok/s | off |
93
+ | **chad** | 101 tok/s | **62.9 tok/s**¹ | DFlash2 block drafter |
94
+
95
+ A 200-token function body takes roughly 18 seconds at 10.9 tok/s and 3 at 63. You wait for
96
+ the first one and you talk to the second.
97
+
98
+ The weights chad ships by default are the same model in Prism ML's **ternary** build
99
+ (every projection Hadamard-rotated and stored at 2 bits): 7.2 GB resident instead of 12.3,
100
+ the same 64 tok/s drafted and 21 serial on this Mac, and the 5 GB it gives back is
101
+ context, roughly a **150k-token window** where the 3-bit landed near 56k. The like-for-like
102
+ table above stays on the 3-bit recipe because that is the quant llama.cpp has a GGUF of.
103
+
104
+ Ollama does not get its own row: it is llama.cpp underneath, measured without speculative
105
+ decoding, and on the same GGUF (0.32.15, Modelfile `FROM` only) it measures 96 tok/s
94
106
  prefill and the same **10.9** decode.
95
107
 
96
- ¹ 62 is a ceiling: `chad-bench`'s prompt is tiled code the drafter reads easily. Replayed
97
- against ten real mid-session contexts from `~/.chad/sessions` (12–19k tokens, tool results in
108
+ ¹ 63 is a ceiling: `chad-bench`'s prompt is tiled code the drafter reads easily. Replayed
109
+ against ten real mid-session contexts from `~/.chad/sessions` (12-19k tokens, tool results in
98
110
  place, 384-token decodes) the same engine measures **31.7 tok/s median / 21.4 floor** greedy
99
111
  against 14.8 serial, and **27.6 / 17.7** thinking against 13.9. That ~2× is what a session
100
- lives at. llama.cpp's Metal path for this hybrid architecture was not profiled, so read its
101
- row as what a fitted engine buys, not as a verdict on llama.cpp.
112
+ lives at.
113
+
114
+ ² llama.cpp has run DFlash2 since build 10658. Here it accepts 96.5% of drafted tokens and still
115
+ gains nothing: verifying 8 tokens costs ~6.4 serial steps on this GGUF and Mac
116
+ ([details](docs/benchmarks.md#same-model-same-mac-stock-engine)).
102
117
 
103
118
  Method, the longer runs and the caveats are in
104
119
  [Throughput & performance](docs/benchmarks.md#same-model-same-mac-stock-engine); the rows are
@@ -110,17 +125,17 @@ committed under [`benchmarks/stock/_runs/`](benchmarks/stock/); reproduce them w
110
125
  chad owns its inference loop instead of talking to a server, and the engine is fitted to the
111
126
  one checkpoint it ships:
112
127
 
113
- - **DFlash2 block speculation.** A bundled 1.9B drafter ([z-lab's DFlash2](https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2),
128
+ - DFlash2 block speculation. A bundled 1.9B drafter ([z-lab's DFlash2](https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2),
114
129
  ported to MLX and quantized) proposes a whole block of tokens from the main model's own
115
130
  hidden states. The main model verifies the block in one batched forward, and exact
116
131
  rejection sampling keeps every emitted token the model's own.
117
- - **A persistent prefix KV cache.** The transcript is kept a strict token-prefix of the live
132
+ - A persistent prefix KV cache. The transcript is kept a strict token-prefix of the live
118
133
  cache, so a follow-up step prefills the ~16 tokens it appended instead of the 5,000 it
119
- already read: **~0.75 s per step instead of ~50 s**. Any server with prompt caching gets
134
+ already read: **~0.55 s per step instead of ~48 s**. Any server with prompt caching gets
120
135
  the easy case; the work is holding it true across compaction, truncated turns and restarts.
121
136
  The system prefix is checkpointed to disk, so the second session anywhere starts warm
122
137
  (75.6 s → 5.5 s to the first tool call).
123
- - **Fused Metal kernels.** Quantized-KV attention, a small-M matmul for speculative verify,
138
+ - Fused Metal kernels. Quantized-KV attention, a small-M matmul for speculative verify,
124
139
  and a compiled single-token layer step, chosen per machine at load time, no knobs.
125
140
 
126
141
  ## What chad gives up
@@ -146,16 +161,21 @@ budget; everything else is deliberately plain.
146
161
 
147
162
  `uv run chad` launches a full-screen terminal UI (built on prompt_toolkit):
148
163
 
149
- - **shift-tab cycles permission modes**: `normal` (confirm each bash/write/edit) →
164
+ - shift-tab cycles permission modes: `normal` (confirm each bash/write/edit) →
150
165
  `auto-accept edits` (edits land silently, **terminal commands still ask**) → `yolo`
151
166
  (nothing asks) → `plan mode` (read-only: investigate and propose a numbered plan) → back.
152
- - **type-ahead message queue.** Keep typing while the agent works; messages run in order.
153
- - **ctrl-c interrupts the running turn** without killing the session. **↑prefilled /
167
+ A finished plan lands in `./plans/`; ctrl-g (or `/accept`) clears the context and starts
168
+ implementing it ([details](docs/configuration.md#plan-mode)).
169
+ - Type-ahead message queue. Keep typing while the agent works; messages run in order.
170
+ - ctrl-c interrupts the running turn without killing the session. **↑prefilled /
154
171
  ↓generated** token counts show an advancing **%** on an unavoidable full re-prefill, so it
155
172
  is never silent.
156
- - **`@file` / `@dir` mentions** and **`!command` shell passthrough.** Pull a file into
173
+ - `@file` / `@dir` mentions and `!command` shell passthrough. Pull a file into
157
174
  context inline, or run a shell command without invoking the model.
158
- - **voice mode, all local.** `/speech`, then ctrl-t to talk: Parakeet-on-MLX transcribes into
175
+ - Standing project instructions. A `CLAUDE.md` (or `AGENTS.md`) in the working directory is
176
+ appended to the system prompt, and `/init` reads the project and writes one for you
177
+ ([details](docs/configuration.md#project-instructions-claudemd--agentsmd)).
178
+ - Voice mode, all local. `/speech`, then ctrl-t to talk: Parakeet-on-MLX transcribes into
159
179
  the input box for you to review before Enter sends it, and replies are read aloud via macOS
160
180
  `say`. A word table teaches it your identifiers. Needs the `speech` extra
161
181
  ([details](docs/configuration.md#voice-mode-speech)).
@@ -169,12 +189,13 @@ budget; everything else is deliberately plain.
169
189
  | `--plan` | start in read-only plan mode (investigate and propose, edits blocked) |
170
190
  | `--yolo` | auto-approve bash/write/edit (skip confirm prompts) |
171
191
  | `--no-think` | skip the model's `<think>` blocks, faster on well-scoped work |
192
+ | `--think-budget N` | soft-cap each step's `<think>` at N tokens, force-close it and carry on (off by default) |
193
+ | `--backend llama` | run the same harness against a remote llama.cpp server, with `--base-url`, `--tokenizer` and `--api-key-env` ([details](docs/configuration.md#alternate-backend-remote)) |
172
194
  | `--model` | `auto` (the shipped default), or any HF repo id / local model dir |
173
195
  | `--repl` | plain line REPL instead of the TUI |
174
196
 
175
- Three subcommands, each with its own `--help`: `chad prove` (the offline smoke test),
176
- `chad serve` ([serve this Mac's model to a container or the LAN](docs/configuration.md#serving-the-local-model-to-a-container-chad-serve)),
177
- and `chad levers` (print the result-channel lever registry as JSON, for A/B ablation).
197
+ Two subcommands, each with its own `--help`: `chad prove` (the offline smoke test) and
198
+ `chad levers` (print the result-channel lever registry as JSON, for A/B ablation).
178
199
 
179
200
  A headless task (positional, or piped with no TTY) auto-approves mutating tools and runs
180
201
  greedy (temp 0). Every conversation is persisted under `~/.chad/sessions/`, and every resume
@@ -187,20 +208,24 @@ chad ships exactly one, downloaded once into the shared Hugging Face cache
187
208
 
188
209
  | Model | Quant | Footprint |
189
210
  |---|---|---|
190
- | [Qwen3.8-27B `UD-Q3_K_XL-DFlash2`](https://huggingface.co/nathansutton/Qwen3.8-27B-UD-Q3_K_XL-DFlash2-MLX) | 3-bit group-64 body, 5-bit `lm_head`, bundled 4-bit DFlash2 drafter | ~13 GB resident, 262k native context |
211
+ | [Qwen3.8-27B `Ternary-Bonsai-2-DFlash2`](https://huggingface.co/nathansutton/Qwen3.8-27B-Ternary-Bonsai-2-DFlash2-MLX) (default) | Prism ML's ternary build: Hadamard-rotated, 2-bit group-128, levels {−s, 0, +s}; bundled 4-bit DFlash2 drafter | ~8 GB resident, 262k native context, ~150k usable on 24 GB |
212
+ | [Qwen3.8-27B `UD-Q3_K_XL-DFlash2`](https://huggingface.co/nathansutton/Qwen3.8-27B-UD-Q3_K_XL-DFlash2-MLX) via `--model` | 3-bit group-64 body, 5-bit `lm_head`, the same bundled drafter | ~13 GB resident, ~56k usable on 24 GB |
191
213
 
192
214
  Qwen3.8-27B is **dense** (64 layers: 48 GatedDeltaNet + 16 full attention), so every
193
- parameter is on the critical path for every token and the quant is where decode speed comes
194
- from. The bits go where held-out perplexity says they pay: `lm_head` is a second full
195
- 1.27B-param tensor and is held at 5-bit, while `embed_tokens` is a lookup table whose error
196
- never compounds through a matmul, so it is cheapest. The name follows
197
- [Unsloth's convention](https://docs.unsloth.ai/) (`UD-…`), though the quant is MLX group-64
198
- affine, not a llama.cpp k-quant. The drafter ships in the same repo, pre-quantized.
215
+ parameter is on the critical path for every token and the quant is where both decode speed
216
+ and context come from: the governor prices a gigabyte of weights at about 29k tokens of
217
+ window. The ternary build is Prism ML's [Bonsai](https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-mlx-2bit)
218
+ pack of this checkpoint, repacked text-only with the base tokenizer and the drafter
219
+ bundled (Apache-2.0; created using Bonsai by Prism ML). Its projections are stored in a
220
+ rotated basis that an ordinary MLX loader would silently get wrong, so chad carries its own
221
+ loader for it and fits the decode fast-path, the verify kernel and the drafter to it. What it
222
+ costs: on code, teacher-forced perplexity is 4.49 against the 3-bit's 3.99 (+12%), while
223
+ the private eval tiers tie at 56/56. The 3-bit stays one flag away.
199
224
 
200
225
  `--model <repo or local dir>` runs different weights through the same engine and stays a
201
226
  first-class escape hatch. The drafter, the fused-attention coverage, the decode fastpath and
202
- the context governor are all fitted to the shipped checkpoint, so other weights run slower;
203
- they do not break.
227
+ the context governor are all fitted to the two checkpoints above, so other weights run
228
+ slower; they do not break.
204
229
 
205
230
  ## Installing & upgrading
206
231
 
@@ -220,7 +245,7 @@ uv run chad "add a --json flag to main.py and update the tests" # one-shot, he
220
245
  uv run chad -c # resume this directory's last conversation
221
246
  ```
222
247
 
223
- **Optional extras.** Two features are opt-in because they pull deps not every install wants:
248
+ Two features are opt-in because they pull deps not every install wants:
224
249
  `speech` (voice mode: a mic library, no torch) and `highlight` (syntax colour in diffs and
225
250
  previews). An extra rides on the install spec, not on a separate command, so how you add it
226
251
  depends on how you installed chad:
@@ -234,12 +259,12 @@ uv sync --extra speech # from a clone
234
259
  `/speech` in the TUI prints whichever of those matches your install, so you never have to
235
260
  work it out from here.
236
261
 
237
- **Upgrading** depends on how you installed: `uv tool upgrade chad-code`, `uvx --refresh
262
+ Upgrading depends on how you installed: `uv tool upgrade chad-code`, `uvx --refresh
238
263
  chad-code`, or `git pull && uv sync` for a clone. What changed lands in
239
264
  [`CHANGELOG.md`](CHANGELOG.md). Model weights are versioned separately, so a code upgrade
240
265
  never re-downloads the model.
241
266
 
242
- **Development.** `uv sync` once, then `uv run pytest -q`. The fast unit gate loads **no model
267
+ For development, `uv sync` once, then `uv run pytest -q`. The fast unit gate loads **no model
243
268
  weights**, runs in seconds, and is what CI runs. For throughput on your own machine, use
244
269
  `uv run chad-bench` (see [Throughput & performance](docs/benchmarks.md)).
245
270
 
@@ -247,11 +272,12 @@ weights**, runs in seconds, and is what CI runs. For throughput on your own mach
247
272
 
248
273
  chad speaks the same two extension formats as Claude Code:
249
274
 
250
- - **[Agent Skills](https://agentskills.io).** Drop a `SKILL.md` folder in
251
- `./.claude/skills/` and it becomes a slash command: `/ship`, `/investigate the flaky test`.
252
- Skills cost nothing until you run one, because chad puts no skill catalog in the system
253
- prompt.
254
- - **[MCP servers](https://modelcontextprotocol.io).** Configure stdio or HTTP servers in
275
+ - [Agent Skills](https://agentskills.io). Drop a `SKILL.md` folder in `./.agents/skills/`
276
+ or `./.claude/skills/` (or under `~/` for every project) and it becomes a slash command:
277
+ `/ship`, `/investigate the flaky test`. Skills cost nothing until you run one, because
278
+ chad puts no skill catalog in the system prompt. Precedence and the full list of roots
279
+ are in the [Configuration reference](docs/configuration.md#agent-skills-agentskillsio).
280
+ - [MCP servers](https://modelcontextprotocol.io). Configure stdio or HTTP servers in
255
281
  `./.mcp.json` to expose external tools (GitHub, Postgres, Linear, Slack, …) alongside
256
282
  chad's builtins, with static-token and OAuth auth.
257
283
 
@@ -259,16 +285,17 @@ Both are covered in full in the [Configuration reference](docs/configuration.md)
259
285
 
260
286
  ## Documentation
261
287
 
262
- - **[Design & internals](docs/design.md)** covers why prefill is the bill, the persistent
288
+ - [Design & internals](docs/design.md) covers why prefill is the bill, the persistent
263
289
  prefix cache, the trimmable/append-only trade, why the tool surface is five tools, and the
264
290
  ideas borrowed from other agents.
265
- - **[Throughput & performance](docs/benchmarks.md)** has the prefill, decode and warm-step
291
+ - [Throughput & performance](docs/benchmarks.md) has the prefill, decode and warm-step
266
292
  numbers you can reproduce with `chad-bench`, the stock-engine comparison, and what the
267
293
  cross-session warm start is worth.
268
- - **[Configuration reference](docs/configuration.md)** documents Agent Skills, MCP servers,
269
- the context window, every environment variable, and the safety opt-outs.
270
- - **[Troubleshooting](docs/troubleshooting.md)** is the symptom→knob map for when a session
294
+ - [Configuration reference](docs/configuration.md) documents project instructions, Agent
295
+ Skills, MCP servers, plan mode, the slash commands, the context window, every environment
296
+ variable, and the safety opt-outs.
297
+ - [Troubleshooting](docs/troubleshooting.md) maps symptoms to knobs for when a session
271
298
  rambles, loops, or slows.
272
- - **[Contributing](CONTRIBUTING.md)** says what lands easily and what needs a conversation
299
+ - [Contributing](CONTRIBUTING.md) says what lands easily and what needs a conversation
273
300
  first.
274
301
 
@@ -8,9 +8,13 @@
8
8
  > *Coding under supervision.*
9
9
 
10
10
  chad is a single-user coding agent that runs entirely on an Apple Silicon Mac via
11
- [MLX](https://github.com/ml-explore/mlx). One 27B model and no API key. Every other
12
- harness assumes a datacenter on the other end of a socket; chad assumes a laptop,
13
- and the whole design falls out of that. (Not affiliated with Anthropic.)
11
+ [MLX](https://github.com/ml-explore/mlx). One 27B model and no API key. (Not affiliated
12
+ with Anthropic.)
13
+
14
+ Plenty of harnesses run local models now, and [pi](https://pi.dev) is a fantastic default
15
+ for the same reason llama.cpp is: it works with everything. I was steering chad in the
16
+ opposite direction. One model and one set of silicon, taken to the max. Swap out your
17
+ `CHAD_MODEL` and it still runs, you just leave the drafter and the kernels behind.
14
18
 
15
19
  ## Try it
16
20
 
@@ -19,13 +23,13 @@ uvx chad-code # runs anywhere; the command is still `chad`
19
23
  uvx chad-code prove # offline smoke test: 4 tiny fix-it tasks, verified, timed 🗿
20
24
  ```
21
25
 
22
- The first run asks, then downloads the model once (~13 GB) into the shared Hugging
26
+ The first run asks, then downloads the model once (~8 GB) into the shared Hugging
23
27
  Face cache. While it downloads, `cd` into a project and think of a scoped first ask:
24
28
  *"fix the failing test in `tests/test_x.py`"* lands, *"improve my codebase"* flails.
25
29
 
26
30
  chad targets 24 GB and nothing smaller. It runs below that and tells you it is doing so,
27
- but 13 GB of weights sit resident before a single token of context, so a 16 GB Mac gets a
28
- window too small to work in.
31
+ but 8 GB of weights and drafter plus a ~4 GB prefill transient sit resident before a single
32
+ token of context, so a 16 GB Mac gets a window too small to work in.
29
33
 
30
34
  The PyPI package is `chad-code`. Bare `chad` is an unrelated squatted package.
31
35
 
@@ -38,32 +42,42 @@ The PyPI package is `chad-code`. Bare `chad` is an unrelated squatted package.
38
42
 
39
43
  ## Same model, same Mac, stock engine
40
44
 
41
- The question worth answering: what do you gain over pointing a generic local-model tool at
42
- the same weights? Qwen3.8-27B at the same `UD-Q3_K_XL` recipe (Unsloth's GGUF for
43
- llama.cpp, chad's MLX conversion of the same bit map), the same M4 Pro (24 GB), one engine
44
- resident at a time, each measured with its own benchmark on a 512-token prompt and a
45
+ What do you gain over pointing a generic local-model tool at the same weights?
46
+ Qwen3.8-27B at the `UD-Q3_K_XL` recipe (Unsloth's GGUF for llama.cpp, chad's MLX
47
+ conversion of the same bit map; chad runs it with `--model`), the same M4 Pro (24 GB), one
48
+ engine resident at a time, each measured with its own benchmark on a 512-token prompt and a
45
49
  128-token generation.
46
50
 
47
51
  | Engine | Prefill (512-tok prompt) | Decode (128 tok) | Speculative decoding |
48
52
  |---|---|---|---|
49
- | llama.cpp `llama-bench` (stock, build 10470) | 102 tok/s | 10.9 tok/s | none for this model |
50
- | **chad**, serial (`CHAD_NO_DFLASH=1`) | 99 tok/s | 18.1 tok/s | off |
51
- | **chad**, default | 98 tok/s | **62 tok/s**¹ | DFlash2 block drafter |
52
-
53
- A 200-token function body takes roughly 18 seconds at 10.9 tok/s and 3 at 62. That is the
54
- difference between a batch job and a pair programmer, and closing it is what the project is
55
- for.
56
-
57
- Ollama does not get its own row: it is llama.cpp underneath with no speculative decoding for
58
- this model, and on the same GGUF (0.32.15, Modelfile `FROM` only) it measures 96 tok/s
53
+ | llama.cpp `llama-bench` (stock, build 10470) | 102 tok/s | 10.9 tok/s | off in this benchmark |
54
+ | llama.cpp `llama-server` (build 10917), serial | 97 tok/s | 11.3 tok/s | off |
55
+ | llama.cpp `llama-server` (build 10917) | 95 tok/s | 11.1 tok/s² | DFlash2 drafter (Q4_K_M GGUF) |
56
+ | **chad**, serial (`CHAD_NO_DFLASH=1`) | 100 tok/s | 17.9 tok/s | off |
57
+ | **chad** | 101 tok/s | **62.9 tok/s**¹ | DFlash2 block drafter |
58
+
59
+ A 200-token function body takes roughly 18 seconds at 10.9 tok/s and 3 at 63. You wait for
60
+ the first one and you talk to the second.
61
+
62
+ The weights chad ships by default are the same model in Prism ML's **ternary** build
63
+ (every projection Hadamard-rotated and stored at 2 bits): 7.2 GB resident instead of 12.3,
64
+ the same 64 tok/s drafted and 21 serial on this Mac, and the 5 GB it gives back is
65
+ context, roughly a **150k-token window** where the 3-bit landed near 56k. The like-for-like
66
+ table above stays on the 3-bit recipe because that is the quant llama.cpp has a GGUF of.
67
+
68
+ Ollama does not get its own row: it is llama.cpp underneath, measured without speculative
69
+ decoding, and on the same GGUF (0.32.15, Modelfile `FROM` only) it measures 96 tok/s
59
70
  prefill and the same **10.9** decode.
60
71
 
61
- ¹ 62 is a ceiling: `chad-bench`'s prompt is tiled code the drafter reads easily. Replayed
62
- against ten real mid-session contexts from `~/.chad/sessions` (12–19k tokens, tool results in
72
+ ¹ 63 is a ceiling: `chad-bench`'s prompt is tiled code the drafter reads easily. Replayed
73
+ against ten real mid-session contexts from `~/.chad/sessions` (12-19k tokens, tool results in
63
74
  place, 384-token decodes) the same engine measures **31.7 tok/s median / 21.4 floor** greedy
64
75
  against 14.8 serial, and **27.6 / 17.7** thinking against 13.9. That ~2× is what a session
65
- lives at. llama.cpp's Metal path for this hybrid architecture was not profiled, so read its
66
- row as what a fitted engine buys, not as a verdict on llama.cpp.
76
+ lives at.
77
+
78
+ ² llama.cpp has run DFlash2 since build 10658. Here it accepts 96.5% of drafted tokens and still
79
+ gains nothing: verifying 8 tokens costs ~6.4 serial steps on this GGUF and Mac
80
+ ([details](docs/benchmarks.md#same-model-same-mac-stock-engine)).
67
81
 
68
82
  Method, the longer runs and the caveats are in
69
83
  [Throughput & performance](docs/benchmarks.md#same-model-same-mac-stock-engine); the rows are
@@ -75,17 +89,17 @@ committed under [`benchmarks/stock/_runs/`](benchmarks/stock/); reproduce them w
75
89
  chad owns its inference loop instead of talking to a server, and the engine is fitted to the
76
90
  one checkpoint it ships:
77
91
 
78
- - **DFlash2 block speculation.** A bundled 1.9B drafter ([z-lab's DFlash2](https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2),
92
+ - DFlash2 block speculation. A bundled 1.9B drafter ([z-lab's DFlash2](https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2),
79
93
  ported to MLX and quantized) proposes a whole block of tokens from the main model's own
80
94
  hidden states. The main model verifies the block in one batched forward, and exact
81
95
  rejection sampling keeps every emitted token the model's own.
82
- - **A persistent prefix KV cache.** The transcript is kept a strict token-prefix of the live
96
+ - A persistent prefix KV cache. The transcript is kept a strict token-prefix of the live
83
97
  cache, so a follow-up step prefills the ~16 tokens it appended instead of the 5,000 it
84
- already read: **~0.75 s per step instead of ~50 s**. Any server with prompt caching gets
98
+ already read: **~0.55 s per step instead of ~48 s**. Any server with prompt caching gets
85
99
  the easy case; the work is holding it true across compaction, truncated turns and restarts.
86
100
  The system prefix is checkpointed to disk, so the second session anywhere starts warm
87
101
  (75.6 s → 5.5 s to the first tool call).
88
- - **Fused Metal kernels.** Quantized-KV attention, a small-M matmul for speculative verify,
102
+ - Fused Metal kernels. Quantized-KV attention, a small-M matmul for speculative verify,
89
103
  and a compiled single-token layer step, chosen per machine at load time, no knobs.
90
104
 
91
105
  ## What chad gives up
@@ -111,16 +125,21 @@ budget; everything else is deliberately plain.
111
125
 
112
126
  `uv run chad` launches a full-screen terminal UI (built on prompt_toolkit):
113
127
 
114
- - **shift-tab cycles permission modes**: `normal` (confirm each bash/write/edit) →
128
+ - shift-tab cycles permission modes: `normal` (confirm each bash/write/edit) →
115
129
  `auto-accept edits` (edits land silently, **terminal commands still ask**) → `yolo`
116
130
  (nothing asks) → `plan mode` (read-only: investigate and propose a numbered plan) → back.
117
- - **type-ahead message queue.** Keep typing while the agent works; messages run in order.
118
- - **ctrl-c interrupts the running turn** without killing the session. **↑prefilled /
131
+ A finished plan lands in `./plans/`; ctrl-g (or `/accept`) clears the context and starts
132
+ implementing it ([details](docs/configuration.md#plan-mode)).
133
+ - Type-ahead message queue. Keep typing while the agent works; messages run in order.
134
+ - ctrl-c interrupts the running turn without killing the session. **↑prefilled /
119
135
  ↓generated** token counts show an advancing **%** on an unavoidable full re-prefill, so it
120
136
  is never silent.
121
- - **`@file` / `@dir` mentions** and **`!command` shell passthrough.** Pull a file into
137
+ - `@file` / `@dir` mentions and `!command` shell passthrough. Pull a file into
122
138
  context inline, or run a shell command without invoking the model.
123
- - **voice mode, all local.** `/speech`, then ctrl-t to talk: Parakeet-on-MLX transcribes into
139
+ - Standing project instructions. A `CLAUDE.md` (or `AGENTS.md`) in the working directory is
140
+ appended to the system prompt, and `/init` reads the project and writes one for you
141
+ ([details](docs/configuration.md#project-instructions-claudemd--agentsmd)).
142
+ - Voice mode, all local. `/speech`, then ctrl-t to talk: Parakeet-on-MLX transcribes into
124
143
  the input box for you to review before Enter sends it, and replies are read aloud via macOS
125
144
  `say`. A word table teaches it your identifiers. Needs the `speech` extra
126
145
  ([details](docs/configuration.md#voice-mode-speech)).
@@ -134,12 +153,13 @@ budget; everything else is deliberately plain.
134
153
  | `--plan` | start in read-only plan mode (investigate and propose, edits blocked) |
135
154
  | `--yolo` | auto-approve bash/write/edit (skip confirm prompts) |
136
155
  | `--no-think` | skip the model's `<think>` blocks, faster on well-scoped work |
156
+ | `--think-budget N` | soft-cap each step's `<think>` at N tokens, force-close it and carry on (off by default) |
157
+ | `--backend llama` | run the same harness against a remote llama.cpp server, with `--base-url`, `--tokenizer` and `--api-key-env` ([details](docs/configuration.md#alternate-backend-remote)) |
137
158
  | `--model` | `auto` (the shipped default), or any HF repo id / local model dir |
138
159
  | `--repl` | plain line REPL instead of the TUI |
139
160
 
140
- Three subcommands, each with its own `--help`: `chad prove` (the offline smoke test),
141
- `chad serve` ([serve this Mac's model to a container or the LAN](docs/configuration.md#serving-the-local-model-to-a-container-chad-serve)),
142
- and `chad levers` (print the result-channel lever registry as JSON, for A/B ablation).
161
+ Two subcommands, each with its own `--help`: `chad prove` (the offline smoke test) and
162
+ `chad levers` (print the result-channel lever registry as JSON, for A/B ablation).
143
163
 
144
164
  A headless task (positional, or piped with no TTY) auto-approves mutating tools and runs
145
165
  greedy (temp 0). Every conversation is persisted under `~/.chad/sessions/`, and every resume
@@ -152,20 +172,24 @@ chad ships exactly one, downloaded once into the shared Hugging Face cache
152
172
 
153
173
  | Model | Quant | Footprint |
154
174
  |---|---|---|
155
- | [Qwen3.8-27B `UD-Q3_K_XL-DFlash2`](https://huggingface.co/nathansutton/Qwen3.8-27B-UD-Q3_K_XL-DFlash2-MLX) | 3-bit group-64 body, 5-bit `lm_head`, bundled 4-bit DFlash2 drafter | ~13 GB resident, 262k native context |
175
+ | [Qwen3.8-27B `Ternary-Bonsai-2-DFlash2`](https://huggingface.co/nathansutton/Qwen3.8-27B-Ternary-Bonsai-2-DFlash2-MLX) (default) | Prism ML's ternary build: Hadamard-rotated, 2-bit group-128, levels {−s, 0, +s}; bundled 4-bit DFlash2 drafter | ~8 GB resident, 262k native context, ~150k usable on 24 GB |
176
+ | [Qwen3.8-27B `UD-Q3_K_XL-DFlash2`](https://huggingface.co/nathansutton/Qwen3.8-27B-UD-Q3_K_XL-DFlash2-MLX) via `--model` | 3-bit group-64 body, 5-bit `lm_head`, the same bundled drafter | ~13 GB resident, ~56k usable on 24 GB |
156
177
 
157
178
  Qwen3.8-27B is **dense** (64 layers: 48 GatedDeltaNet + 16 full attention), so every
158
- parameter is on the critical path for every token and the quant is where decode speed comes
159
- from. The bits go where held-out perplexity says they pay: `lm_head` is a second full
160
- 1.27B-param tensor and is held at 5-bit, while `embed_tokens` is a lookup table whose error
161
- never compounds through a matmul, so it is cheapest. The name follows
162
- [Unsloth's convention](https://docs.unsloth.ai/) (`UD-…`), though the quant is MLX group-64
163
- affine, not a llama.cpp k-quant. The drafter ships in the same repo, pre-quantized.
179
+ parameter is on the critical path for every token and the quant is where both decode speed
180
+ and context come from: the governor prices a gigabyte of weights at about 29k tokens of
181
+ window. The ternary build is Prism ML's [Bonsai](https://huggingface.co/prism-ml/Ternary-Bonsai-2-27B-mlx-2bit)
182
+ pack of this checkpoint, repacked text-only with the base tokenizer and the drafter
183
+ bundled (Apache-2.0; created using Bonsai by Prism ML). Its projections are stored in a
184
+ rotated basis that an ordinary MLX loader would silently get wrong, so chad carries its own
185
+ loader for it and fits the decode fast-path, the verify kernel and the drafter to it. What it
186
+ costs: on code, teacher-forced perplexity is 4.49 against the 3-bit's 3.99 (+12%), while
187
+ the private eval tiers tie at 56/56. The 3-bit stays one flag away.
164
188
 
165
189
  `--model <repo or local dir>` runs different weights through the same engine and stays a
166
190
  first-class escape hatch. The drafter, the fused-attention coverage, the decode fastpath and
167
- the context governor are all fitted to the shipped checkpoint, so other weights run slower;
168
- they do not break.
191
+ the context governor are all fitted to the two checkpoints above, so other weights run
192
+ slower; they do not break.
169
193
 
170
194
  ## Installing & upgrading
171
195
 
@@ -185,7 +209,7 @@ uv run chad "add a --json flag to main.py and update the tests" # one-shot, he
185
209
  uv run chad -c # resume this directory's last conversation
186
210
  ```
187
211
 
188
- **Optional extras.** Two features are opt-in because they pull deps not every install wants:
212
+ Two features are opt-in because they pull deps not every install wants:
189
213
  `speech` (voice mode: a mic library, no torch) and `highlight` (syntax colour in diffs and
190
214
  previews). An extra rides on the install spec, not on a separate command, so how you add it
191
215
  depends on how you installed chad:
@@ -199,12 +223,12 @@ uv sync --extra speech # from a clone
199
223
  `/speech` in the TUI prints whichever of those matches your install, so you never have to
200
224
  work it out from here.
201
225
 
202
- **Upgrading** depends on how you installed: `uv tool upgrade chad-code`, `uvx --refresh
226
+ Upgrading depends on how you installed: `uv tool upgrade chad-code`, `uvx --refresh
203
227
  chad-code`, or `git pull && uv sync` for a clone. What changed lands in
204
228
  [`CHANGELOG.md`](CHANGELOG.md). Model weights are versioned separately, so a code upgrade
205
229
  never re-downloads the model.
206
230
 
207
- **Development.** `uv sync` once, then `uv run pytest -q`. The fast unit gate loads **no model
231
+ For development, `uv sync` once, then `uv run pytest -q`. The fast unit gate loads **no model
208
232
  weights**, runs in seconds, and is what CI runs. For throughput on your own machine, use
209
233
  `uv run chad-bench` (see [Throughput & performance](docs/benchmarks.md)).
210
234
 
@@ -212,11 +236,12 @@ weights**, runs in seconds, and is what CI runs. For throughput on your own mach
212
236
 
213
237
  chad speaks the same two extension formats as Claude Code:
214
238
 
215
- - **[Agent Skills](https://agentskills.io).** Drop a `SKILL.md` folder in
216
- `./.claude/skills/` and it becomes a slash command: `/ship`, `/investigate the flaky test`.
217
- Skills cost nothing until you run one, because chad puts no skill catalog in the system
218
- prompt.
219
- - **[MCP servers](https://modelcontextprotocol.io).** Configure stdio or HTTP servers in
239
+ - [Agent Skills](https://agentskills.io). Drop a `SKILL.md` folder in `./.agents/skills/`
240
+ or `./.claude/skills/` (or under `~/` for every project) and it becomes a slash command:
241
+ `/ship`, `/investigate the flaky test`. Skills cost nothing until you run one, because
242
+ chad puts no skill catalog in the system prompt. Precedence and the full list of roots
243
+ are in the [Configuration reference](docs/configuration.md#agent-skills-agentskillsio).
244
+ - [MCP servers](https://modelcontextprotocol.io). Configure stdio or HTTP servers in
220
245
  `./.mcp.json` to expose external tools (GitHub, Postgres, Linear, Slack, …) alongside
221
246
  chad's builtins, with static-token and OAuth auth.
222
247
 
@@ -224,16 +249,17 @@ Both are covered in full in the [Configuration reference](docs/configuration.md)
224
249
 
225
250
  ## Documentation
226
251
 
227
- - **[Design & internals](docs/design.md)** covers why prefill is the bill, the persistent
252
+ - [Design & internals](docs/design.md) covers why prefill is the bill, the persistent
228
253
  prefix cache, the trimmable/append-only trade, why the tool surface is five tools, and the
229
254
  ideas borrowed from other agents.
230
- - **[Throughput & performance](docs/benchmarks.md)** has the prefill, decode and warm-step
255
+ - [Throughput & performance](docs/benchmarks.md) has the prefill, decode and warm-step
231
256
  numbers you can reproduce with `chad-bench`, the stock-engine comparison, and what the
232
257
  cross-session warm start is worth.
233
- - **[Configuration reference](docs/configuration.md)** documents Agent Skills, MCP servers,
234
- the context window, every environment variable, and the safety opt-outs.
235
- - **[Troubleshooting](docs/troubleshooting.md)** is the symptom→knob map for when a session
258
+ - [Configuration reference](docs/configuration.md) documents project instructions, Agent
259
+ Skills, MCP servers, plan mode, the slash commands, the context window, every environment
260
+ variable, and the safety opt-outs.
261
+ - [Troubleshooting](docs/troubleshooting.md) maps symptoms to knobs for when a session
236
262
  rambles, loops, or slows.
237
- - **[Contributing](CONTRIBUTING.md)** says what lands easily and what needs a conversation
263
+ - [Contributing](CONTRIBUTING.md) says what lands easily and what needs a conversation
238
264
  first.
239
265