mlx-code 0.0.19__tar.gz → 0.0.20__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 (31) hide show
  1. mlx_code-0.0.20/PKG-INFO +509 -0
  2. mlx_code-0.0.20/README.md +473 -0
  3. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/gits.py +7 -1
  4. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/main.py +3 -2
  5. mlx_code-0.0.20/mlx_code/ntui.py +483 -0
  6. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/repl.py +67 -94
  7. mlx_code-0.0.20/mlx_code.egg-info/PKG-INFO +509 -0
  8. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code.egg-info/SOURCES.txt +1 -0
  9. {mlx_code-0.0.19 → mlx_code-0.0.20}/setup.py +1 -1
  10. mlx_code-0.0.20/tests/test.py +668 -0
  11. mlx_code-0.0.19/PKG-INFO +0 -226
  12. mlx_code-0.0.19/README.md +0 -190
  13. mlx_code-0.0.19/mlx_code.egg-info/PKG-INFO +0 -226
  14. mlx_code-0.0.19/tests/test.py +0 -218
  15. {mlx_code-0.0.19 → mlx_code-0.0.20}/LICENSE +0 -0
  16. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/__init__.py +0 -0
  17. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/apis.py +0 -0
  18. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/lsp_tool.py +0 -0
  19. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/mcb.py +0 -0
  20. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/mcb_tool.py +0 -0
  21. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/stream_log.py +0 -0
  22. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/tools.py +0 -0
  23. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/util.py +0 -0
  24. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/view_git.py +0 -0
  25. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code/view_log.py +0 -0
  26. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code.egg-info/dependency_links.txt +0 -0
  27. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code.egg-info/entry_points.txt +0 -0
  28. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code.egg-info/requires.txt +0 -0
  29. {mlx_code-0.0.19 → mlx_code-0.0.20}/mlx_code.egg-info/top_level.txt +0 -0
  30. {mlx_code-0.0.19 → mlx_code-0.0.20}/setup.cfg +0 -0
  31. {mlx_code-0.0.19 → mlx_code-0.0.20}/tests/__init__.py +0 -0
