smolcoder 0.4.1 → 0.4.3

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.
package/README.md CHANGED
@@ -1,196 +1,115 @@
1
1
  # smolcoder
2
2
 
3
- A smol, zero-config CLI coding agent for **local models**. Ollama and LM Studio only — and because it supports only those, it can make them first-class: no base URLs, no API keys, no config files, no setup questions. Start it and code.
3
+ A smol coding agent for the models already running on your machine.
4
+
5
+ If you have Ollama or LM Studio installed, you are two commands away from an AI pair programmer that reads your code, edits files, runs your tests and starts your dev server, all without an API key, a config file, or a single question about base URLs.
4
6
 
5
7
  ```bash
6
8
  npm install -g smolcoder
7
9
  smol
8
10
  ```
9
11
 
10
- Works on Windows, macOS, and Linux. Zero runtime dependencies.
12
+ That is the whole setup. smolcoder finds your local server, lists the models you already have, and drops you into a session.
11
13
 
12
- ## The TUI
14
+ ## Why you might like it
13
15
 
14
- smol opens straight into a session the last model you used (or the first one detected) is picked automatically. Everything is changed in-session:
16
+ Most coding agents are built for big cloud models and treat local ones as an afterthought. They ship huge system prompts, dozens of tools and plugin systems, then wonder why a 7B model with a 16k window gets lost. smolcoder goes the other way.
15
17
 
16
- - **`/` slash commands** with an autocomplete menu: `/models` (switch model, type to filter), `/mode`, `/effort`, `/tasks`, `/compact`,
17
- - **shift+tab** cycles read-only edit bypass permissions
18
- - **esc** cancels a running turn; **ctrl+c ×2** quits
19
- - The status line under the input shows mode · model · effort · context fill · running background tasks, live.
20
- - `/effort` maps to Ollama's `think` parameter and LM Studio's `reasoning_effort`. `off` disables thinking on qwen3-class models a real speedup. Models that don't support it fall back silently.
18
+ - **Zero config.** It probes the standard Ollama and LM Studio ports and uses whatever it finds. Docker-hosted Ollama with the usual port mapping works too.
19
+ - **Built for small context windows.** A two-paragraph system prompt, seven flat tools, and hard caps on every tool output. Nothing else competes for the model's attention.
20
+ - **Context handled for you.** It reads the real token counts from the backend, shows a live context meter, and compacts old tool output before it ever has to summarise your conversation.
21
+ - **Small-model-friendly tools.** Every tool has an example call in its description, error messages coach the model toward the fix, and the edit tool forgives whitespace drift. That last one is the difference between a local model that can edit files and one that cannot.
22
+ - **A plan the model cannot lose.** For multi-step work the agent keeps a checklist that lives in the harness, not the transcript, so it survives compaction and keeps the model on track.
23
+ - **Runs anywhere Node runs.** Windows, macOS and Linux. Zero runtime dependencies.
21
24
 
22
- For scripts and automations there is a headless mode that prints the transcript and exits: `smol -p "prompt"` (reasoning noise suppressed, exit code reflects success).
25
+ ## Setup
23
26
 
24
- ## Why
27
+ You need two things.
25
28
 
