chad-code 1.0.5__tar.gz → 1.0.7__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.5 → chad_code-1.0.7}/PKG-INFO +22 -10
- {chad_code-1.0.5 → chad_code-1.0.7}/README.md +17 -4
- {chad_code-1.0.5 → chad_code-1.0.7}/pyproject.toml +40 -13
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/__init__.py +1 -1
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/agent.py +43 -20
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/cli.py +6 -6
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/levers.py +11 -0
- chad_code-1.0.7/src/chad/lsp.py +340 -0
- chad_code-1.0.7/src/chad/lspclient.py +341 -0
- chad_code-1.0.7/src/chad/lspservers.py +157 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/mcp.py +81 -31
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/mcp_oauth.py +37 -16
- chad_code-1.0.7/src/chad/parakeet/LICENSE +201 -0
- chad_code-1.0.7/src/chad/parakeet/__init__.py +27 -0
- chad_code-1.0.7/src/chad/parakeet/alignment.py +287 -0
- chad_code-1.0.7/src/chad/parakeet/attention.py +640 -0
- chad_code-1.0.7/src/chad/parakeet/audio.py +183 -0
- chad_code-1.0.7/src/chad/parakeet/cache.py +156 -0
- chad_code-1.0.7/src/chad/parakeet/conformer.py +423 -0
- chad_code-1.0.7/src/chad/parakeet/ctc.py +34 -0
- chad_code-1.0.7/src/chad/parakeet/parakeet.py +1055 -0
- chad_code-1.0.7/src/chad/parakeet/rnnt.py +155 -0
- chad_code-1.0.7/src/chad/parakeet/tokenizer.py +3 -0
- chad_code-1.0.7/src/chad/parakeet/utils.py +112 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/prove.py +1 -1
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/repomap.py +242 -16
- chad_code-1.0.7/src/chad/speech.py +565 -0
- chad_code-1.0.7/src/chad/symbols.py +162 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/tools.py +30 -5
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/tui.py +296 -13
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad_code.egg-info/PKG-INFO +22 -10
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad_code.egg-info/SOURCES.txt +20 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad_code.egg-info/requires.txt +4 -5
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_agent_e2e.py +14 -14
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_engine.py +1 -1
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_garble_invariant.py +1 -1
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_gate.py +67 -11
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_ignore.py +3 -3
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_lever_bite.py +27 -4
- chad_code-1.0.7/tests/test_lsp.py +315 -0
- chad_code-1.0.7/tests/test_lsp_live.py +108 -0
- chad_code-1.0.7/tests/test_lspclient.py +205 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_mcp.py +77 -7
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_mcp_oauth.py +3 -3
- chad_code-1.0.7/tests/test_repomap_polyglot.py +111 -0
- chad_code-1.0.7/tests/test_speech.py +284 -0
- chad_code-1.0.7/tests/test_speech_tui.py +508 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_subagent.py +7 -5
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_symbols.py +83 -52
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_tui.py +50 -1
- chad_code-1.0.5/src/chad/lsp.py +0 -323
- chad_code-1.0.5/src/chad/symbols.py +0 -372
- chad_code-1.0.5/tests/test_lsp.py +0 -115
- {chad_code-1.0.5 → chad_code-1.0.7}/LICENSE +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/setup.cfg +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/atif.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/base_engine.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/bench.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/compaction.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/completion_engine.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/config.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/diag.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/engine.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/guardrails.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/ignore.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/mlx_fastpath.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/mlx_qsdpa.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/profiles.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/prompt.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/render.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/serve.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/session.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/skills.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/syntaxgate.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/toolcall_parse.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad/validate.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad_code.egg-info/dependency_links.txt +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad_code.egg-info/entry_points.txt +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/src/chad_code.egg-info/top_level.txt +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_agent.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_agent_guards.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_atif.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_bench.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_cli.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_compact_notice.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_compaction.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_completion_engine.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_config.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_confirm_preview.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_done_audit.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_drift_warn.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_edit.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_edit_corruption.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_engine_kvquant.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_engine_pld_hybrid.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_feel_pack.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_intent.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_levers.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_log_redaction.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_mlx_fastpath.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_mlx_qsdpa.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_plan_review.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_prove.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_render.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_replace_lines.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_repomap.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_serve.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_session.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_skills.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_syntaxgate.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_toolcall_parse.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_tools.py +0 -0
- {chad_code-1.0.5 → chad_code-1.0.7}/tests/test_validate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chad-code
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.7
|
|
4
4
|
Summary: Local MLX-backed, Claude-Code-style coding agent (Apple Silicon, Ornith 35B/9B)
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Project-URL: Repository, https://github.com/nathansutton/chad
|
|
@@ -21,18 +21,17 @@ Requires-Dist: mlx<0.33,>=0.32.0; sys_platform == "darwin"
|
|
|
21
21
|
Requires-Dist: mlx-lm<0.32,>=0.31.3; sys_platform == "darwin"
|
|
22
22
|
Requires-Dist: transformers<5.13,>=5.0
|
|
23
23
|
Requires-Dist: jinja2>=3.1
|
|
24
|
-
Requires-Dist: numpy<
|
|
24
|
+
Requires-Dist: numpy<3,>=1.26
|
|
25
25
|
Requires-Dist: huggingface-hub>=1.22.0
|
|
26
|
-
Requires-Dist: jedi>=0.19.2
|
|
27
26
|
Requires-Dist: prompt_toolkit>=3.0.43
|
|
28
27
|
Requires-Dist: tree-sitter>=0.26.0
|
|
29
28
|
Requires-Dist: tree-sitter-language-pack>=1.12.2
|
|
30
29
|
Requires-Dist: rustworkx>=0.15
|
|
31
|
-
Requires-Dist: mcp
|
|
30
|
+
Requires-Dist: mcp<3,>=2
|
|
32
31
|
Provides-Extra: highlight
|
|
33
32
|
Requires-Dist: pygments>=2.17; extra == "highlight"
|
|
34
|
-
Provides-Extra:
|
|
35
|
-
Requires-Dist:
|
|
33
|
+
Provides-Extra: speech
|
|
34
|
+
Requires-Dist: sounddevice>=0.5; extra == "speech"
|
|
36
35
|
Dynamic: license-file
|
|
37
36
|
|
|
38
37
|
# chad — a local, MLX-backed coding agent
|
|
@@ -81,10 +80,12 @@ instrument:
|
|
|
81
80
|
| **Harness** | open-ended, anything you can imagine | plan. execute. nothing else. |
|
|
82
81
|
| **When wrong** | reasons a way out | already shipped |
|
|
83
82
|
|
|
84
|
-