@@ -0,0 +1,509 @@
1
+ Metadata-Version: 2.4
2
+ Name: mlx-code
3
+ Version: 0.0.20
4
+ Summary: Coding Agent for Mac
5
+ Home-page: https://josefalbers.github.io/mlx-code/
6
+ Author: J Joe
7
+ Author-email: albersj66@gmail.com
8
+ License: Apache-2.0
9
+ Project-URL: Source, https://github.com/JosefAlbers/mlx-code
10
+ Project-URL: Issues, https://github.com/JosefAlbers/mlx-code/issues
11
+ Project-URL: Documentation, https://josefalbers.github.io/mlx-code/
12
+ Requires-Python: >=3.12.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: mlx-lm>=0.31.3; platform_system == "Darwin"
16
+ Requires-Dist: httpx
17
+ Requires-Dist: pydantic
18
+ Requires-Dist: textual>=8.2.7
19
+ Requires-Dist: rich>=15.0.0
20
+ Provides-Extra: all
21
+ Requires-Dist: python-lsp-server[all]; extra == "all"
22
+ Requires-Dist: GitPython; extra == "all"
23
+ Requires-Dist: pygments; extra == "all"
24
+ Dynamic: author
25
+ Dynamic: author-email
26
+ Dynamic: description
27
+ Dynamic: description-content-type
28
+ Dynamic: home-page
29
+ Dynamic: license
30
+ Dynamic: license-file
31
+ Dynamic: project-url
32
+ Dynamic: provides-extra
33
+ Dynamic: requires-dist
34
+ Dynamic: requires-python
35
+ Dynamic: summary
36
+
37
+ # mlx-code
38
+
39
+ A Git-native coding agent that can run entirely on your Mac. No API keys, no cloud, and no data leaving your machine. Powered by Apple MLX, it turns commits, branches, and worktrees into the agent’s state, history, and execution model
40
+
41
+ https://github.com/user-attachments/assets/0569d101-8d0a-4e67-9e82-fce84a5ef3f0
42
+
43
+ ---
44
+
45
+ ## Architecture
46
+
47
+ ```
48
+ Conversation tree (nodes = git commits with embedded chat history)
49
+
50
+ main ──●──●──●──●──●──●──●──●──●──●
51
+ │ │
52
+ │ └── branch-1 ──●──●──●
53
+ │ │ ┌────────────┐
54
+ │ └─┤ branch-1-0 ├──●──●
55
+ │ └─────┬──────┘
56
+ └── branch-0 ──●──●──● │
57
+
58
+
59
+ REPL tabs (each tab = a git branch + agent) │
60
+
61
+
62
+ ┌──────────────────────────────────────────────┼─────────┐
63
+ │ TUI tabs │ │
64
+ │ ┌──────┐ ┌──────────┐ ┌──────────┐ ┌─────┴──────┐ │
65
+ │ │ main │ │ branch-0 │ │ branch-1 │ │ branch-1-0 │ │
66
+ │ └──────┘ └────┬─────┘ └──────────┘ └────────────┘ │
67
+ └─────────────────┼──────────────────────────────────────┘
68
+
69
+ ├────────────────────────────────────► each tab is an independent Agent
70
+
71
+ ┌────┴────────────────────────────────┐
72
+ │ Agent │
73
+ │ ┌──────────────┐ ┌──────────────┐ │
74
+ │ │ API: │ │ Tools: │ │
75
+ │ │ MLX (local) │ │ Read Write │ │
76
+ │ │ Claude │ │ Edit Bash │ │
77
+ │ │ Gemini │ │ Grep Find │ │
78
+ │ │ OpenAI │ │ Ls Skill │ │
79
+ │ └──────────────┘ │ Agent ───────┼─┼───► spawns child Agent
80
+ │ └──────────────┘ │ (each with own tools + worktree + etc)
81
+ │ Git worktree │
82
+ │ (isolation + session state) │
83
+ └─────────────────────────────────────┘
84
+ ```
85
+
86
+ Each layer is importable and composable on its own. A commit records state, a branch records an alternative path, and a tab is just a live view over an `Agent`.
87
+
88
+ ```python
89
+ from mlx_code.repl import Agent
90
+ from mlx_code.tools import ReadTool, WriteTool, EditTool
91
+
92
+ agent = Agent(api='claude', tool_names=['Read', 'Write', 'Edit'])
93
+ result = await agent.run('refactor utils.py to use dataclasses')
94
+ ```
95
+
96
+ ---
97
+
98
+ ## Quick start
99
+
100
+ ```bash
101
+ pip install mlx-code
102
+ mlc # launch with local MLX model
103
+ mlc-run --api gemini # or use a remote provider
104
+ mlc-run --api deepseek --model deepseek-v4-flash
105
+ ```
106
+
107
+ That's it. The first run starts a local inference server and drops you into the REPL.
108
+
109
+ [![Link](https://raw.githubusercontent.com/JosefAlbers/mlx-code/main/assets/mlx-code-v0.0.20.gif)](https://youtu.be/0lkY7YQCyCo)
110
+
111
+ ---
112
+
113
+ ## Core ideas
114
+
115
+ - **Git is the state machine.** Every file-changing agent step is committed with the conversation that produced it, so you can inspect, resume, and branch from any checkpoint.
116
+ - **Branches are alternative futures.** A branch is not just a Git branch; it is a different reasoning path with its own worktree and session state.
117
+ - **Agents are the primitive.** Tabs, branches, and delegated subtasks are all instances of the same `Agent` abstraction.
118
+ - **Worktrees provide isolation.** The agent edits in a separate worktree, so your main checkout stays clean and recoverable.
119
+
120
+ ---
121
+
122
+ ## Why mlx-code
123
+
124
+ **Agents as reusable workflow atoms.** Tabs, branches, and tasks are all managed within instances of `Agent`. Each one gets its own conversation, its own tools, and its own worktree. Agents can spawn sub-agents to delegate subtasks, and each child is a full agent with its own scoped tool set.
125
+
126
+ **Git is the database.** When the agent makes file changes, they’re committed to a git worktree with the full conversation embedded in the commit message. Resume any past session by hash, branch from any checkpoint, and inspect the agent timeline with `git log`. No proprietary state files, just Git.
127
+
128
+ **Your working directory is never at risk.** The agent operates inside a `git worktree`, not your checkout. It can make a mess, and you can inspect or discard it without ever touching `main`.
129
+
130
+ **Built-in safety nets.** Subprocess environment variables go through an explicit allowlist, so secrets in your shell are never leaked to agent-spawned processes.
131
+
132
+ **Batteries included.** Everything ships in one pip install: the MLX inference engine, the multi-protocol API server, the agent loop, the tools, and the TUI. No llama.cpp, no ollama, no vLLM bridge to find and configure. And the server natively speaks OpenAI, Anthropic, Gemini, and Codex wire formats simultaneously, so `claude`, `codex`, and `gemini` CLIs can all work against your local model without a translation layer.
133
+
134
+ ---
135
+
136
+ ## Agent primitive
137
+
138
+ Every surface in mlx-code composes the same abstraction:
139
+
140
+ | Surface | What it does | How it creates an Agent |
141
+ |---------|-------------|------------------------|
142
+ | Python API | Programmatic pipeline | `Agent(...)` |
143
+ | Agent tool | Model delegates a subtask | `agent.spawn()` |
144
+ | `/branch` | Fork from any checkpoint | `agent.branch()` |
145
+ | TUI tab | Parallel conversation thread | `Tab('title', Agent(...))` |
146
+
147
+ They're all the same object. A branch is an agent. A sub-task is an agent. A tab is an agent in a tab container. This means patterns compose: a branched agent can spawn sub-agents, a sub-agent can be branched, and any agent can be inspected, diffed, or resumed because it's backed by git.
148
+
149
+ ---
150
+
151
+ ## Sessions as git history
152
+
153
+ When the agent makes file changes, they're committed to the worktree with the full conversation embedded in the commit message. This means you can always pick up where you left off:
154
+
155
+ ```bash
156
+ # Resume a session from any commit hash
157
+ mlc --resume abc1234
158
+ ```
159
+
160
+ ```python
161
+ # Or from Python
162
+ from mlx_code.gits import resume_worktree
163
+ from mlx_code.repl import Agent
164
+
165
+ gwt, messages = resume_worktree(".", "abc1234")
166
+ agent = Agent(ctx={"gwt": gwt})
167
+ agent.messages = messages
168
+ await agent.run("now add unit tests")
169
+ ```
170
+
171
+ Branch from any point in the conversation — each branch gets its own worktree:
172
+
173
+ ```
174
+ /branch # branch from current state
175
+ /branch --rev 2 # branch from the 2nd user turn
176
+ /branch --rev 3 --as-worktree try different approach
177
+ ```
178
+
179
+ Since it's just git, you can inspect the timeline outside the REPL:
180
+
181
+ ```bash
182
+ git log --oneline # commits = agent turns that changed files
183
+ git diff HEAD~1 # what the agent changed
184
+ git worktree list # all active worktrees
185
+ ```
186
+
187
+ ---
188
+
189
+ ## Swapping agents for different phases
190
+
191
+ Coding is a pipeline: spec, draft, review, verify. Each phase benefits from a different mind: a planner needs broad context and no write access; a coder needs tight constraints and fast iteration; a reviewer needs read-only access and a critical eye.
192
+
193
+ mlx-code lets you hot-swap the agent configuration mid-session. The conversation continues, but the system prompt, model, and tool set change:
194
+
195
+ ```
196
+ /clear --config reviewer.yaml
197
+ ```
198
+
199
+ ```yaml
200
+ # reviewer.yaml
201
+ model: gemini-3.1-flash-lite
202
+ api: gemini
203
+ system: |
204
+ Do NOT make edits. Only comment.
205
+ Challenge incorrect assumptions.
206
+ Identify edge cases and failure modes.
207
+ Prefer robust solutions over quick hacks.
208
+ Keep implementations simple.
209
+ Avoid introducing dependencies unless they provide clear value.
210
+ Ask when unsure.
211
+ tools:
212
+ - Read
213
+ - Grep
214
+ - Bash
215
+ ```
216
+
217
+ ```yaml
218
+ # coder.yaml
219
+ model: deepseek-v4-flash
220
+ api: deepseek
221
+ system: |
222
+ Read first. Change second.
223
+ Understand the relevant files before editing.
224
+ Make focused, minimal changes.
225
+ Follow existing code style and architecture.
226
+ Do not rewrite working code unnecessarily.
227
+ Fix root causes rather than symptoms.
228
+ After coding, verify that the solution is consistent with the requirements and surrounding code.
229
+ tools:
230
+ - Read
231
+ - Write
232
+ - Edit
233
+ - Bash
234
+ - Grep
235
+ ```
236
+
237
+ Reliability comes from specialization plus constraint. A read-only reviewer can't silently break your code. A scoped implementer can't wander off into architecture discussions. Each agent does one thing well.
238
+
239
+ ---
240
+
241
+ ## Command Line
242
+
243
+ ### `mlc`: local server + harness
244
+
245
+ Starts the MLX inference server and launches the built-in TUI harness against it.
246
+
247
+ ```bash
248
+ # Default: local server + default TUI
249
+ mlc
250
+
251
+ # Use a simple terminal REPL instead of the TUI
252
+ mlc --notui
253
+
254
+ # Use a different harness (routes traffic through the local server)
255
+ mlc --leash claude
256
+ mlc --leash gemini
257
+ mlc --leash codex
258
+
259
+ # Server only, no harness
260
+ mlc --leash none
261
+
262
+ # Specify a model
263
+ mlc --model mlx-community/Qwen3.5-4B-OptiQ-4bit
264
+
265
+ # Restrict the tools available to the agent
266
+ mlc --tools Read Write Bash
267
+
268
+ # Custom system prompt
269
+ mlc --system "You are a helpful assistant."
270
+
271
+ # Load skills from a directory (scans recursively for SKILL.md files)
272
+ mlc --skill ./my-skills
273
+
274
+ # Resume a previous session from a git commit hash
275
+ mlc --resume <commit-hash>
276
+
277
+ # Send an initial prompt automatically when the REPL starts
278
+ mlc --prompt "summarise all changes in the last 7 days"
279
+
280
+ # Stream the agent log to a file (useful for monitoring long runs)
281
+ mlc --stream agent.log
282
+
283
+ # Bind to a specific host/port (default: 127.0.0.1:8000, auto-increments if busy)
284
+ mlc --host 0.0.0.0 --port 9000
285
+ ```
286
+
287
+ Because `mlc` reads from stdin when it isn’t a TTY, it composes naturally with shell pipes:
288
+
289
+ ```bash
290
+ echo "Here's the solution you proposed: <excerpt>$(mlc -p "write code for a chrome extension to play youtube x5 speed")</excerpt> Now argue against it." | mlc
291
+ ```
292
+
293
+ ### `mlc-run`: harness only
294
+
295
+ Runs the agent harness against an already-running server or a remote provider.
296
+
297
+ ```bash
298
+ # Connect to a local server at 127.0.0.1:8000 (default)
299
+ mlc-run
300
+
301
+ # Remote providers
302
+ mlc-run --api claude
303
+ mlc-run --api gemini
304
+ mlc-run --api deepseek
305
+ mlc-run --api deepseek --model deepseek-v4-pro
306
+ mlc-run --api codex
307
+
308
+ # Custom endpoint
309
+ echo "explain lsp.py" | mlc-run -a deepseek | cat - PLAN.md | mlc-run --url http://localhost:9000
310
+
311
+ # Simple terminal REPL (no TUI)
312
+ mlc-run --notui
313
+ ```
314
+
315
+
316
+ ---
317
+
318
+ ## Using as a Library
319
+
320
+ Import the pieces you need to build background workers, scheduled jobs, or event-triggered handlers.
321
+
322
+ ### Spawn an agent from Python
323
+
324
+ ```python
325
+ import asyncio
326
+ from mlx_code.repl import Agent
327
+
328
+ async def main():
329
+ agent = Agent(system="You are a concise technical writer.")
330
+ await agent.run("Summarise all *.py files changed in the last 7 days. Save to digest.md.")
331
+
332
+ asyncio.run(main())
333
+ ```
334
+
335
+ ### Multi-agent pipeline
336
+
337
+ ```python
338
+ import asyncio
339
+ from mlx_code.repl import Agent
340
+
341
+ async def main():
342
+ researcher = Agent(system="You are a research assistant.")
343
+ await researcher.run("Research PBFT consensus. Save a structured summary to kb/draft.md.")
344
+
345
+ reviewer = Agent(system="You are a critical reviewer.")
346
+ await reviewer.run(
347
+ "Read kb/draft.md. Write a one-paragraph critique to kb/critique.md. "
348
+ "Use only information in that file."
349
+ )
350
+
351
+ asyncio.run(main())
352
+ ```
353
+
354
+ ### Parallel workers with `asyncio.gather`
355
+
356
+ ```python
357
+ import asyncio
358
+ from mlx_code.repl import Agent
359
+
360
+ async def main():
361
+ topics = ["history", "algorithms", "industry_usage"]
362
+ agents = [Agent() for _ in topics]
363
+ await asyncio.gather(*[
364
+ a.run(f"Research the {t} of Byzantine Fault Tolerance. Save to kb/{t}.md.")
365
+ for a, t in zip(agents, topics)
366
+ ])
367
+ reducer = Agent()
368
+ await reducer.run("Read all files in kb/. Synthesise into final_report.md.")
369
+
370
+ asyncio.run(main())
371
+ ```
372
+
373
+ ### Branch an agent
374
+
375
+ ```python
376
+ child = agent.branch() # deep-copies messages; independent worktree
377
+ await child.run("Try an alternative approach and save to alt.py.")
378
+ ```
379
+
380
+ ### Resume a session from a git commit
381
+
382
+ mlx-code stores the full conversation as JSON in each commit message, so you can restore both the workspace state and the agent's memory from any checkpoint.
383
+
384
+ ```python
385
+ import asyncio
386
+ from mlx_code.gits import resume_worktree
387
+ from mlx_code.repl import Agent, repl
388
+
389
+ async def main():
390
+ gwt, messages = resume_worktree(".", "abc1234")
391
+ agent = Agent(ctx={"gwt": gwt})
392
+ agent.messages = messages
393
+ await repl(agent)
394
+
395
+ asyncio.run(main())
396
+ ```
397
+
398
+ ### Load agent config from a file
399
+
400
+ Pass a JSON or YAML file to reconfigure the agent at runtime (also available as `/clear --config F` in the TUI).
401
+
402
+ ```python
403
+ from mlx_code.repl import load_agent_config
404
+ config = load_agent_config("agent.yaml")
405
+ agent = Agent(**config)
406
+ ```
407
+
408
+ ### Custom tools
409
+
410
+ Subclass `Tool`, define a Pydantic schema, and pass the class at instantiation.
411
+
412
+ ```python
413
+ from mlx_code.tools import Tool
414
+ from mlx_code.repl import Agent
415
+ from pydantic import BaseModel, Field
416
+
417
+ class QueryParams(BaseModel):
418
+ query: str = Field(description="SQL query to run")
419
+
420
+ class LiveDBTool(Tool):
421
+ name = "QueryDB"
422
+ description = "Execute a query against the dev database"
423
+ parameters = QueryParams
424
+
425
+ async def execute(self, params: QueryParams, signal=None) -> dict:
426
+ result = run_query(params.query) # your logic here
427
+ return {"content": [{"type": "text", "text": result}], "is_error": False}
428
+
429
+ agent = Agent(extra_tool_classes=[LiveDBTool], tool_names=["QueryDB"])
430
+ ```
431
+
432
+ ## Reference
433
+
434
+ ### Commands
435
+
436
+ | Command | Description |
437
+ |---|---|
438
+ | `/help` | Show command reference |
439
+ | `/clear [--config F]` | Clear conversation; `--config` reloads agent from a JSON/YAML file |
440
+ | `/history [--raw]` | Show conversation transcript; `--raw` shows the raw API message log |
441
+ | `/diff [--all]` | Show a side-by-side diff of changes in the worktree |
442
+ | `/errors` | Show timestamped error log for the current tab |
443
+ | `/tools` | List active tools |
444
+ | `/branch [--rev N] [prompt]` | Open a new branch tab from the current (or earlier) checkpoint |
445
+ | `/abort` | Abort the running agent |
446
+ | `/export [path]` | Export session to JSON |
447
+ | `/exit` or `/quit` | Close branch tab, or exit the app |
448
+ | `!command` | Run a shell command; output captured in the TUI |
449
+ | `!!command` | Run an interactive command (TUI suspends, terminal handed to process) |
450
+
451
+ ### Key bindings
452
+
453
+ | Key | Action |
454
+ |---|---|
455
+ | `Enter` | Submit |
456
+ | `Ctrl-J` | Insert newline |
457
+ | `Alt-1` … `Alt-9` | Jump to tab N |
458
+ | `Tab` / `Shift-Tab` | Cycle through tabs |
459
+ | `Ctrl-C` | Abort running agent |
460
+ | `Ctrl-D` | Close branch tab, or exit app |
461
+ | `Ctrl-R` | Recall last prompt into editor |
462
+
463
+ ### Tools
464
+
465
+ | Tool | What it does |
466
+ |------|-------------|
467
+ | `Read` | Read file with optional offset/limit for large files |
468
+ | `Write` | Create or overwrite a file |
469
+ | `Edit` | Replace an exact unique string in a file |
470
+ | `Bash` | Run a shell command with timeout and abort support |
471
+ | `Grep` | Search files by pattern, respects .gitignore |
472
+ | `Find` | Find files/dirs by name glob, respects .gitignore |
473
+ | `Ls` | List directory contents, respects .gitignore |
474
+ | `Skill` | Retrieve named skill instructions from config |
475
+ | `Agent` | Spawn an autonomous sub-agent for delegated work |
476
+
477
+ All file tools enforce path sandboxing — the agent cannot read or write outside the worktree.
478
+
479
+ ### Backends
480
+
481
+ | Backend | Flag | Notes |
482
+ |---------|------|-------|
483
+ | MLX (local) | `--api noapi` | Default. Runs on-device, no API key needed |
484
+ | Claude | `--api claude` | Requires `ANTHROPIC_API_KEY` |
485
+ | Gemini | `--api gemini` | Requires `GOOGLE_API_KEY` |
486
+ | DeepSeek | `--api deepseek` | DeepSeek API or compatible endpoint |
487
+ | Codex | `--api codex` | OpenAI Codex CLI integration |
488
+ | OpenAI | `--api openai` | Any OpenAI-compatible endpoint |
489
+
490
+ ### Frontends
491
+
492
+ The local MLX server speaks OpenAI, Anthropic, and Gemini wire formats simultaneously, so you can use any compatible CLI as the frontend:
493
+
494
+ ```bash
495
+ mlc --leash claude # claude CLI routes through local model
496
+ mlc --leash codex # codex CLI routes through local model
497
+ mlc --leash gemini # gemini CLI routes through local model
498
+ mlc --leash none # server only
499
+ ```
500
+
501
+ ---
502
+
503
+ ## Credits
504
+
505
+ Built on [mlx](https://github.com/ml-explore/mlx) and [mlx-lm](https://github.com/ml-explore/mlx-lm). Inspired by Mario Zechner's [pi](https://github.com/badlogic/pi-mono).
506
+
507
+ ## License
508
+
509
+ Apache License 2.0: see [LICENSE](LICENSE) for details.