26
- Most coding harnesses treat local models as an afterthought: you configure endpoints by hand, and then they inject huge system prompts, dozens of tools, MCP servers and skills into a model with a small context window. smolcoder is built the other way around:
29
+ 1. **Node.js 18 or newer.** Get it from [nodejs.org](https://nodejs.org) if you do not have it.
30
+ 2. **A local model server**, either:
31
+ - [Ollama](https://ollama.com) with a tool-capable model pulled, for example `ollama pull qwen3`, or
32
+ - [LM Studio](https://lmstudio.ai) with a model loaded and its local server running (Developer tab, then Start Server).
27
33
 
28
- - **Zero config.** Probes the standard Ollama (`127.0.0.1:11434`, or `$OLLAMA_HOST`) and LM Studio (`127.0.0.1:1234`) endpoints and lists whatever models you already have. Docker-hosted Ollama with the usual port mapping is picked up automatically.
29
- - **Tiny context footprint.** A two-paragraph system prompt, exactly seven flat tools, hard caps on every tool output, and no MCP, no skills, no subagents.
30
- - **Context windows handled properly.** For Ollama, smolcoder respects the server's own configured context length (the Ollama app's setting) — it preloads the model and reads the effective window from `/api/ps`, sending an explicit `num_ctx` only on old Ollama versions where the silent tiny default would truncate prompts, or when you pass `--ctx`. For LM Studio it reads the loaded context length from `/api/v0/models` and budgets within it. Real token usage reported by the backend drives a live context meter and automatic compaction (old tool output is evicted first — nearly free — and the conversation is summarized only when that's not enough).
31
- - **Small-model-friendly tools.** Flat string parameters, an example call in every description, and error messages written as coaching (a failed edit shows the closest real snippet to copy). The edit tool forgives whitespace drift — the difference between usable and unusable local editing.
34
+ Then install and start:
32
35
 
33
- ## Modes
36
+ ```bash
37
+ npm install -g smolcoder
38
+ cd your-project
39
+ smol
40
+ ```
34
41
 
35
- | Mode | Files | Commands |
36
- |------|-------|----------|
37
- | `ro` (read-only) | read/search only | none |
38
- | `edit` (default) | read/write/edit | runs freely inside the workspace (`npm install`, tests, scripts); a command that reaches outside it asks y/n (or **a**lways-allow that program for the session) |
39
- | `bypass` (bypass permissions) | read/write/edit | never asks for approval |
42
+ Prefer not to install anything globally? `npx smolcoder` works too.
40
43
 
41
- The mode decides which tools *exist* — in read-only mode the model is never even told a write tool exists. File tools are sandboxed to the workspace folder (symlink escapes included). Commands run with the workspace as their working directory, and in edit mode the command text is scanned before it runs: absolute paths outside the workspace, `/tmp`, `~`, temp-dir variables, `..` climbing past the root, and global package installs all trigger the approval prompt (with the reason shown). This is a best-effort text scan, not an OS sandbox — a command can still reach outside through, say, a script it runs — so use read-only mode for untrusted work and bypass only when you want no prompts at all.
44
+ ## Your first session
42
45
 
43
- ## The plan a compass for small models
46
+ smolcoder opens straight into a chat with the last model you used, or the first one it detects. Type what you want done and press enter. The agent reads files, makes edits, and runs commands inside your project folder, telling you what it is doing as it goes.
44
47
 
45
- For multi-step tasks the agent keeps a to-do list via a `plan` tool (`set` / `done` / `add` / `show`). It's not a gimmick copied from the big harnesses — it's built for small context windows:
48
+ A few keys worth knowing from the start:
46
49
 
47
- - The list lives in the **harness**, not in a file or the transcript, so rendering it costs zero tokens and **compaction can never destroy it** — after every compaction the checklist is re-injected, so the model wakes up looking at its map.
48
- - Every `done` result answers "what's next" in ~10 tokens, continuously re-focusing the model.
49
- - If the model tries to stop with steps unfinished, the harness pushes back once — attacking the classic local-model failure of quitting halfway.
50
- - You see it live: a checklist block in the TUI whenever it changes, `plan 2/4` in the status bar, `/plan` to reprint it, and checklist updates on stderr in headless runs.
50
+ | Key | What it does |
51
+ |---|---|
52
+ | `/` | Opens the slash-command menu with autocomplete |
53
+ | `shift+tab` | Cycles the permission mode: read-only, edit, bypass |
54
+ | `esc` | Cancels the running turn, or clears the input |
55
+ | `ctrl+c` twice | Quits |
51
56
 
52
- ## AGENTS.md memory
57
+ The status line under the input shows the current mode, model, reasoning effort, how full the context window is, and any background tasks you have running.
53
58
 
54
- If the workspace contains an `AGENTS.md`, its contents are injected right after the system prompt (size-capped at ~2k tokens) and survive compaction. Put your project conventions, commands, and quirks there.
59
+ ## Slash commands
55
60
 
56
- ## Tools the model gets
61
+ | Command | What it does |
62
+ |---|---|
63
+ | `/models` | Switch model. Type to filter the list. |
64
+ | `/mode` | Set the permission mode (`ro`, `edit`, `bypass`) |
65
+ | `/effort` | Set reasoning effort (`off`, `low`, `medium`, `high`, `default`) |
66
+ | `/plan` | Show the agent's current checklist |
67
+ | `/context` | Show context window usage |
68
+ | `/compact` | Compact the conversation now |
69
+ | `/tasks`, `/logs <id>`, `/stop <id>` | Inspect and stop background tasks such as dev servers |
70
+ | `/clear` | Start a fresh conversation |
71
+ | `/help`, `/exit` | Help and quit |
57
72
 
58
- `read_file` · `write_file` · `edit_file` · `list_files` · `search` · `plan` · `run_command` · `task`
73
+ ## Modes
59
74
 
60
- `task` manages background processes (dev servers, watchers): `start`, `list`, `logs`, `stop`. Background tasks are non-blocking, keep a ring buffer of recent output, show up in the status line, and are killed when smolcoder exits. You can inspect them yourself with `/tasks`, `/logs <id>`, `/stop <id>`.
75
+ The mode decides which tools the model even knows about. In read-only mode it is never told a write tool exists.
61
76
 
62
- ## Usage
77
+ | Mode | Files | Commands |
78
+ |---|---|---|
79
+ | `ro` | read and search only | none |
80
+ | `edit` (default) | read, write, edit | runs freely inside the project folder. Anything reaching outside it asks you first. |
81
+ | `bypass` | read, write, edit | never asks |
82
+
83
+ File tools are sandboxed to the project folder, symlinks included. In edit mode each command is scanned before it runs, and paths outside the folder, home-directory or temp-directory references, and global package installs all trigger an approval prompt that shows the reason. This is a text scan rather than an OS sandbox, so a script the model runs could still reach outside. Use read-only mode for code you do not trust, and bypass when you want no prompts at all.
84
+
85
+ ## Handy options
63
86
 
64
87
  ```bash
65
- smol # current folder, remembers your last model & mode
66
- smol path/to/project # a specific workspace
67
- smol --mode bypass # bypass permissions: no approval prompts
68
- smol --model qwen3 # pick a model by (partial) name
69
- smol --ctx 16384 # cap the context window (Ollama: sets num_ctx)
70
- smol -p "fix the failing test" # one-shot, non-interactive
88
+ smol # current folder, remembers your last model and mode
89
+ smol path/to/project # a specific project
90
+ smol --model qwen3 # pick a model by partial name
91
+ smol --effort off # no thinking: the fastest setting for long tool loops
92
+ smol --mode bypass # never ask for approval
93
+ smol --ctx 16384 # cap the context window
94
+ smol --web # also serve the session as a local web page
95
+ smol -p "fix the failing test" # headless: run one prompt, print the transcript, exit
71
96
  ```
72
97
 
73
- In a session: `/mode`, `/model`, `/context`, `/compact`, `/tasks`, `/logs <id>`, `/stop <id>`, `/clear`, `/help`, `/exit`. `Ctrl+C` cancels a running turn.
98
+ Headless mode is for scripts and automation. It suppresses reasoning noise and the exit code tells you whether the run succeeded.
74
99
 
75
- ## Backend notes: Ollama vs LM Studio
100
+ ## Tips
76
101
 
77
- Measured head-to-head with identical qwen3.8-27B Q4_K_M weights on one RTX 5090
78
- (the model is a hybrid recurrent `qwen35` build; Ollama 0.33, LM Studio 0.4.21):
102
+ **Turn thinking off for long tasks.** `/effort off` disables reasoning on qwen3-class models, which makes each step several times faster. Thinking helps with planning and tricky bugs, but for a twenty-step build the model rarely needs it. Models without a thinking switch fall back silently.
79
103
 
80
- | | Ollama | LM Studio |
81
- |---|---|---|
82
- | prompt processing (6.7k-token cold prompt) | ~3,300 tok/s | ~3,000–3,400 tok/s |
83
- | generation, 400 tokens of prose, thinking off | ~120 tok/s | ~68 (4 slots + MTP) / ~76 (1 slot, no MTP) tok/s |
84
- | generation, 1,200 tokens of JavaScript, thinking off | ~170 tok/s | ~110 (1 slot + MTP) / ~76 (no MTP) tok/s |
85
- | one agent step (plan + write call), thinking off | 0.9 s | 1.5–1.8 s |
86
- | one agent step with thinking, same prompt | 0.8–10 s (38–3,500 reasoning chars) | 1.8–85 s (250–19,000 reasoning chars) |
87
-
88
- The engine is the same llama.cpp on both, so the differences come from what the
89
- harness sends and from load settings:
90
-
91
- - **Reasoning effort was the whole story behind "LM Studio is slow".** LM Studio's
92
- API takes `reasoning_effort` none/minimal/low/medium/high/xhigh, but each model only
93
- supports some of them and a value the model lacks is silently replaced by the
94
- model's *default* — which for current qwen3.x builds is **xhigh**, the maximum.
95
- Asking for `high` therefore produced 8,000-token thinking bursts before single
96
- tool calls. smolcoder now reads the model's supported levels and default from
97
- `/api/v1/models`, sends `none` for `off` (measured: fully disables thinking), and
98
- snaps other levels to the nearest one the model has (`high` → `medium` on qwen;
99
- ties go to the cheaper level). The status line shows the mapping (`high → medium`,
100
- `default → xhigh`), and a warning is printed when the default is the maximum.
101
- On Ollama, `off` is a real `think: false` and any other level is `think: true`
102
- (levels only exist for gpt-oss there).
103
- - **Thinking is unpredictable on local models.** The same prompt at the same level
104
- thought for 250 characters one run and 19,000 the next. For long tool loops,
105
- `--effort off` is the reliable setting on both backends; `low`/`medium` are fine
106
- for questions and planning.
107
- - **Generation is ~1.5× faster on Ollama** with default settings (Ollama also
108
- drafts 4 tokens per step with the model's MTP head; LM Studio drafts 2). On LM
109
- Studio, keep MTP speculative decoding ON for coding — it took code generation from
110
- 76 to ~110 tok/s (it slightly slows prose, which is what most benchmarks measure) —
111
- and load with a single slot, which is another ~10%:
112
- `lms load <model> --context-length 65536 --parallel 1 --speculative-draft-mtp`.
113
- Prompt caching works on both (only the new tail of the prompt is processed).
114
- - **Ollama keeps the model resident** for 30 minutes between calls (its own default
115
- unloads after 5 min — a long approval pause used to cost a 10–20 s reload).
116
- Override with `SMOLCODER_KEEP_ALIVE=1h`.
117
- - **Reasoning traces are not replayed** for finished turns (the qwen templates drop
118
- them anyway); only the current turn's traces travel with the tool loop. On a
119
- thinking model this is the largest single prompt-size saving.
120
-
121
- ## End-to-end: the same Minecraft build on both backends
122
-
123
- One headless run each (`smol -p "<prompt>" --mode bypass --effort off`), same
124
- model weights, same prompt (procedural voxel terrain, first-person controls, block
125
- place/remove, three.js from a CDN, then serve it). Nobody typed "continue".
126
-
127
- | | Ollama | LM Studio (1 slot, MTP on) |
128
- |---|---|---|
129
- | wall clock | 64 s | 85 s |
130
- | tool calls | 20 (5 files) | 14 (3 files) |
131
- | tokens generated | 9.4k @ 171 tok/s | 7.4k @ 96 tok/s |
132
- | plan | 6/6 done | 3/3 done |
133
- | syntax warnings from the write hook | 0 | 0 |
134
- | result in the browser | loads, no console errors; one mesh-winding bug | loads, no console errors; terrain not visible |
135
-
136
- Both agents finished on their own, checked their files with `node --check`, and
137
- started a static server to prove the page served. Reproduce with `bench/run.sh`
138
- (the prompt is `bench/minecraft-prompt.txt`; every headless run ends with a
139
- `[stats]` JSON line on stderr). Both first drafts had one real bug
140
- (this is a 27B model writing a voxel engine with thinking off); each was fixed with
141
- a second headless turn carrying a one-paragraph, symptom-only bug report:
142
-
143
- | fix turn | Ollama | LM Studio |
144
- |---|---|---|
145
- | wall clock | 42 s | 100 s |
146
- | tool calls | 27 (reads, searches, 3 edits, node one-liners) | 27 (7 reads, 3 edits, 10 commands, 6 plan) |
147
- | tokens generated | 4.8k @ 165 tok/s | 6.9k @ 79 tok/s |
148
- | outcome | solid terrain; face vertex order fixed | terrain renders; spawn height + camera pitch fixed |
104
+ **Give the agent a memory.** If your project has an `AGENTS.md` file, its contents are injected after the system prompt and survive compaction. Put your conventions, commands and quirks there.
149
105
 
150
- ### The same build with reasoning on (`--effort high`)
106
+ **Let it run things in the background.** The agent can start dev servers and watchers as background tasks, check their logs, and stop them. They show up in the status line and are killed when smolcoder exits.
151
107
 
152
- `high` resolves to `medium` on this LM Studio model (its levels are off/low/medium/xhigh;
153
- ties snap to the cheaper neighbour) and to `think: true` on Ollama.
108
+ **Curious about the numbers?** Benchmarks, Ollama versus LM Studio tuning notes, and what happens when the context window is squeezed are all in [docs/backend-notes.md](docs/backend-notes.md).
154
109
 
155
- | | Ollama (`think: true`) | LM Studio (`high → medium`) |
156
- |---|---|---|
157
- | wall clock | 84 s | 289 s (about a third of it a port-collision detour caused by the test setup) |
158
- | tool calls | 16 | 28 |
159
- | tokens generated | 10.6k @ 146 tok/s, ~2.6k of them reasoning | 22.5k @ 84 tok/s, ~12.2k of them reasoning |
160
- | plan | 4/4 | 5/5 |
161
- | result in the browser | correct on the first try: terrain, hills, controls, no console errors | correct on the first try: terrain, hills, controls, no console errors |
162
-
163
- Reasoning bought correctness: both first drafts worked, where both effort-off drafts had
164
- needed a bug-fix turn. The price is time — the model's per-step thinking is where the
165
- backends differ most (Ollama's `think: true` produced a quarter as many reasoning
166
- tokens as LM Studio's `medium`), and LM Studio's default `xhigh` would have been far
167
- slower still. Note that the two effort-off runs plus their fix turns (106 s on Ollama,
168
- 185 s on LM Studio) still beat the reasoning runs on wall clock.
169
-
170
- ### Forcing compaction
171
-
172
- The same build with the window capped at 12k tokens (`--ctx 12000`, output budget
173
- 3k) is the stress test for everything above: Ollama finished the whole game in
174
- 104 s and 28 tool calls with context management kicking in five times, the plan
175
- reported 5/5, and the page rendered. On LM Studio the same run went through a tier-1
176
- eviction (7.4k → 5.1k tokens) and then a real tier-2 compaction (7.5k → 2.1k tokens:
177
- system prompt + plan + model-written hand-over notes + the working tail), after which
178
- the model carried on with the remaining steps — syntax check, serve, summarize. (That
179
- LM Studio build parses and serves but throws a runtime TypeError on load — a file
180
- assembled in five pieces under a 3k output cap is where a 27B model starts to slip,
181
- and a parse check cannot catch runtime errors.) Two things made the runs work at all:
182
-
183
- - when a whole-file `write_file` overflows the output cap, the model is told the call
184
- was **not executed** and coached to write the file in parts (first part with
185
- `write_file`, then `edit_file` appends) — before that coaching existed the model
186
- retried the same oversized write three times in a row;
187
- - the plan and the hand-over notes are re-injected after every compaction, so the
188
- model resumes at the right step instead of starting over.
189
-
190
- ## Requirements
191
-
192
- - Node.js 18+
193
- - [Ollama](https://ollama.com) with at least one tool-capable model pulled (e.g. `ollama pull qwen3`), **or** LM Studio with its local server running (Developer tab → Start Server).
110
+ ## Contributing
111
+
112
+ Issues and pull requests are welcome at [github.com/leonvanzyl/smolcoder](https://github.com/leonvanzyl/smolcoder). Clone it, run `npm install`, then `npm test`.
194
113
 
195
114
  ## License
196
115
 
package/dist/index.js CHANGED
@@ -256,12 +256,12 @@ function sessionLine(m, mode) {
256
256
  }
257
257
  const MODE_ORDER = ["ro", "edit", "bypass"];
258
258
  const LOGO_ROWS = [
259
- "████████╗ ██╗ ███╗ ██╗ ██╗ ██╗",
260
- "╚══██╔══╝ ██║ ████╗ ██║ ╚██╗ ██╔╝",
261
- " ██║ ██║ ██╔██╗ ██║ ╚████╔╝ ",
262
- " ██║ ██║ ██║╚██╗██║ ╚██╔╝ ",
263
- " ██║ ██║ ██║ ╚████║ ██║ ",
264
- " ╚═╝ ╚═╝ ╚═╝ ╚═══╝ ╚═╝ ",
259
+ "███████╗ ███╗ ███╗ ██████╗ ██╗ ",
260
+ "██╔════╝ ████╗ ████║ ██╔═══██╗ ██║ ",
261
+ "███████╗ ██╔████╔██║ ██║ ██║ ██║ ",
262
+ "╚════██║ ██║╚██╔╝██║ ██║ ██║ ██║ ",
263
+ "███████║ ██║ ╚═╝ ██║ ╚██████╔╝ ███████╗",
264
+ "╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝",
265
265
  ];
266
266
  function printLogo() {
267
267
  const cols = process.stdout.columns || 80;
package/dist/web/page.js CHANGED
@@ -88,12 +88,12 @@ exports.PAGE_HTML = `<!doctype html>
88
88
  </head>
89
89
  <body>
90
90
  <div id="wrap">
91
- <div id="logo">████████╗██╗███╗ ██╗██╗ ██╗
92
- ╚══██╔══╝██║████╗ ██║╚██╗ ██╔╝
93
- ██║ ██║██╔██╗ ██║ ╚████╔╝
94
- ██║ ██║██║╚██╗██║ ╚██╔╝
95
- ██║ ██║██║ ╚████║ ██║
96
- ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ <span class="coder">coder — web</span></div>
91
+ <div id="logo">███████╗ ███╗ ███╗ ██████╗ ██╗
92
+ ██╔════╝ ████╗ ████║ ██╔═══██╗ ██║
93
+ ███████╗ ██╔████╔██║ ██║ ██║ ██║
94
+ ╚════██║ ██║╚██╔╝██║ ██║ ██║ ██║
95
+ ███████║ ██║ ╚═╝ ██║ ╚██████╔╝ ███████╗
96
+ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ <span class="coder">coder — web</span></div>
97
97
  <div id="log"></div>
98
98
  <div id="busy"><span class="spin">⠋</span> <span id="busylabel">thinking…</span> <span id="busysecs"></span></div>
99
99
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smolcoder",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "A smol, zero-config CLI coding agent for local models (Ollama & LM Studio). It just works.",
5
5
  "keywords": [
6
6
  "cli",