|
|
85
84
|
|
|
86
|
-
> Real session, unedited (
|
|
87
|
-
>
|
|
85
|
+
> Real session, unedited (the silent prefill is cut): a local 35B finds the cent that
|
|
86
|
+
> floor division loses, fixes it, and verifies itself — then your own `pytest`, in your
|
|
87
|
+
> own shell. Recorded with `--yolo` so nothing pauses for a keypress; the default mode
|
|
88
|
+
> stops and asks before every edit and every command.
|
|
88
89
|
|
|
89
90
|
## Frontier scores, laptop cost
|
|
90
91
|
|
|
@@ -171,7 +172,10 @@ it chad uses the tree-sitter fallback automatically.
|
|
|
171
172
|
`uv run chad` launches a full-screen terminal UI (built on prompt_toolkit):
|
|
172
173
|
|
|
173
174
|
- **shift-tab cycles permission modes** — `normal` (confirm each bash/write/edit) →
|
|
174
|
-
`auto-accept edits`
|
|
175
|
+
`auto-accept edits` (edits land silently; **terminal commands still ask**) → `yolo`
|
|
176
|
+
(nothing asks) → `plan mode` (read-only: investigate + propose a numbered plan) → back.
|
|
177
|
+
- **the approval prompt shows what you're approving** — the full command on its own lines
|
|
178
|
+
above the input, not a clipped single line. Approving blind isn't approving.
|
|
175
179
|
- **type-ahead message queue** — keep typing while the agent works; messages run in order.
|
|
176
180
|
- **ctrl-c interrupts the running turn** without killing the session.
|
|
177
181
|
- **live status line** — model, mode, context %, a state glyph + verb, elapsed seconds, and
|
|
@@ -181,6 +185,14 @@ it chad uses the tree-sitter fallback automatically.
|
|
|
181
185
|
`/model`, `/mode`, `/help`, `/exit`. Same set in the `--repl` line interface.
|
|
182
186
|
- **`@file` / `@dir` mentions** and **`!command` shell passthrough** — pull a file into
|
|
183
187
|
context inline, or run a shell command without invoking the model.
|
|
188
|
+
- **voice mode, all local** — `/speech`, then ctrl-t to talk: Parakeet-on-MLX transcribes
|
|
189
|
+
into the input box for you to review before Enter sends it (esc discards a take);
|
|
190
|
+
replies are read aloud via macOS `say`. A pre-roll buffer means your first syllable
|
|
191
|
+
isn't clipped, and a personal word table (`~/.chad/speech_words.json`) teaches it your
|
|
192
|
+
identifiers — `{"pie test": "pytest"}`. Dictation cost is linear in take length, so a
|
|
193
|
+
long thought is fine; `/speech` off releases both the mic and the weights. Nothing
|
|
194
|
+
leaves the machine. Needs the `speech` extra (`uv sync --extra speech` — just a mic
|
|
195
|
+
library; no torch, no numba).
|
|
184
196
|
|
|
185
197
|
**Usage.** `uv run chad --help` is the source of truth:
|
|
186
198
|
|
|
@@ -44,10 +44,12 @@ instrument:
|
|
|
44
44
|
| **Harness** | open-ended, anything you can imagine | plan. execute. nothing else. |
|
|
45
45
|
| **When wrong** | reasons a way out | already shipped |
|
|
46
46
|
|
|
47
|
-

|
|
48
48
|
|
|
49
|
-
> Real session, unedited (
|
|
50
|
-
>
|
|
49
|
+
> Real session, unedited (the silent prefill is cut): a local 35B finds the cent that
|
|
50
|
+
> floor division loses, fixes it, and verifies itself — then your own `pytest`, in your
|
|
51
|
+
> own shell. Recorded with `--yolo` so nothing pauses for a keypress; the default mode
|
|
52
|
+
> stops and asks before every edit and every command.
|
|
51
53
|
|
|
52
54
|
## Frontier scores, laptop cost
|
|
53
55
|
|
|
@@ -134,7 +136,10 @@ it chad uses the tree-sitter fallback automatically.
|
|
|
134
136
|
`uv run chad` launches a full-screen terminal UI (built on prompt_toolkit):
|
|
135
137
|
|
|
136
138
|
- **shift-tab cycles permission modes** — `normal` (confirm each bash/write/edit) →
|
|
137
|
-
`auto-accept edits`
|
|
139
|
+
`auto-accept edits` (edits land silently; **terminal commands still ask**) → `yolo`
|
|
140
|
+
(nothing asks) → `plan mode` (read-only: investigate + propose a numbered plan) → back.
|
|
141
|
+
- **the approval prompt shows what you're approving** — the full command on its own lines
|
|
142
|
+
above the input, not a clipped single line. Approving blind isn't approving.
|
|
138
143
|
- **type-ahead message queue** — keep typing while the agent works; messages run in order.
|
|
139
144
|
- **ctrl-c interrupts the running turn** without killing the session.
|
|
140
145
|
- **live status line** — model, mode, context %, a state glyph + verb, elapsed seconds, and
|
|
@@ -144,6 +149,14 @@ it chad uses the tree-sitter fallback automatically.
|
|
|
144
149
|
`/model`, `/mode`, `/help`, `/exit`. Same set in the `--repl` line interface.
|
|
145
150
|
- **`@file` / `@dir` mentions** and **`!command` shell passthrough** — pull a file into
|
|
146
151
|
context inline, or run a shell command without invoking the model.
|
|
152
|
+
- **voice mode, all local** — `/speech`, then ctrl-t to talk: Parakeet-on-MLX transcribes
|
|
153
|
+
into the input box for you to review before Enter sends it (esc discards a take);
|
|
154
|
+
replies are read aloud via macOS `say`. A pre-roll buffer means your first syllable
|
|
155
|
+
isn't clipped, and a personal word table (`~/.chad/speech_words.json`) teaches it your
|
|
156
|
+
identifiers — `{"pie test": "pytest"}`. Dictation cost is linear in take length, so a
|
|
157
|
+
long thought is fine; `/speech` off releases both the mic and the weights. Nothing
|
|
158
|
+
leaves the machine. Needs the `speech` extra (`uv sync --extra speech` — just a mic
|
|
159
|
+
library; no torch, no numba).
|
|
147
160
|
|
|
148
161
|
**Usage.** `uv run chad --help` is the source of truth:
|
|
149
162
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# import name, and command name are independent. `uvx chad-code` runs the alias
|
|
5
5
|
# script added under [project.scripts].
|
|
6
6
|
name = "chad-code"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.7"
|
|
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"
|
|
@@ -49,9 +49,11 @@ dependencies = [
|
|
|
49
49
|
# absent in a clean install (e.g. chad inside a Linux benchmark container), where its
|
|
50
50
|
# absence crashes the first turn. Declare it directly.
|
|
51
51
|
"jinja2>=3.1",
|
|
52
|
-
|
|
52
|
+
# <3: the old <2 cap was 1.0.0-era caution, not a known break. numpy 2.4.6
|
|
53
|
+
# verified end-to-end (full gate + live Parakeet-on-MLX transcription +
|
|
54
|
+
# `chad prove` 4/4); staying open keeps 2.x-floored ASR deps adoptable.
|
|
55
|
+
"numpy>=1.26,<3",
|
|
53
56
|
"huggingface-hub>=1.22.0", # first-run model download (cli.py/engine.py); imported directly, not just transitively
|
|
54
|
-
"jedi>=0.19.2", # in-process symbolic code intelligence (read/search/edit)
|
|
55
57
|
"prompt_toolkit>=3.0.43",
|
|
56
58
|
"tree-sitter>=0.26.0",
|
|
57
59
|
"tree-sitter-language-pack>=1.12.2",
|
|
@@ -60,10 +62,14 @@ dependencies = [
|
|
|
60
62
|
# wheel, zero transitive deps. Benchmarked vs scipy/networkx/igraph on a real
|
|
61
63
|
# 421k-edge graph: identical top-50 ranking, 0.79s -> 0.04s.
|
|
62
64
|
"rustworkx>=0.15",
|
|
63
|
-
#
|
|
64
|
-
# chad
|
|
65
|
-
#
|
|
66
|
-
|
|
65
|
+
# Single 2.x code path (the 1.x cap left with serena-agent, which hard-pinned mcp
|
|
66
|
+
# and was the only thing keeping chad below 2). The 1->2 port was done deliberately,
|
|
67
|
+
# not by a resolver: two of the renames (isError->is_error, readOnlyHint->
|
|
68
|
+
# read_only_hint) FAIL SILENTLY through getattr defaults, so both are pinned by
|
|
69
|
+
# tests that would fail on the old attribute names (tests/test_mcp.py). 2.0 also
|
|
70
|
+
# negotiates protocol 2026-07-28; the 1.x line tops out at 2025-11-25. Capped <3:
|
|
71
|
+
# the same class of break will happen again.
|
|
72
|
+
"mcp>=2,<3",
|
|
67
73
|
]
|
|
68
74
|
|
|
69
75
|
# Optional syntax highlighting in diffs / confirm previews. Pure-Python,
|
|
@@ -73,11 +79,16 @@ dependencies = [
|
|
|
73
79
|
# package. Document uv/git installs only.
|
|
74
80
|
[project.optional-dependencies]
|
|
75
81
|
highlight = ["pygments>=2.17"]
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
#
|
|
79
|
-
#
|
|
80
|
-
|
|
82
|
+
# Voice mode (/speech in the TUI): push-to-talk dictation via chad's vendored
|
|
83
|
+
# Parakeet-on-MLX (src/chad/parakeet — the PyPI parakeet-mlx drags librosa's
|
|
84
|
+
# numba/scipy tree for one mel-filterbank call) + spoken replies via macOS
|
|
85
|
+
# `say`. All on-device, nothing leaves the machine. mlx/numpy/huggingface-hub
|
|
86
|
+
# are already core deps, so the extra adds only the mic (sounddevice/PortAudio).
|
|
87
|
+
# Optional and import-guarded in speech.py: absent, /speech prints the install
|
|
88
|
+
# hint and everything else is untouched. Install with `uv sync --extra speech`.
|
|
89
|
+
speech = [
|
|
90
|
+
"sounddevice>=0.5",
|
|
91
|
+
]
|
|
81
92
|
|
|
82
93
|
# The one entrypoint: `uv run chad`. (`chad-bench` reproduces docs/benchmarks.md.)
|
|
83
94
|
# `chad-code` is an alias so `uvx chad-code` (which runs the console script matching
|
|
@@ -107,6 +118,10 @@ build-backend = "setuptools.build_meta"
|
|
|
107
118
|
[tool.setuptools.packages.find]
|
|
108
119
|
where = ["src"]
|
|
109
120
|
|
|
121
|
+
# The vendored Parakeet code is Apache-2.0; its license text must travel with
|
|
122
|
+
# the code, wheel included.
|
|
123
|
+
[tool.setuptools.package-data]
|
|
124
|
+
"chad.parakeet" = ["LICENSE"]
|
|
110
125
|
[tool.pytest.ini_options]
|
|
111
126
|
testpaths = ["tests"]
|
|
112
127
|
# No "-q" here: CI/docs/agents all invoke `pytest -q` explicitly, and stacking the two
|
|
@@ -127,10 +142,16 @@ src = ["src", "tests"]
|
|
|
127
142
|
select = ["F", "E9", "I"]
|
|
128
143
|
ignore = ["E741"] # ambiguous var names (l/I) appear in existing math code
|
|
129
144
|
|
|
145
|
+
[tool.ruff.lint.per-file-ignores]
|
|
146
|
+
# Vendored upstream (see chad/parakeet/__init__.py), kept byte-close for
|
|
147
|
+
# future re-syncs — not held to chad's own lint standard. (F401: imports left
|
|
148
|
+
# in place for upstream fidelity after the transcribe(path) cut.)
|
|
149
|
+
"src/chad/parakeet/*" = ["F401", "F821", "F841", "E741"]
|
|
150
|
+
|
|
130
151
|
[tool.mypy]
|
|
131
152
|
python_version = "3.11"
|
|
132
153
|
files = ["src/chad"]
|
|
133
|
-
ignore_missing_imports = true # mlx/
|
|
154
|
+
ignore_missing_imports = true # mlx/tree_sitter/mcp ship partial or no stubs
|
|
134
155
|
warn_unused_ignores = true
|
|
135
156
|
implicit_optional = true # `x: T = None` defaults are idiomatic throughout; this
|
|
136
157
|
# restores the pre-PEP-484 reading rather than rewriting
|
|
@@ -142,6 +163,12 @@ disable_error_code = ["var-annotated"]
|
|
|
142
163
|
# Tighten (disallow_untyped_defs, re-enable var-annotated, add B/UP/SIM in ruff) in a
|
|
143
164
|
# follow-up once this baseline holds.
|
|
144
165
|
|
|
166
|
+
[[tool.mypy.overrides]]
|
|
167
|
+
# Vendored upstream inference code (see chad/parakeet/__init__.py) is not held
|
|
168
|
+
# to chad's gate so re-syncs stay a plain re-copy.
|
|
169
|
+
module = "chad.parakeet.*"
|
|
170
|
+
ignore_errors = true
|
|
171
|
+
|
|
145
172
|
[[tool.mypy.overrides]]
|
|
146
173
|
# mlx_fastpath patches mlx_lm classes that only exist on macOS: with mlx installed
|
|
147
174
|
# (dev Macs, the macos CI jobs) its `type: ignore[method-assign]` comments are load-
|
|
@@ -4,7 +4,7 @@ A flat collection of cooperating modules behind one console script (``chad``):
|
|
|
4
4
|
the inference engine, the tool layer, the agent loop, and the terminal UI.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "1.0.
|
|
7
|
+
__version__ = "1.0.7"
|
|
8
8
|
|
|
9
9
|
# chad sets no MLX_* runtime vars. MLX_METAL_FAST_SYNCH, MLX_MAX_OPS_PER_BUFFER
|
|
10
10
|
# and MLX_MAX_MB_PER_BUFFER were each measured end-to-end on the 35B and every
|
|
@@ -57,11 +57,12 @@ SUBAGENT_READ_ONLY = {
|
|
|
57
57
|
|
|
58
58
|
def subagent_tools_for(parent_mode: str, requested: str) -> str:
|
|
59
59
|
"""The toolset a sub-agent may run with. A sub-agent auto-approves its own tool
|
|
60
|
-
calls (mode='
|
|
61
|
-
than its parent: only
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
calls (mode='yolo', no confirm callback), so it must never hold more autonomy
|
|
61
|
+
than its parent: only a 'yolo' parent (--yolo / headless) may delegate 'all'.
|
|
62
|
+
A 'normal' parent's human-approval promise, an 'auto' parent's narrower promise
|
|
63
|
+
(edits yes, shell no — and 'all' includes bash), and plan mode's read-only
|
|
64
|
+
promise all clamp the sub-agent to read-only."""
|
|
65
|
+
if parent_mode == "yolo" and requested == "all":
|
|
65
66
|
return "all"
|
|
66
67
|
return "read-only"
|
|
67
68
|
|
|
@@ -209,10 +210,30 @@ def close_unclosed_think(text: str, thinking: bool) -> str:
|
|
|
209
210
|
|
|
210
211
|
# Permission modes (Claude Code parity, cycled with shift-tab in the TUI):
|
|
211
212
|
# normal — confirm each mutating tool (bash/write/edit)
|
|
212
|
-
# auto — auto-approve
|
|
213
|
+
# auto — auto-approve FILE EDITS only; bash and MCP tools still ask
|
|
214
|
+
# yolo — auto-approve every mutating tool (--yolo starts here)
|
|
213
215
|
# plan — read-only: changes are blocked; the model researches and proposes a plan
|
|
214
|
-
MODES = ("normal", "auto", "plan")
|
|
215
|
-
MODE_LABEL = {"normal": "normal", "auto": "auto-accept edits", "
|
|
216
|
+
MODES = ("normal", "auto", "yolo", "plan")
|
|
217
|
+
MODE_LABEL = {"normal": "normal", "auto": "auto-accept edits", "yolo": "yolo",
|
|
218
|
+
"plan": "plan mode"}
|
|
219
|
+
|
|
220
|
+
# What `auto` waves through: edits to files in the workspace, whose blast radius is a
|
|
221
|
+
# diff you can read and revert. `bash` is excluded on purpose — a shell command can
|
|
222
|
+
# touch anything on the machine, is the one tool whose preview is genuinely hard to
|
|
223
|
+
# eyeball, and is exactly what a human is still in the loop for. MCP tools are excluded
|
|
224
|
+
# for the same reason (a server-side tool can send mail or hit an API). The list is
|
|
225
|
+
# explicit rather than `MUTATING - {"bash"}` so it fails CLOSED: a mutating tool added
|
|
226
|
+
# later asks for approval until someone deliberately adds it here.
|
|
227
|
+
AUTO_EDIT_TOOLS = {"write", "edit", "replace_lines", "insert_lines",
|
|
228
|
+
"replace_symbol", "insert_symbol", "rename_symbol"}
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def auto_approves(mode: str, name: str) -> bool:
|
|
232
|
+
"""Does `mode` clear tool `name` without asking a human? Read-only tools never
|
|
233
|
+
reach here (the caller checks is_mutating first)."""
|
|
234
|
+
if mode == "yolo":
|
|
235
|
+
return True
|
|
236
|
+
return mode == "auto" and name in AUTO_EDIT_TOOLS
|
|
216
237
|
_PLAN_PREFIX = (
|
|
217
238
|
"[PLAN MODE. Research first (read/grep/glob/repo_map/overview) — do NOT edit "
|
|
218
239
|
"project files or run commands. Then write ONE self-contained plan to "
|
|
@@ -325,7 +346,7 @@ class Agent:
|
|
|
325
346
|
skills.reset_session()
|
|
326
347
|
from . import mcp
|
|
327
348
|
mcp.reset_session()
|
|
328
|
-
self.mode = mode or ("
|
|
349
|
+
self.mode = mode or ("yolo" if yolo else "normal")
|
|
329
350
|
self.thinking = thinking # Ornith is a reasoning model; toggles <think> blocks
|
|
330
351
|
# Steps per WINDOW, not a hard kill: a window that landed+verified a change
|
|
331
352
|
# earns an extension (see guardrails.extend_step_cap; absolute ceiling 4x).
|
|
@@ -460,7 +481,7 @@ class Agent:
|
|
|
460
481
|
|
|
461
482
|
@property
|
|
462
483
|
def yolo(self) -> bool:
|
|
463
|
-
return self.mode == "
|
|
484
|
+
return self.mode == "yolo"
|
|
464
485
|
|
|
465
486
|
@property
|
|
466
487
|
def tok_per_s(self) -> float:
|
|
@@ -586,18 +607,18 @@ class Agent:
|
|
|
586
607
|
|
|
587
608
|
def _confirm(self, name, args) -> bool:
|
|
588
609
|
# Destructive-bash seatbelt: a catastrophic shell command (rm -rf ~, mkfs,
|
|
589
|
-
# curl|sh, …) is screened even in --yolo
|
|
590
|
-
# untrusted repo contents and
|
|
610
|
+
# curl|sh, …) is screened even in --yolo mode, because the model acts on
|
|
611
|
+
# untrusted repo contents and yolo mode has no human in the loop. If a confirm
|
|
591
612
|
# channel exists (TTY or callback) we force the prompt; headless with no channel
|
|
592
613
|
# we BLOCK rather than execute on injection. CHAD_NO_DESTRUCTIVE_GUARD=1 opts out.
|
|
593
614
|
dangerous = (name == "bash" and isinstance(args, dict)
|
|
594
615
|
and not config.flag("CHAD_NO_DESTRUCTIVE_GUARD")
|
|
595
616
|
and guardrails.is_destructive_bash(str(args.get("command", ""))))
|
|
596
|
-
if
|
|
617
|
+
if not is_mutating(name) or auto_approves(self.mode, name):
|
|
597
618
|
if not dangerous:
|
|
598
619
|
return True
|
|
599
620
|
if self._confirm_cb is None and not sys.stdin.isatty():
|
|
600
|
-
self._emit("info", f" [blocked destructive command
|
|
621
|
+
self._emit("info", f" [blocked destructive command — nobody to approve it: "
|
|
601
622
|
f"{args.get('command', '')!r}; set CHAD_NO_DESTRUCTIVE_GUARD=1 to allow]")
|
|
602
623
|
# Tell the MODEL the truth about who blocked it and why: "[denied by
|
|
603
624
|
# user]" reads as a human refusal and teaches the wrong lesson (the
|
|
@@ -642,12 +663,13 @@ class Agent:
|
|
|
642
663
|
parent. Its grep/read churn never enters the main transcript; only this return
|
|
643
664
|
does. Depth 1 only: a sub-agent can't itself call `task` (its schema omits it)."""
|
|
644
665
|
# Never grant a sub-agent more autonomy than its parent: the sub-agent runs
|
|
645
|
-
# mode='
|
|
646
|
-
# the parent itself is auto-approved (--yolo/headless); 'normal' (human
|
|
647
|
-
# every mutation)
|
|
666
|
+
# mode='yolo' with no confirm callback, so an 'all' toolset is honored only when
|
|
667
|
+
# the parent itself is fully auto-approved (--yolo/headless); 'normal' (human
|
|
668
|
+
# confirms every mutation), 'auto' (human still confirms shell) and 'plan'
|
|
669
|
+
# (read-only) parents clamp it to read-only.
|
|
648
670
|
requested, tools = tools, subagent_tools_for(self.mode, tools)
|
|
649
671
|
if requested == "all" and tools == "read-only":
|
|
650
|
-
self._emit("muted", " ⌊ sub-agent clamped to read-only (parent mode is not
|
|
672
|
+
self._emit("muted", " ⌊ sub-agent clamped to read-only (parent mode is not yolo)")
|
|
651
673
|
self._emit("muted", f" ⌊ delegating to sub-agent: {description}")
|
|
652
674
|
log.info("TASK start | desc=%r | tools=%s | prompt=%r",
|
|
653
675
|
description, tools, redact(prompt[:300]))
|
|
@@ -657,7 +679,7 @@ class Agent:
|
|
|
657
679
|
try:
|
|
658
680
|
sub = Agent(
|
|
659
681
|
self.engine,
|
|
660
|
-
mode="
|
|
682
|
+
mode="yolo", # auto-approve within its restricted toolset
|
|
661
683
|
max_steps=SUBAGENT_MAX_STEPS,
|
|
662
684
|
ctx_limit=min(self.ctx_limit, SUBAGENT_CTX_LIMIT),
|
|
663
685
|
thinking=self.thinking,
|
|
@@ -1509,7 +1531,8 @@ class Agent:
|
|
|
1509
1531
|
# fire, which is how a turn once shipped a 28-token garble as its final
|
|
1510
1532
|
# answer. read_only/explain asks are exempt. Banks a note so
|
|
1511
1533
|
# auto-continue relaunches fresh instead of shipping nothing.
|
|
1512
|
-
did_nothing = self.mode
|
|
1534
|
+
did_nothing = (self.mode in ("auto", "yolo")
|
|
1535
|
+
and not read_only_intent and not did_work)
|
|
1513
1536
|
if (action_task and not read_only_intent
|
|
1514
1537
|
and (not made_edit or unverified_edit)) or did_nothing:
|
|
1515
1538
|
# Churn→audit handoff: this hard stop used to fire with the
|
|
@@ -712,7 +712,7 @@ def _main(argv=None):
|
|
|
712
712
|
sys.stderr.write(f"ready in {load_s:.1f}s | context {eng.effective_ctx} tokens "
|
|
713
713
|
f"(compact at {ctx_limit})\n")
|
|
714
714
|
|
|
715
|
-
start_mode = "plan" if args.plan else ("
|
|
715
|
+
start_mode = "plan" if args.plan else ("yolo" if args.yolo else "normal")
|
|
716
716
|
thinking = not args.no_think
|
|
717
717
|
|
|
718
718
|
# Live per-turn ctx-limit recheck: the startup number was
|
|
@@ -758,9 +758,9 @@ def _main(argv=None):
|
|
|
758
758
|
# auto-approve mutating tools unless the user asked for read-only --plan.
|
|
759
759
|
run_mode = start_mode
|
|
760
760
|
if run_mode == "normal" and not sys.stdin.isatty():
|
|
761
|
-
run_mode = "
|
|
761
|
+
run_mode = "yolo"
|
|
762
762
|
sys.stderr.write("[headless: auto-approving tools (use --plan for read-only)]\n")
|
|
763
|
-
agent = Agent(eng, yolo=(run_mode == "
|
|
763
|
+
agent = Agent(eng, yolo=(run_mode == "yolo"), ctx_limit=ctx_limit,
|
|
764
764
|
mode=run_mode, thinking=thinking, resume=resume, persist=True,
|
|
765
765
|
think_budget=args.think_budget,
|
|
766
766
|
turn_budget_s=turn_budget_s, ctx_limit_fn=ctx_limit_fn)
|
|
@@ -777,7 +777,7 @@ def _main(argv=None):
|
|
|
777
777
|
# ships as an empty diff — the measured bail signature.
|
|
778
778
|
auto_continue = config.env_int("CHAD_AUTO_CONTINUE")
|
|
779
779
|
continues = auto_continue if auto_continue is not None \
|
|
780
|
-
else (2 if run_mode == "
|
|
780
|
+
else (2 if run_mode == "yolo" else 0)
|
|
781
781
|
used_continues = 0
|
|
782
782
|
while agent.budget_note:
|
|
783
783
|
# Base allowance first; past it, keep granting fresh attempts while most
|
|
@@ -819,7 +819,7 @@ def _main(argv=None):
|
|
|
819
819
|
if getattr(eng, "temp", None) is not None:
|
|
820
820
|
eng.temp = max(eng.temp, 0.6)
|
|
821
821
|
eng.reset()
|
|
822
|
-
agent = Agent(eng, yolo=(run_mode == "
|
|
822
|
+
agent = Agent(eng, yolo=(run_mode == "yolo"), ctx_limit=ctx_limit,
|
|
823
823
|
mode=run_mode, thinking=thinking, persist=True,
|
|
824
824
|
think_budget=args.think_budget,
|
|
825
825
|
turn_budget_s=relaunch_s, ctx_limit_fn=ctx_limit_fn)
|
|
@@ -841,7 +841,7 @@ def _main(argv=None):
|
|
|
841
841
|
sys.stderr.write(f"[review] task finished with {turn_budget_s - elapsed:.0f}s "
|
|
842
842
|
"of budget left; running one fresh-context verification pass\n")
|
|
843
843
|
eng.reset()
|
|
844
|
-
agent = Agent(eng, yolo=(run_mode == "
|
|
844
|
+
agent = Agent(eng, yolo=(run_mode == "yolo"), ctx_limit=ctx_limit,
|
|
845
845
|
mode=run_mode, thinking=thinking, persist=True,
|
|
846
846
|
think_budget=args.think_budget,
|
|
847
847
|
turn_budget_s=review_budget, ctx_limit_fn=ctx_limit_fn)
|
|
@@ -446,6 +446,17 @@ LEVERS: dict[str, Lever] = {
|
|
|
446
446
|
"a flat file listing, so the model orients without a reflexive step-1 repo_map "
|
|
447
447
|
"call. Degrades to the flat listing when repomap is unavailable (see prompt.py).",
|
|
448
448
|
"ai-codex"),
|
|
449
|
+
|
|
450
|
+
# --- group "ctxengine": the uniform-symbols work. -------------------------------
|
|
451
|
+
"post_edit_diagnostics": Lever(
|
|
452
|
+
"Append the language server's post-edit typecheck errors (~50 tokens, errors "
|
|
453
|
+
"only, capped) to edit/symbol-edit results — the semantic tier above "
|
|
454
|
+
"syntaxgate's parse check. A type error surfaced in the edit result replaces "
|
|
455
|
+
"the multi-thousand-token failed test run the model would otherwise need to "
|
|
456
|
+
"discover it. Never spawns a server (an edit must not pay a cold start): "
|
|
457
|
+
"only a server already warmed by find_refs/hover/disambiguation is "
|
|
458
|
+
"consulted; absent one, silence.",
|
|
459
|
+
"ctxengine"),
|
|
449
460
|
}
|
|
450
461
|
|
|
451
462
|
|