kronk-cli 0.1.0

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 ADDED
@@ -0,0 +1,712 @@
1
+ # kronk-cli
2
+
3
+ A Claude-Code-style terminal agent for local models served by
4
+ [Kronk](https://github.com/ardanlabs/kronk). Reads your files, runs your commands, writes code —
5
+ entirely on your machine. No network, no API key, no per-token cost.
6
+
7
+ ```
8
+ › refactor the SSE parser in src/client.js to handle multi-line data fields
9
+
10
+ ┄ thinking ┄
11
+ The parser splits on newlines and only handles `data: ` prefixes…
12
+ ┄─────────┄
13
+
14
+ 1 ⚙ read src/client.js
15
+ ✓ 118 lines
16
+ 2 ⚙ write src/client.js
17
+ + export async function* streamChat({ model, messages, tools, signal }) {
18
+ …42 more lines
19
+ approve write_file? [y/N] y
20
+ ✓ 1 lines
21
+
22
+ Done — the parser now buffers continuation lines before dispatching.
23
+ 2104→812 tok · 61.3 tok/s · ttft 240ms · 1980 cached
24
+ ```
25
+
26
+ **Zero dependencies.** It is `fetch` and `readline` against Kronk's OpenAI-compatible API.
27
+
28
+ ---
29
+
30
+ ## Requirements
31
+
32
+ - Node 20+
33
+ - A running Kronk server with at least one chat model
34
+
35
+ ```bash
36
+ kronk model pull unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M
37
+ kronk server start --detach
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Install
43
+
44
+ ### From source — recommended while you're editing it
45
+
46
+ ```bash
47
+ git clone https://github.com/you/kronk-cli && cd kronk-cli
48
+ npm link
49
+ ```
50
+
51
+ `npm link` symlinks the repo into your global `bin`, so `kronk-cli` works from any directory
52
+ **and your edits take effect immediately**:
53
+
54
+ ```console
55
+ $ which kronk-cli
56
+ /Users/you/.nvm/versions/node/v24.13.0/bin/kronk-cli
57
+
58
+ $ ls -l $(which kronk-cli)
59
+ … -> ../lib/node_modules/kronk-cli/src/index.js
60
+ ```
61
+
62
+ ### Uninstall
63
+
64
+ ```bash
65
+ npm unlink -g kronk-cli # removes the global symlink
66
+ npm unlink # run inside the repo, clears the local link
67
+ ```
68
+
69
+ Verify it's gone with `which kronk-cli` — no output means success.
70
+
71
+ ### Other ways
72
+
73
+ ```bash
74
+ npm install -g kronk-cli # from the registry
75
+ npm install -g . # from source, COPIES — you must redo it after every edit
76
+ npx kronk-cli # no install
77
+ node src/index.js # no install, from the repo
78
+ ```
79
+
80
+ ### Using nvm?
81
+
82
+ Global bins live **inside the active Node version**, so switching versions hides the command.
83
+
84
+ ```bash
85
+ nvm alias default v24.13.0 # pin, and stay on it
86
+ npm link # …or re-link under each version you use
87
+ ```
88
+
89
+ To make it survive version switches entirely, skip npm and symlink somewhere neutral:
90
+
91
+ ```bash
92
+ ln -s "$PWD/src/index.js" ~/.local/bin/kronk-cli
93
+ ```
94
+
95
+ ### Scope
96
+
97
+ The agent roots itself at **the directory you launch it from**. That becomes its sandbox — file
98
+ tools cannot read or write outside it, and `bash` cannot `cd` out of it. `cd` into a project first.
99
+
100
+ ---
101
+
102
+ ## Project awareness
103
+
104
+ On startup `kronk-cli` scans the directory once and puts a primer in the system prompt, so the
105
+ model knows where it is before it calls a single tool:
106
+
107
+ - working directory and platform
108
+ - git branch, uncommitted files, last 5 commits
109
+ - top-level layout (skipping `node_modules`, `dist`, `.git`, …)
110
+ - the contents of the first **agent file** it finds:
111
+ `AGENTS.md`, `CLAUDE.md`, `KRONK.md`, `.cursorrules`, `CONVENTIONS.md`
112
+
113
+ The REPL confirms what it picked up:
114
+
115
+ ```console
116
+ $ kronk-cli
117
+
118
+ ██ kronk-cli · local agent, no network
119
+ model unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT
120
+ server http://localhost:11435/v1
121
+
122
+ context ~/Projects/api · git · AGENTS.md
123
+ ```
124
+
125
+ Which means this needs no tools at all:
126
+
127
+ ```console
128
+ $ kronk-cli "what branch am I on and what indentation does this project use?"
129
+
130
+ - Branch: main, 1 uncommitted file (src/more.js)
131
+ - Indentation: tabs, never spaces — per AGENTS.md
132
+ ```
133
+
134
+ An `AGENTS.md` is the way to give the model standing instructions. It is read on every run, so
135
+ conventions stick without you restating them:
136
+
137
+ ```markdown
138
+ # Project conventions
139
+
140
+ - This project uses tabs, never spaces.
141
+ - Every exported function needs a JSDoc block.
142
+ - Never add dependencies without asking.
143
+ ```
144
+
145
+ The primer costs roughly 150–800 tokens and sits at the front of the system message, so Kronk's
146
+ prompt cache reuses it across every turn. Disable it with `--no-context`.
147
+
148
+ > It is a **primer, not a preload** — the model still reads files with `read_file` when it needs
149
+ > their contents. Nothing beyond the listing and the agent file is sent up front.
150
+
151
+ ---
152
+
153
+ ## Usage
154
+
155
+ ```bash
156
+ kronk-cli # interactive REPL
157
+ kronk-cli "explain src/agent.js" # one shot, prints and exits
158
+ git diff | kronk-cli "review this diff" # stdin as extra context
159
+ git log --oneline -20 | kronk-cli # stdin as the whole prompt
160
+ kronk-cli --auto "make the tests pass" # unattended, runs the whole task
161
+ ```
162
+
163
+ ---
164
+
165
+ ## Command-line options
166
+
167
+ | Flag | Default | |
168
+ |---|---|---|
169
+ | `-m`, `--model <id>` | `unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT` | Model to use. A substring is enough; `/AGENT` profiles win ties |
170
+ | `-l`, `--models`, `--list` | — | List the models Kronk is serving, then exit |
171
+ | `--no-context` | off | Skip the startup scan of the working directory |
172
+ | `--no-compact` | off | Never auto-compact; fail when the window fills instead |
173
+ | `--mcp [names]` | off | Attach MCP servers — bare for all, or a comma list |
174
+ | `--mcp-list` | — | Show configured MCP servers and their tools, then exit |
175
+ | `-a`, `--auto` | off | Autonomous: auto-approve tools **and** run until the task is done. Implies `--yes` |
176
+ | `-y`, `--yes` | off | Auto-approve `write_file` and `bash` without the autonomous prompt |
177
+ | `--no-think` | off | Disable the model's reasoning pass server-side. Much faster |
178
+ | `--steps <n>` | unlimited | Cap tool calls per task. `0`, `off`, `none`, `inf`, `unlimited` all mean no cap |
179
+ | `-h`, `--help` | — | Print all options and exit |
180
+
181
+ Anything not consumed as a flag becomes the prompt. With both an inline prompt and piped stdin,
182
+ the two are concatenated.
183
+
184
+ `Ctrl-C` aborts the in-flight response and the tool loop without killing the session.
185
+
186
+ ---
187
+
188
+ ## The status line
189
+
190
+ Above every prompt, reflecting current state rather than what you launched with:
191
+
192
+ ```
193
+ ⏵ AGENT · auto · no-think · mcp nx,kronk · steps 50 · 22k/131k 17% ▓▓░░░░░░░░
194
+
195
+ ```
196
+
197
+ Model, active modes, attached MCP servers, any step cap, and the context meter — grey under 70%,
198
+ yellow past 70%, red past 90%. Toggling `/auto`, `/think` or `/steps` updates it immediately.
199
+
200
+ The per-turn usage line still prints after each response; this one is the running picture.
201
+
202
+ ---
203
+
204
+ ## REPL commands
205
+
206
+ | Command | |
207
+ |---|---|
208
+ | `/help` | list these commands |
209
+ | `/models` | what Kronk is serving, with sizes and what is resident |
210
+ | `/model <id>` | switch model — substring match, `/AGENT` preferred |
211
+ | `/file <path>` | add a file to the conversation as context |
212
+ | `/auto` | toggle autonomous mode (auto-approve + run to completion) |
213
+ | `/steps [n\|off]` | show or set the tool-call cap |
214
+ | `/thinking` | show or hide the model's reasoning |
215
+ | `/think` | turn reasoning off entirely — much faster |
216
+ | `/mcp` | list attached MCP servers and their tools |
217
+ | `/context` | how much of the context window is used |
218
+ | `/compact` | replace the conversation with a summary of itself |
219
+ | `/clear` | reset the conversation, keep the model |
220
+ | `/exit`, `/quit` | quit |
221
+
222
+ ---
223
+
224
+ ## Environment variables
225
+
226
+ | Variable | Default | |
227
+ |---|---|---|
228
+ | `KRONK_URL` | `http://localhost:11435/v1` | Kronk API base |
229
+ | `KRONK_TOKEN` | `kronk` | Any non-empty value while Kronk runs open; a real JWT when protected |
230
+ | `KRONK_MODEL` | `unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT` | Model id |
231
+ | `KRONK_MAX_TOKENS` | `8192` | Output cap per response |
232
+ | `KRONK_MAX_STEPS` | unlimited | Cap on tool calls per task |
233
+ | `KRONK_THINKING` | `true` | `false` hides reasoning but still generates it |
234
+ | `KRONK_NO_THINK` | — | `1` disables reasoning server-side |
235
+ | `KRONK_TOOL_TIMEOUT` | `900` | Seconds before a shell command is killed |
236
+ | `KRONK_DISTILL` | `true` | `false` disables tool-output distillation |
237
+ | `KRONK_DISTILL_AT` | `8000` | Characters of output that trigger distillation |
238
+ | `KRONK_AUTO_COMPACT` | `true` | `false` disables automatic compaction |
239
+ | `KRONK_COMPACT_AT` | `0.85` | Fraction of the window that triggers compaction |
240
+ | `NO_COLOR` | — | Any value disables colour |
241
+
242
+ ### Config file
243
+
244
+ `~/.kronk-cli.json`. Command line beats environment beats this file.
245
+
246
+ ```json
247
+ {
248
+ "baseUrl": "http://localhost:11435/v1",
249
+ "token": "kronk",
250
+ "model": "unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT",
251
+ "maxTokens": 16384,
252
+ "maxSteps": 200,
253
+ "showThinking": false,
254
+ "autoCompact": true,
255
+ "compactAt": 0.85,
256
+ "noThink": true
257
+ }
258
+ ```
259
+
260
+ ---
261
+
262
+ ## Choosing a model
263
+
264
+ Resolution order:
265
+
266
+ 1. `-m` / `--model`
267
+ 2. `KRONK_MODEL`, or `model` in `~/.kronk-cli.json`
268
+ 3. `unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT`, if Kronk is serving it
269
+ 4. otherwise the largest chat model available, preferring an `/AGENT` profile
270
+
271
+ A substring is enough — `-m Qwen3.6` resolves to the full id. An unrecognised value warns and
272
+ falls back rather than failing.
273
+
274
+ ### Listing models
275
+
276
+ ```console
277
+ $ kronk-cli --models
278
+
279
+ ○ unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M 22.1 GB · vision
280
+ ● unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT 22.1 GB · vision · loaded 28.1 GB
281
+
282
+ resident: 28.1 GB
283
+ default: unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT
284
+ select: kronk-cli -m <substring>
285
+ ```
286
+
287
+ `●` is the model you will get. `loaded` means it is resident in Kronk's pool, and how much
288
+ memory it holds.
289
+
290
+ > ⚠️ Profiles share one file **on disk** but are **separate resident copies in RAM**. Asking for
291
+ > the base id while `/AGENT` is loaded pulls a second 22 GB instance. Pick one and stay on it.
292
+ > Free a stray one with:
293
+ > ```bash
294
+ > curl -X POST localhost:11435/v1/kronk/models/unload \
295
+ > -H 'Content-Type: application/json' -d '{"id":"<model-id>"}'
296
+ > ```
297
+
298
+ ### Tip: use an `/AGENT` profile
299
+
300
+ Kronk lets one GGUF serve several runtime configurations. Add this to
301
+ `~/.kronk/models/model_config.yaml` and restart the server:
302
+
303
+ ```yaml
304
+ version: 1
305
+ models:
306
+ unsloth/Qwen3.6-35B-A3B-UD-Q4_K_M/AGENT:
307
+ context-window: 131072
308
+ nseq-max: 2
309
+ sampling-parameters:
310
+ temperature: 0.6
311
+ top_k: 20
312
+ top_p: 0.95
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Context window
318
+
319
+ Kronk reports the **effective** window for whichever model id you selected — that comes from
320
+ `context-window` in `model_config.yaml`, so a `/AGENT` profile and its base model can differ.
321
+
322
+ Three places surface it:
323
+
324
+ **The banner**
325
+
326
+ ```
327
+ context ~/Projects/api · git · AGENTS.md · 131k ctx
328
+ ```
329
+
330
+ **Every usage line**, as a meter that fills as the conversation grows:
331
+
332
+ ```
333
+ 18471→57 tok · 69.0 tok/s · ttft 397ms · 18260 cached 18k/131k 14% ▓░░░░░░░░░
334
+ ```
335
+
336
+ Grey under 70%, yellow past 70%, red past 90%.
337
+
338
+ **`/context`**, on demand:
339
+
340
+ ```console
341
+ › /context
342
+ 3.8k/131k 3% ░░░░░░░░░░
343
+ window: 131,072 tokens · model supports up to 262,144
344
+ messages: 2
345
+ ```
346
+
347
+ `model supports up to` is the model's trained maximum from its GGUF metadata. If it exceeds your
348
+ configured window, you can raise `context-window` in `~/.kronk/models/model_config.yaml` — at the
349
+ cost of KV-cache memory.
350
+
351
+ ### The model knows its own budget
352
+
353
+ The startup primer tells it, so you can plan work against the number without stating it:
354
+
355
+ > Your context window is 131,072 tokens, shared by everything in this conversation: these
356
+ > instructions, file contents you read, command output, and your own replies. When you plan work
357
+ > that must fit in one context, size it against that number and say what you assumed.
358
+
359
+ ```console
360
+ $ kronk-cli "how many tokens is your context window?"
361
+ 131,072 tokens
362
+ ```
363
+
364
+ That makes prompts like *"break this refactor into tickets, each sized to fit one context"*
365
+ resolve against a real number instead of a guess.
366
+
367
+ > ⚠️ It knows the **window**, not the live fill level — no model can see its own usage mid-turn.
368
+ > The meter is for you. If you need it to plan against remaining space, tell it what `/context`
369
+ > reports.
370
+
371
+ ### What happens at 100%
372
+
373
+ Kronk **rejects the request** — no silent truncation, no sliding window:
374
+
375
+ ```
376
+ 400 input tokens [40021] exceed context window [32768]
377
+ ```
378
+
379
+ Because history only grows, every later turn would fail the same way. So `kronk-cli` compacts.
380
+
381
+ ### Compaction
382
+
383
+ **`/compact`** replaces the conversation with a summary of itself, keeping the system message and
384
+ the project primer:
385
+
386
+ ```console
387
+ › /compact
388
+ compacted 18,412 → 1,204 tokens (−93%)
389
+ ```
390
+
391
+ The summary is written for the model, not for you — goal, decisions made **and rejected**, files
392
+ touched and what they now do, commands run and what they showed, what is outstanding.
393
+
394
+ **Automatic**, in two situations:
395
+
396
+ | Trigger | |
397
+ |---|---|
398
+ | Past `KRONK_COMPACT_AT` of the window (default **85%**) | compacts between turns |
399
+ | A `400 … exceed context window` | compacts and **retries the same turn once** |
400
+
401
+ ```console
402
+ context full — compacting and retrying
403
+ transcript too large for one pass — elided 92,415 chars from the middle
404
+ compacted 38,102 → 143 tokens (−100%)
405
+ ```
406
+
407
+ Disable with `--no-compact` or `KRONK_AUTO_COMPACT=false` if you would rather see the failure.
408
+
409
+ **Details worth knowing**
410
+
411
+ - Tool messages are **dropped**, not carried over — they are only valid beside the assistant
412
+ `tool_calls` that produced them, and a partial carry leaves orphaned `tool_call_id`s that the
413
+ API rejects. The summary is what survives.
414
+ - The summarizer runs against the same window that just overflowed, so an oversized transcript is
415
+ first trimmed from the **middle** — the goal sits at the start, current state at the end.
416
+ - If a summary comes out no shorter than the original, the conversation is left alone.
417
+ - **Compaction is lossy.** Anything the summary omits is gone. `/context` before a long run, and
418
+ `/clear` when you switch tasks, both beat relying on it.
419
+
420
+ ---
421
+
422
+ ## Tools
423
+
424
+ | Tool | Approval | |
425
+ |---|---|---|
426
+ | `read_file` | — | Read a UTF-8 file |
427
+ | `list_dir` | — | List a directory |
428
+ | `search` | — | Regex search via ripgrep, falling back to grep |
429
+ | `write_file` | ✋ | Create or overwrite; shows a diff preview first |
430
+ | `bash` | ✋ | Run a command; shows it first |
431
+
432
+ `--yes` and `--auto` skip the prompts. Paths resolve against the session directory and cannot
433
+ escape the launch root. `bash` keeps its working directory **between calls**, so a bare `cd`
434
+ sticks the way it would in a real shell.
435
+
436
+ ---
437
+
438
+ ## MCP servers
439
+
440
+ `kronk-cli` is an MCP **client**. It speaks both transports — stdio for local servers, Streamable
441
+ HTTP for remote ones — with no extra dependency.
442
+
443
+ Off by default. Attach with `--mcp`:
444
+
445
+ ```bash
446
+ kronk-cli --mcp # everything configured
447
+ kronk-cli --mcp nx,kronk # just these
448
+ kronk-cli --mcp-list # what is configured, what connects, what it exposes
449
+ ```
450
+
451
+ ```console
452
+ $ kronk-cli --mcp nx "what does 'nx affected' do?"
453
+ mcp nx(1) · 1 tools
454
+ 1 ⚙ nx__nx_docs {"userQuery":"what does 'nx affected' do"}
455
+ ✓ 44 lines
456
+ `nx affected` identifies projects changed by a PR and runs tasks only on those…
457
+ ```
458
+
459
+ Tools are namespaced `server__tool` so they cannot collide with the built-ins, and `/mcp` lists
460
+ what is attached.
461
+
462
+ ### Where to put the config
463
+
464
+ Four files are read, later ones winning on name collision:
465
+
466
+ | File | Scope | |
467
+ |---|---|---|
468
+ | `~/.claude.json` | user | Claude Code's global `mcpServers`, reused as-is |
469
+ | `~/.claude.json` → `projects[cwd].mcpServers` | user, per-project | |
470
+ | **`./.mcp.json`** | **project** | **committed to the repo** |
471
+ | `~/.kronk-cli.json` | user | `mcpServers` key |
472
+ | `./.kronk-cli.json` | project | `mcpServers` key |
473
+
474
+ **Best practice: put project tooling in `./.mcp.json` and commit it.** A teammate cloning the
475
+ repo gets the same servers with no setup, and the config is versioned with the code that needs it.
476
+ Keep account-level services that carry your credentials — Jira, GitLab, cloud providers — in
477
+ `~/.claude.json` so they follow you between projects and never land in a repo.
478
+
479
+ ```jsonc
480
+ // .mcp.json — commit this
481
+ {
482
+ "mcpServers": {
483
+ "nx": { "type": "stdio", "command": "npx", "args": ["-y", "nx-mcp@latest"] },
484
+ "kronk": { "type": "http", "url": "http://localhost:9000/mcp" },
485
+ "postgres": {
486
+ "type": "stdio",
487
+ "command": "npx",
488
+ "args": ["-y", "@modelcontextprotocol/server-postgres"],
489
+ "env": { "DATABASE_URL": "postgres://localhost/dev" }
490
+ }
491
+ }
492
+ }
493
+ ```
494
+
495
+ Add `"disabled": true` to any entry to keep it in the file but out of the session.
496
+
497
+ ### Approval
498
+
499
+ MCP tools are third-party code, so anything whose name looks like a write — `create`, `update`,
500
+ `delete`, `apply`, `sync`, `run`, `deploy`, … — prompts before it runs. Read-only lookups do not,
501
+ because a prompt you always accept is not a safety control. `--yes` and `--auto` bypass both.
502
+
503
+ ### Keep the tool count down
504
+
505
+ Every attached tool goes into the request, and a local model's tool selection degrades well before
506
+ a frontier model's does. `--mcp` with everything configured can easily mean 35+ tools; `kronk-cli`
507
+ warns past 25. Name the two or three servers a task actually needs.
508
+
509
+ A server that fails to start is reported and skipped — the rest still work. Servers needing OAuth
510
+ (Atlassian, GitLab) return `401` here, since `kronk-cli` has no browser auth flow.
511
+
512
+ ---
513
+
514
+ ## Long-running commands
515
+
516
+ **While it runs**, a live line redraws in place — elapsed time, lines produced, and the last line
517
+ printed, so a ten-minute build shows movement instead of looking like a hang:
518
+
519
+ ```
520
+ 3 ⚙ bash: npx nx run-many -t lint
521
+ ⠹ 84s · 12,403 lines · →7.5k ctx 6% capped [42/97] Linting @acme/api…
522
+ ```
523
+
524
+ `→7.5k ctx 6%` is what this command will cost your context once it finishes — not what it has
525
+ printed. `capped` means it has already produced more than will be kept, so the number has stopped
526
+ climbing and the excess is being discarded rather than silently eating your window.
527
+
528
+ **Timeout** is 15 minutes (`KRONK_TOOL_TIMEOUT`, in seconds), not the 2 minutes that used to kill
529
+ real builds. The command runs in its own process group, so a timeout kills the whole tree rather
530
+ than leaving orphans holding the pipe open.
531
+
532
+ **When it fails**, you get the reason, the duration, the directory, and whatever it printed
533
+ before dying:
534
+
535
+ ```
536
+ ✗ error: exit code 1 after 184.2s
537
+ cwd: /Users/you/Projects/api
538
+ stderr:
539
+ src/api/handlers.ts:142:11 - TS2345: Argument of type 'string' is not assignable…
540
+ ```
541
+
542
+ or, on a timeout:
543
+
544
+ ```
545
+ ✗ error: killed after 900.0s (timeout 900s).
546
+ The command may simply be slow — re-run a narrower scope, or raise KRONK_TOOL_TIMEOUT.
547
+ stdout:
548
+ [42/97] Linting @acme/api…
549
+ ```
550
+
551
+ ---
552
+
553
+ ## Do tool calls cost tokens?
554
+
555
+ **Yes — the output does.** Every tool result is appended to the conversation as a `tool` message
556
+ and re-sent with each subsequent turn. The command itself is a few dozen tokens; a build log can
557
+ be tens of thousands. One `nx run-many` on a large monorepo can take more than half a context
558
+ window on its own.
559
+
560
+ ### Distillation
561
+
562
+ So large results are summarized **in a separate model call whose context is thrown away**. Only
563
+ the digest reaches your conversation — the raw log never enters it.
564
+
565
+ ```
566
+ 3 ⚙ bash: ./build.sh
567
+ distilled 9,988 → 2,153 tokens (separate context)
568
+ ✓ 80 lines
569
+ ```
570
+
571
+ The digest is structured and deliberately blunt:
572
+
573
+ ```
574
+ STATUS: failed — 14 of 217 tests failed, 400 packages built
575
+ FAILURES: src/api/handlers.ts:142:11 - TS2345: Argument of type 'string' is not assignable…
576
+ src/api/handlers.ts:207:3 - TS2554: Expected 2 arguments, but got 1.
577
+ NOTES:
578
+ ```
579
+
580
+ Before the model sees anything, error and warning lines are extracted from the raw text **by
581
+ regex**, and those go into the digest verbatim and marked authoritative. A summarizer reading ten
582
+ thousand tokens of build chatter can miss three error lines at the end — mine did, and confidently
583
+ reported `FAILURES: none`. Grep is not clever, but it does not overlook things.
584
+
585
+ The last 2,000 characters of raw output are appended untouched as well, since the tail is usually
586
+ the failure itself.
587
+
588
+ Output longer than 30,000 characters is trimmed **from the middle**, never the end — build tools
589
+ put their errors last.
590
+
591
+ | Setting | Default | |
592
+ |---|---|---|
593
+ | `KRONK_DISTILL` | `true` | `false` keeps every result whole |
594
+ | `KRONK_DISTILL_AT` | `8000` | Result size in characters that triggers it |
595
+
596
+ ### What if a command outputs more than the context window?
597
+
598
+ It cannot reach your conversation. Output passes three bounds before it becomes a message:
599
+
600
+ ```
601
+ command prints 4.2 MB
602
+
603
+ ▼ capture cap — keep the last 400 KB (MAX_CAPTURE)
604
+ 400 KB
605
+
606
+ ▼ trim the middle, keep head + tail (MAX_OUT, 30 KB ≈ 7.5k tokens)
607
+ 30 KB
608
+
609
+ ▼ distil in a throwaway context (KRONK_DISTILL_AT)
610
+ ~750 tokens ──▶ your conversation
611
+ ```
612
+
613
+ Every stage keeps the **end** of the output, because that is where build tools report failures,
614
+ and error lines are pulled out by regex before any of it is summarized. So the worst case for a
615
+ single command is roughly 7.5k tokens with distillation off, or under 1k with it on — never more,
616
+ however much the command prints.
617
+
618
+ The live meter shows you which stage you are in: the projection climbs, then reads `capped` once
619
+ the command has outrun what will be kept.
620
+
621
+
622
+ It applies to any tool result, not just `bash` — a large `search` or MCP response is treated the
623
+ same way.
624
+
625
+ > The distiller runs on the same model, so it costs a little time and output tokens. What it buys
626
+ > is that those tokens are spent **once** in a throwaway context, instead of sitting in your
627
+ > window and being re-sent on every turn for the rest of the session.
628
+
629
+ ---
630
+
631
+ ## Autonomous mode
632
+
633
+ `--auto`, or `/auto` in the REPL, swaps in a system prompt that tells the model to finish the
634
+ whole task — write code, **run** it, read the failure, fix it — and never claim success it has
635
+ not executed. Tools are auto-approved and the run continues as long as the task needs.
636
+
637
+ ```console
638
+ $ kronk-cli --auto "write a CSV stats script, add a node:test, run it, fix what breaks"
639
+
640
+ 1 ⚙ ls .
641
+ 2 ⚙ read data.csv
642
+ 3 ⚙ write stats.js
643
+ 4 ⚙ write test.js
644
+ 5 ⚙ bash: node --test
645
+ ✓ 10 lines
646
+
647
+ Test passes. stats.js auto-detects numeric columns and skips non-numeric values.
648
+ ```
649
+
650
+ `Ctrl-C` stops it. Add `--steps N` for unattended runs where nobody is watching.
651
+
652
+ > ⚠️ `--auto` runs shell commands without asking. Use it where `git checkout` can save you.
653
+
654
+ ---
655
+
656
+ ## How it works
657
+
658
+ ```
659
+ REPL ──▶ messages[] ──▶ POST /v1/chat/completions (stream:true)
660
+ ▲ │
661
+ │ ▼
662
+ │ SSE ──▶ text / reasoning / tool_calls
663
+ │ │
664
+ │ ▼
665
+ └──── tool results ◀──── approval ──▶ execute
666
+ ```
667
+
668
+ The loop repeats while the model requests tools, so one prompt can read files, run commands, and
669
+ write code before answering. Kronk's incremental message cache means each turn re-uses the
670
+ previous prompt prefix — watch `cached` climb in the usage line.
671
+
672
+ | File | |
673
+ |---|---|
674
+ | `src/index.js` | argv, REPL, one-shot mode |
675
+ | `src/agent.js` | the tool loop and system prompts |
676
+ | `src/client.js` | SSE streaming and Kronk endpoints |
677
+ | `src/tools.js` | tool definitions, sandbox, shell session |
678
+ | `src/context.js` | startup scan: git, layout, `AGENTS.md` |
679
+ | `src/compact.js` | summarizing the conversation when the window fills |
680
+ | `src/mcp.js` | MCP client: stdio + HTTP transports, tool routing |
681
+ | `src/distill.js` | summarizing large tool output in a throwaway context |
682
+ | `src/config.js` | precedence of flags, env, config file |
683
+ | `src/ui.js` | colour, spinner, usage formatting |
684
+
685
+ ---
686
+
687
+ ## Troubleshooting
688
+
689
+ | Symptom | |
690
+ |---|---|
691
+ | `Cannot reach Kronk` | `kronk server start --detach` |
692
+ | `Kronk is running but has no models` | `kronk model pull <id>` |
693
+ | First response takes ~25 s | Cold model load. Keep it warm with `--pool-ttl 1h` on the server |
694
+ | Long silence before text | The model is reasoning. `--no-think`, or `/thinking` to watch it |
695
+ | `(model produced no answer)` | Reasoning consumed the whole budget. Raise `KRONK_MAX_TOKENS` or use `--no-think` |
696
+ | `kronk-cli: command not found` after an nvm switch | Re-run `npm link`, or see [Using nvm?](#using-nvm) |
697
+ | Memory climbing | Two profiles resident at once. `kronk-cli --models` to confirm |
698
+ | Meter turning red | It self-compacts at 85%. `/compact` sooner, `/clear` to reset, or raise `context-window` in `model_config.yaml` |
699
+ | An MCP server shows `401` | It needs OAuth; `kronk-cli` has no browser flow. Use a token-based server config instead |
700
+ | A build times out | Raise `KRONK_TOOL_TIMEOUT` (seconds), or narrow the command's scope |
701
+ | A command's failure went unnoticed | Should not happen — exit status is captured before the cwd marker runs. Check `✗ error: exit code N` appeared |
702
+ | Context vanishing after one command | Its output is large. Distillation is on by default — check `KRONK_DISTILL_AT` |
703
+ | Model picks the wrong tool | Too many attached. Narrow with `--mcp <names>` |
704
+ | One message alone exceeds the window | Compaction cannot help — nothing survives trimming a single oversized input. Split the file, or raise `context-window` |
705
+ | Model ignores your conventions | Put them in `AGENTS.md` at the project root, and check the `context` line names it |
706
+ | Slow start in a huge repo | `--no-context` skips the scan |
707
+
708
+ ---
709
+
710
+ ## License
711
+
712
+ MIT