deel-local-cli 1.4.0 → 1.4.2

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.en.md CHANGED
@@ -1,12 +1,31 @@
1
1
  <div align="center">
2
2
 
3
- # deel
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/hero-en-dark.svg">
5
+ <img alt="deel — stays on this machine" src="docs/assets/hero-en-light.svg" width="620">
6
+ </picture>
4
7
 
5
- **A coding-agent CLI that runs on local models and private gateways only**
8
+ ### A coding-agent CLI that runs on local models and private gateways only
6
9
 
7
10
  Zero dependencies · Node 20+ · Exactly one place your source can go
8
11
 
9
- [한국어](README.md) · [Corporate review](#corporate-review-package) · [Troubleshooting](#troubleshooting)
12
+ <br>
13
+
14
+ [![npm](https://img.shields.io/npm/v/deel-local-cli?logo=npm&logoColor=white&label=npm&color=cb3837)](https://www.npmjs.com/package/deel-local-cli)
15
+ [![downloads](https://img.shields.io/npm/dt/deel-local-cli?label=downloads&color=1a7f37)](https://www.npmjs.com/package/deel-local-cli)
16
+ [![node](https://img.shields.io/node/v/deel-local-cli?logo=nodedotjs&logoColor=white&label=node&color=5FA04E)](https://nodejs.org)
17
+ [![license](https://img.shields.io/npm/l/deel-local-cli?label=license&color=0969da)](LICENSE)
18
+
19
+ [![Node.js CI](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/test.yml?branch=main&logo=github&logoColor=white&label=Node.js%20CI)](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
20
+ [![CodeQL](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/codeql.yml?branch=main&logo=github&logoColor=white&label=CodeQL)](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
21
+ [![tests](https://img.shields.io/badge/tests-3%2C498%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
22
+
23
+ [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
24
+ [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
25
+ [![network](https://img.shields.io/badge/network-127.0.0.1%20only-1a7f37?logo=wireguard&logoColor=white)](#where-your-data-can-go)
26
+ [![telemetry](https://img.shields.io/badge/telemetry-none-1a7f37?logo=ghostery&logoColor=white)](#where-your-data-can-go)
27
+
28
+ **[한국어](README.md)** · [Corporate review](#corporate-review-package) · [Troubleshooting](#troubleshooting) · [Full docs](docs/en/)
10
29
 
11
30
  </div>
12
31
 
@@ -20,12 +39,12 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
20
39
  │ Sends to this machine 127.0.0.1:11434 ← nowhere else │
21
40
  │ Link streaming · tools · reasoning control │
22
41
  │ Folder C:\work\myproject │
23
- │ Approval ⏵⏵ 자동 승인 — nothing is asked; /undo is the net
42
+ │ Approval ⏵⏵ auto-approve — nothing is asked; /undo is the net│
24
43
  │ Shift+Tab to change · Tab completes a / command │
25
44
  │ This PC 337 skills · 127 commands · 42 plugins │
26
45
  ╰──────────────────────────────────────────────────────────────╯
27
46
 
28
- ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱▱▱ 22% 28k/128k ▏ ◎ 종합 · ◇ medium·절약 · ⏵⏵ 자동
47
+ ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱▱▱ 22% 28k/128k ▏ ◎ Auto · ◇ medium·save · ⏵⏵ auto
29
48
  ❯ unify the logging style
30
49
 
31
50
  ❊ Grep(console.log)
@@ -68,7 +87,19 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
68
87
  - [Development](#development)
69
88
  - [Release notes](#release-notes)
70
89
 
71
- Each section is open at the summary. Click **▸ More** to unfold the detail behind it.
90
+ This page is the **summary**. Each section links to the detail behind it.
91
+
92
+ | Full docs | What is in there |
93
+ |---|---|
94
+ | [Models](docs/en/models.md) | Grade and window size · Korean-model presets · project detection |
95
+ | [The screen](docs/en/interface.md) | The input box · work modes · simple vs developer · what it asks about |
96
+ | [Tools in depth](docs/en/tools.md) | `Outline` · `Verify` · `Task` · `Jobs` · `Append` · `Def`/`Refs` · edit matching |
97
+ | [Korean documents and Excel](docs/en/documents.md) | hwpx/docx/pptx · encoding · Excel → CSV |
98
+ | [Extending](docs/en/extend.md) | Skills · plugins · MCP · ACP |
99
+ | [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
100
+ | [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
101
+ | [Configuration](docs/en/config.md) · [Development](docs/en/develop.md) | Env vars · run flags · running the tests · folder layout |
102
+ | [Release notes](docs/en/releases.md) | [1.4.2](docs/en/releases.md#142) · [1.4.1](docs/en/releases.md#141) · [1.4.0](docs/en/releases.md#140) · [1.3.0](docs/en/releases.md#130) · [1.2.0](docs/en/releases.md#120) |
72
103
 
73
104
  ---
74
105
 
@@ -232,140 +263,9 @@ $ deel scan
232
263
 
233
264
  Switch with `/model` mid-conversation — **the conversation carries over.**
234
265
 
235
- <details>
236
- <summary><b>More</b> — It adapts to whatever model is attached · Small windows get a smaller fixed share · On startup it reads what kind of project this folder is</summary>
237
-
238
- ### It adapts to whatever model is attached
239
-
240
- If you move between models, any number tuned for one of them is wrong for all the others.
241
- So **no number is hardcoded.**
242
-
243
- There are two axes. They are easy to confuse, so they have separate commands.
244
-
245
- | | What it measures | Command |
246
- |---|---|---|
247
- | **Window size** | how much it can hold | `/ctx` |
248
- | **Model grade** | how much it can do on its own | `/grade` |
249
-
250
- They do not move together. A 3B model with a 128k window exists; so does a very good model
251
- with 32k. Treat them as one axis and you hold one back while overrunning the other.
252
-
253
- **Derived from window size** (`src/agent/budget.js`):
254
-
255
- | | 8k | 32k | 131k | 655k |
256
- |---|---|---|---|---|
257
- | Steps per turn (code) | 16 | 48 | 192 | 200 |
258
- | `Read` lines | 200 | 384 | 1,536 | 4,000 |
259
- | `Glob` results | 50 | 192 | 768 | 1,000 |
260
- | `Outline` lines | 120 | 480 | 1,920 | 2,500 |
261
- | `WebFetch` chars | 4,000 | 12,800 | 51,200 | 120,000 |
262
- | Subtask summary | 400 | 1,600 | 4,000 | 4,000 |
263
-
264
- **Derived from model grade** (`src/agent/grade.js`):
265
-
266
- | | small | medium | large |
267
- |---|---|---|---|
268
- | Files per `Write` | 3 | 6 | 12 |
269
- | Split-writing threshold | 200 lines | 400 lines | 800 lines |
270
- | Spell out the procedure | yes | yes | **no — give the goal** |
271
- | Require verification | yes | yes | **yes** (grade-independent) |
272
-
273
- The grade is decided like this:
274
-
275
- 1. **First guess from the name.** `qwen2.5-coder-7b` -> small, `llama-3.3-70b` -> large.
276
- Version numbers (`2.5`) and quantisation tags (`q4_k_m`) are not read as sizes.
277
- A name that says nothing means **medium**, not small — a corporate gateway is exactly
278
- that case, and the models behind one are usually big. Guessing small holds them back.
279
- 2. **Corrected by what actually happened.** Truncated tool arguments, empty answers,
280
- failed edits and repeats are counted. A model labelled 70B that truncates every step is
281
- dropped to **small**; a 7B that runs ten clean steps is raised one level. The name is a
282
- guess; what happened is a fact.
283
- 3. **You win if you say so.** `/grade large`, and `/grade auto` hands it back.
284
-
285
- The status line shows `◈ small?`. The question mark means **still a guess** — a guess is
286
- not presented with the same face as something measured.
287
-
288
- The grade only changes **how much hand-holding you get**. Working scope, approval mode,
289
- undo and the audit log are identical at every grade. There is no "it is a good model, so
290
- skip verification" — that is exactly how a good model's mistake goes unnoticed.
291
-
292
- ### Small windows get a smaller fixed share
293
-
294
- The system prompt and the tool definitions go out **in full on every request**. Compaction
295
- (`/compact`) cannot shrink them. Once that share passes half the window there is no room
296
- left however well you fold, and it looks like "the model suddenly got stupid".
297
-
298
- Adding three tools (`Outline`, `Verify`, `Task`) pushed it to **49%** at 8k. Dropping a
299
- tool would have fixed it — and would have made small models capable of different things,
300
- which is the thing to avoid. The descriptions were trimmed to the window instead.
301
-
302
- | | 8k | 16k | 32k+ |
303
- |---|---|---|---|
304
- | Base rules | short form | short form | full |
305
- | Mode description | short form | short form | full |
306
- | Tool descriptions | 90 chars | 140 chars | 220 / full |
307
- | Obvious param descriptions | dropped | kept | kept |
308
- | Descriptions inside array items | dropped | dropped | kept |
309
- | **Fixed share** | **2,712 tokens (33%)** | 3,290 (21%) | 4,745 (4% at 131k) |
310
-
311
- The **folder brief** adds roughly 80 more tokens at 8k (below). That value varies by
312
- folder, so it is not in the table.
313
-
314
- **Tool names and arguments are untouched.** What is possible is identical in every window;
315
- what disappears is only the argument for *why* to use a tool. Large windows keep it,
316
- because that argument earns its keep — those two sentences are what make a model call
317
- `Outline` before `Read`.
318
-
319
- A test pins these numbers (`test/compact.test.js`).
320
-
321
- ### On startup it reads what kind of project this folder is
322
-
323
- Started in a folder of someone else's code, the model began knowing nothing. So it
324
- retraced the same three steps every time — scan the top level, read `package.json`, find
325
- out how tests are run. **On a local model each step is 20-40 seconds, so two minutes go
326
- by before the work even starts.**
327
-
328
- The worse case is the model *skipping* those three steps. Then it creates files by its
329
- own conventions without knowing what the project already uses — a `requirements.txt`
330
- dropped into an npm project.
331
-
332
- The answers are all knowable at startup, so they are read once and put in the prompt.
333
-
334
- The brief itself is written in Korean, like everything else deel puts on screen — this is
335
- deel's own folder, wrapped here for width:
336
-
337
- ```
338
- --- 이 폴더 --- (this folder)
339
- node 프로젝트 (deel-local-cli) · git main
340
- 돌릴 수 있는 것: npm start · npm test · npm run bench · npm run chat · npm run check ·
341
- npm run coverage · npm run demo · npm run diagnose (runnable)
342
- 위쪽: bin/ src/ test/ LICENSE README.en.md README.md package.json report.txt (top level)
343
- 위쪽 한 겹만 본 것이다. 안을 알아야 하면 Outline 을 불러라.
344
- (top level only — call Outline to see inside)
345
- ```
346
-
347
- **Nothing is invented.** Runnable commands are copied verbatim from `scripts` in
348
- `package.json`. Advertising a command that does not exist means the model calls it,
349
- fails, and spends the steps you just saved looking for the real one.
350
-
351
- **The last line matters.** What is listed is the top level and `package.json`, nothing
352
- inside subfolders. Without saying so, the model treats this as a map of the whole project
353
- and stops calling `Outline` — at which point the brief costs more than it saves.
354
-
355
- | Not done | Why |
356
- |---|---|
357
- | No `git` subprocess | `.git/HEAD` is read directly. Spawning one freezes startup for seconds on a large repo |
358
- | No directory walk | Top level **only**. Descending is slow on large repos, and only one line goes in the prompt anyway |
359
- | Not re-read each turn | Once, at startup. A prompt that changes mid-conversation makes it impossible to tell why an answer changed |
360
-
361
- It scales with the window — 10 entries and 4 commands at 8k, 24 and 8 on a large one.
362
- If only one thing survives a narrow window it is the **commands**. The top-level listing
363
- can be recovered with one `Glob`; "tests run with `npm test`" requires opening
364
- `package.json`.
365
-
366
- 7ms on a folder with 600 files (`test/project.test.js`).
367
-
368
- </details>
266
+ > **More** — It adapts to whatever model is attached · Korean models are known before they are experienced · Small windows get a smaller fixed share · On startup it reads what kind of project this folder is
267
+ >
268
+ > **[Models read →](docs/en/models.md#multiple-local-runtimes)**
369
269
 
370
270
  ---
371
271
 
@@ -379,6 +279,7 @@ Names follow Claude Code / Codex conventions.
379
279
  | `/lang [ko\|en]` | Screen language. Falls back to Korean for anything not translated yet |
380
280
  | `/bell [on\|off]` | Ring and set the window title when a turn ends, or when deel needs an answer |
381
281
  | `/consult <profile> <question>` | Ask a second model one question. Your current model stays put |
282
+ | `/export` | This conversation as a **one-page HTML report** — asked, changed, verified. Self-contained, opens on any network |
382
283
  | `/lsp [on\|off]` | Language servers — what is installed, and whether `Def`/`Refs` are available. `off` turns post-edit diagnostics off only |
383
284
  | `/context` | What is consuming the context window |
384
285
  | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
@@ -434,68 +335,9 @@ the conversation.
434
335
 
435
336
  Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
436
337
 
437
- <details>
438
- <summary><b>More</b> — Attaching a file with @ · Interrupting</summary>
439
-
440
- ### Attaching a file with `@`
441
-
442
- Write `@` followed by a path and that file is sent along with your message.
443
-
444
- ```
445
- ❯ @src/a.js why is this slow?
446
- ◧ attached src/a.js
447
- ```
448
-
449
- That removes **one round-trip** — the one where the model has to call `Read` itself. Local
450
- models are weak at tool calling and that round-trip often misfires: it calls the wrong path,
451
- or skips the call and invents the contents. You already know which file it is; there is no
452
- reason to make the model go looking.
453
-
454
- The hard part is not attaching. It is **not mistaking something else for a file.**
455
- Things starting with `@` are everywhere.
456
-
457
- | What you type | What happens |
458
- |---|---|
459
- | `@src/a.js` | Attached — only when the path actually exists |
460
- | `hong@example.com` | Not a mention at all. A letter before the `@` means it is an address |
461
- | `@media` · `@dataclass` · `@scope/pkg` | Left alone — no such path exists |
462
- | `@src/` (a directory) | A listing of what is inside is attached |
463
- | `@"draft report.txt"` | Quote names containing spaces |
464
- | Outside the working scope | Refused, and the refusal is printed |
465
- | A CP949 corporate document | Decoded by content and attached correctly |
466
-
467
- There is one rule: **attach only when the path really exists.** Otherwise the text is left
468
- exactly as typed, silently — it probably wasn't a mention.
469
-
470
- Attachments are capped at **25% of the context length** (20,000 tokens at most). Anything larger is attached from the
471
- top only, and a truncated file is **not marked as already read.** Marking it read would let the
472
- model edit a part it never saw. Only a fully attached file lets it skip `Read`.
473
-
474
- Every attachment is announced on screen. Text the user did not type is now in the conversation;
475
- not showing it would also leave them wondering where the context went.
476
-
477
- ### Interrupting
478
-
479
- Press **Ctrl+C** to stop the model mid-answer when it is heading the wrong way.
480
-
481
- ```
482
- ❯ rewrite the whole test suite
483
- ◧ Read test/smoke.js
484
- ◧ Read test/loop.test.js
485
- ^C
486
- ⚠ Stopped (after step 2)
487
-
488
- ❯ ▊
489
- ```
490
-
491
- The conversation stays valid. If the model had announced tool calls, each unanswered one is
492
- filled with a `stopped by user` result so the call/result pairing holds — a conversation with
493
- broken pairing is rejected with HTTP 400 on the next request, which would waste the whole
494
- session. Tools already running finish; **tools not yet started never run.**
495
-
496
- Pressing Ctrl+C again on an empty line quits.
497
-
498
- </details>
338
+ > **More** — Attaching a file with @ · Interrupting
339
+ >
340
+ > **[The screen read →](docs/en/interface.md#slash-commands)**
499
341
 
500
342
  ---
501
343
 
@@ -525,52 +367,9 @@ Don't confuse this with `/mode`. They are separate axes:
525
367
  If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
526
368
  overrides something a person chose.
527
369
 
528
- <details>
529
- <summary><b>More</b> — Switching by itself (Auto mode)</summary>
530
-
531
- ### Switching by itself (Auto mode)
532
-
533
- You start in **Auto**. Nothing has been decided about what kind of work is coming.
534
- Every turn, deel reads what you typed, picks the mode that fits, and works in it.
535
-
536
- ```
537
- ❯ why won't the login go through?
538
-
539
- ◉ Debug because your message contained "why won't", "won't"
540
- Not what you wanted? Type /code to pin a mode yourself.
541
- ```
542
-
543
- Switching brings **the whole mode** with it — its working protocol, its tool set, its
544
- reasoning settings. It isn't a label saying "debug mode": the model is actually walked
545
- through symptom → reproduce → hypothesis → evidence, and in Plan mode `Write` and `Edit`
546
- are not handed over at all.
547
-
548
- | When you say | It goes to |
549
- |---|---|
550
- | why won't · error · fails · crashes · what's causing | ◉ Debug |
551
- | plan · roadmap · what order · let's map it out first | ☰ Plan |
552
- | design · architecture · how should this be structured · how to split | ◈ Architect |
553
- | what is · explain · how does it work · difference between | ◇ Ask |
554
- | all of · everything · one by one · to the end · unify | ❋ Orchestrator |
555
- | fix · add · implement · rename · delete | ◆ Code |
556
-
557
- **When it's close, it doesn't switch.** "ok", "go on", "that thing from earlier" leave you
558
- in Auto. So does a near-tie between first and second place — a wrong switch into a read-only
559
- mode leaves you blocked without knowing *why*. Read-only modes (Plan, Architect, Ask)
560
- therefore carry a higher bar: "explain this and fix it" routes to Code, not Ask.
561
-
562
- A switch lasts **one turn only.** The next message is judged fresh.
563
- A `~` in the status line means it switched by itself; no `~` means you chose it.
564
-
565
- ```
566
- ◎ Auto ← waiting
567
- ~◉ Debug ← this turn only, chosen for you
568
- ◉ Debug ← you typed /debug. It stays.
569
- ```
570
-
571
- Choosing a mode yourself **pins** it. `/auto` (or `/work auto`) hands the wheel back.
572
-
573
- </details>
370
+ > **More** — Switching by itself (Auto mode)
371
+ >
372
+ > **[The screen read →](docs/en/interface.md#work-modes)**
574
373
 
575
374
  ---
576
375
 
@@ -593,202 +392,9 @@ Two things matter here:
593
392
  - **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
594
393
  blocking are identical. A beginner needs the undo more, not less.
595
394
 
596
- <details>
597
- <summary><b>More</b> — The input box · You don't have to type the whole command · The box stays while it works · The picture on the left moves too and 1 more</summary>
598
-
599
- ### The input box
600
-
601
- Launched in a terminal, **the conversation scrolls normally and an input box is pinned at
602
- the bottom.** Only the box is erased and redrawn — nothing above it is touched.
603
-
604
- ```
605
- ❊ Grep(console.log)
606
- └ 3 files · 11 hits
607
- ◈ Edit(src/runner.js)
608
- └ 1 spot +3-1
609
- - 12 console.log('시작', 이름)
610
- + 12 logger.info({ 단계: '시작', 이름 })
611
-
612
- ▌ Unified log calls to the logger format. One change in runner.js.
613
-
614
- ── 4.2s · 3 tools · ↑3,900 ↓180
615
-
616
- ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱ 22% ▏ ◎ 종합 · ◇ medium · ⏵⏵ 자동
617
- ╭─────────────────────────────────────────────────────────────────────────────╮
618
- │ ❯ also shrink the aggregate helpers │
619
- ╰─────────────────────────────────────────────────────────────────────────────╯
620
- ```
621
-
622
- Terminal scrollback, selection and `Ctrl+F` all keep working, because the conversation is
623
- never trapped inside a pane of ours. Long input grows the box to as many lines as it needs.
624
-
625
- **It switches itself off where it would do harm.** Piped or redirected output, `CI` set,
626
- `TERM=dumb`, or a window under 40 columns: no box, no asking. `deel … | tee log.txt` must
627
- not become a pile of escape codes. Passing `--tui` does not override a pipe, and `--no-tui`
628
- turns it off at any time.
629
-
630
- Line editing stays entirely with Node's readline — Korean IME composition, paste, history,
631
- Ctrl+A/E, backspace. We only *draw* the string readline is holding. Hand-rolling a line
632
- editor is how you break IME input first.
633
-
634
- > **A path taken and abandoned** — the first version borrowed the whole terminal (an
635
- > alternate screen, like vim) and split it into conversation, changed-files and todo panes.
636
- > It looked the part, and **every slash command went dead.** Six modules including
637
- > `commands.js` write straight to the terminal rather than through the screen object, and a
638
- > full repaint erased their output the instant it appeared. Not "the command didn't run" —
639
- > "you can't see that it ran", which is worse. Fixing it would mean threading every one of
640
- > those six through the screen object, plus every one added later, with a silent return of
641
- > the same symptom if one is missed. So the design went the other way: let the conversation
642
- > flow, manage only the box. ([`test/box.test.js`](test/box.test.js) spawns a child that
643
- > pretends to be a terminal, so this one cannot ship again.)
644
-
645
- ---
646
-
647
- ### You don't have to type the whole command
648
-
649
- There are over thirty commands. The only person who has them memorised is the one who
650
- wrote them, and even he gets as far as `/mem…` and stops to wonder whether it was `memory`
651
- or `memo`. If the only recourse at that point is typing `/help` and scanning thirty lines,
652
- that isn't a command, it's a quiz.
653
-
654
- So they show up **while you type.**
655
-
656
- ```
657
- ╭─────────────────────────────────────────────────────────────────────────────╮
658
- │ ❯ /mo │
659
- ╰─────────────────────────────────────────────────────────────────────────────╯
660
- › /model [이름|list|models] 연결·모델 바꾸기 (이름 일부 · list · models)
661
- /mode <모드> 승인 정책 — 얼마나 물어보나
662
- /memory [지우기 <번호>|…] 대화가 끝나도 남는 기억 — 보기·지우기
663
- ```
664
-
665
- `Tab` fills it in: all the way if only one matches, otherwise **only as far as they all
666
- agree** (`/mo` + Tab → `/mode`). It doesn't pick one for you, because deleting the wrong
667
- guess costs more than typing the rest. Commands that take an argument get a trailing space
668
- so you can keep going.
669
-
670
- Prefix matches come first and **substring matches follow** — typos cluster in the first
671
- character, so `/emo` still surfaces `/memory`. The moment you type a space (`/mode auto`)
672
- the list folds away: the command is already decided.
673
-
674
- The list sits **below** the box. Putting it inside would push what you are typing upward,
675
- so you could no longer see it.
676
-
677
- > No up/down selection. That would have to take over **input history** (up arrow), which
678
- > is used far more often. What is needed here isn't picking, it's recognising — and once
679
- > you recognise it, one `Tab` is enough.
680
- >
681
- > Commands hidden at the `쉬움` level still surface when you type their prefix. Hidden is
682
- > not the same as unavailable — someone who knows `/recall` should not be told it doesn't
683
- > exist because of a display setting.
684
-
685
- ---
686
-
687
- ### The box stays while it works
688
-
689
- Local models are slow. A single step can take tens of seconds, and if the bottom of the
690
- screen goes blank for that long, **people assume it hung and hit Ctrl+C** — losing work that
691
- was nearly done. So the border stays and only the contents change.
692
-
693
- ```
694
- ◧ Read(집계.py)
695
- └ 6 lines
696
-
697
- ▏myproject · qwen2.5-coder:7b ▏ ▰▱▱▱▱▱▱▱▱▱ 2% ▏ ◎ 종합 · ◇ medium · ⏵⏵ 자동 ▏ ↑3.8k ↓180
698
- ╭─────────────────────────────────────────────────────────────────────────────╮
699
- │ ⠹ 파일 들여다보는 중… 12초 · 생각 1,240자 · Ctrl+C 중단 │
700
- ╰─────────────────────────────────────────────────────────────────────────────╯
701
- ```
702
-
703
- The phrase tracks **what is actually happening**. This is not decoration: a message that
704
- cycles at random stops being read after the second time, and from then on it is worth no
705
- more than a blank screen. One turn reads like this:
706
-
707
- ```
708
- 머리 굴리는 중 → 파일 들여다보는 중 → 코드 짜는 중 → 답 쓰는 중
709
- (turning it over) (looking at files) (writing code) (writing the answer)
710
- ```
711
-
712
- The phrases are Korean, because the interface is. Here is what each set means:
713
-
714
- | Activity | On screen | Roughly |
715
- |---|---|---|
716
- | Thinking | 머리 굴리는 중 · 어떻게 할지 궁리하는 중 · 수 읽는 중 · 따져 보는 중 | turning it over · working out how · reading ahead · weighing it up |
717
- | `Read` `Grep` `Glob` | 파일 들여다보는 중 · 코드 훑는 중 · 어디 있나 뒤지는 중 · 단서 찾는 중 | looking at files · skimming code · hunting for where it is · looking for a clue |
718
- | `Write` `Edit` `Append` | 코드 짜는 중 · 고쳐 넣는 중 · 손보는 중 · 한 줄씩 옮기는 중 | writing code · patching it in · touching it up · moving it a line at a time |
719
- | `Bash` | 명령 돌리는 중 · 터미널 두드리는 중 · 결과 기다리는 중 | running a command · at the terminal · waiting on output |
720
- | `WebFetch` | 문서 찾아보는 중 · 읽어 오는 중 | looking up docs · fetching |
721
- | Answering | 답 쓰는 중 · 정리해서 말하는 중 | writing the answer · putting it together |
722
- | **Past 45 seconds** | 아직 하는 중 · 조금만 더 · 생각보다 오래 걸리는 중 | still going · nearly there · taking longer than expected |
723
-
724
- Within a category the phrase advances every 4 seconds — text frozen for 30 seconds reads as
725
- hung too. On the right: **elapsed time**, and while the model is reasoning, **how many
726
- characters of thinking have arrived**. One number that genuinely increases is what turns
727
- "still alive" from a claim into a fact.
728
-
729
- ### The picture on the left moves too
730
-
731
- What spins next to the phrase is not a spinner — it is **a small drawing of the work being
732
- done right now**.
733
-
734
- | Doing | One cycle | The picture |
735
- |---|---|---|
736
- | Thinking | `⠀⠶⠀` `⠰⣿⠆` `⢾⣿⡷` `⠰⣿⠆` | swells and shrinks |
737
- | Reading | `⠉⠉⠉` `⠒⠒⠒` `⠤⠤⠤` `⣀⣀⣀` | a scanning line travels down |
738
- | Writing | `⡼⠭⠧` `⠼⡯⠧` `⠼⠿⡧` `⠼⡭⠧` | **a laptop typing** |
739
- | Commands | `⠉⠀⠀` `⠉⠈⠀` `⠛⠊⠀` `⠿⠮⠄` | output piles up a line at a time |
740
- | Answering | `⠉⠀⠀` `⠛⠉⠀` `⠿⠛⠉` `⣿⠿⠛` | text fills up |
741
- | Web | `⣀⣀⣀` `⣤⣀⣀` `⣶⣤⣀` `⣿⣶⣤` | signal bars grow |
742
- | Compacting | `⣿⣿⣿` `⣶⣶⣶` `⣤⣤⣤` `⣀⣀⣀` | pressed down into one line |
743
- | Past 45 seconds | `⠶⠀⠀` `⠰⠆⠀` `⠀⠶⠀` `⠀⠰⠆` | back and forth — "waiting" |
744
-
745
- Braille only. One braille cell is **2 wide by 4 dots tall**, so three cells make a 6×4 grid
746
- that is exactly three columns in any terminal. Emoji and `●` `▪` include glyphs that East
747
- Asian locales measure as two columns, which knocks the border out of line every 90ms.
748
-
749
- If the drawing does not render, or you use a screen reader, `DEEL_NO_MOTION=1` turns it off
750
- and you get the old single-cell spinner.
751
-
752
- ### What gets asked, and what just happens
753
-
754
- Whether your files change **with or without being asked** is the one thing that has to be
755
- readable at a glance. It sits on the right of the status line at all times.
756
-
757
- | Indicator | Command | What it asks about |
758
- |---|---|---|
759
- | `⏵⏵ 자동 승인` (auto) | `/mode auto` | Nothing is asked. `/undo` is the safety net |
760
- | `⏵ 위험만 확인` (risky only) | `/mode confirm` | Only irreversible commands. Files change unasked |
761
- | `⏸ 모두 확인` (everything) | `/mode strict` | Every file change and every command is confirmed first |
762
-
763
- `/mode` on its own lists all three and marks the current one with ●. The startup header
764
- spells it out in a sentence, so the glyph is enough from then on.
765
-
766
- **`Shift+Tab` cycles it without typing.** Each press moves one step and leaves a line
767
- saying where it went. Whatever you were typing stays put.
768
-
769
- ```
770
- ⏵ 위험만 확인 되돌릴 수 없는 명령만 물어봅니다. 파일은 안 묻고 고칩니다
771
- 자동 승인 → 위험만 확인 · Shift+Tab 으로 계속 바꿉니다
772
- ```
773
-
774
- The cycle runs **loose → strict** (auto → risky only → everything → auto). A mistaken
775
- press only makes it ask more; it never drops you into "changes files unasked" in one hit.
776
-
777
- > That key used to cycle the work mode (`종합`, `코드`, …). The swap is about **who
778
- > reaches for it more often.** Work mode follows your request on its own, while approval
779
- > policy is what you want to change mid-task when a particular job deserves a look.
780
- > Work mode moved to `Ctrl+O`; `/work` still does the same thing.
781
-
782
- > It used to be the bare word `auto`. Next to `종합` and `medium·절약` it looked like just
783
- > another mode, and nothing on screen said that one of them meant **files change without
784
- > asking.**
395
+ > **More** — The input box · You don't have to type the whole command · The box stays while it works · The picture on the left moves too and 1 more
785
396
  >
786
- > That also changed what gets truncated when space runs out. A corporate gateway model name
787
- > like `databricks-gpt-5-6-luna` eats twenty-three columns, and that alone was pushing the
788
- > approval indicator off the line entirely. Now **the model name shortens first** — you
789
- > already know what you are running; whether your files change unasked is what you need now.
790
-
791
- </details>
397
+ > **[The screen read →](docs/en/interface.md#simple-vs-developer)**
792
398
 
793
399
  ---
794
400
 
@@ -798,7 +404,7 @@ Names and arguments match Claude Code, so skills written for that convention wor
798
404
 
799
405
  | Tool | What it does |
800
406
  |---|---|
801
- | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV**) |
407
+ | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV, hwpx/docx/pptx as text**) |
802
408
  | `Write` | Write / overwrite a file (**several at once via the `files` array**) |
803
409
  | `Append` | Append to the end of a file — **how large files get written in pieces** |
804
410
  | `Edit` | Replace an exact string (`replace_all`; **several sites at once via the `edits` array**) |
@@ -822,884 +428,165 @@ Seven tools here are not in Claude Code — `Append`, `Recall`, `Remember`, `Out
822
428
  so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
823
429
  their cost; here is why.
824
430
 
825
- <details>
826
- <summary><b>More</b> — Outline · Verify · Task · Def · Refs · Commands that never finish and 9 more</summary>
827
-
828
- ### Seeing a project's shape cheaply — `Outline`
829
-
830
- There used to be only two ways to understand someone else's code. `Glob` gives you paths;
831
- `Read` pulls a whole file into the window. **The middle was missing.**
832
-
833
- So the model started editing without knowing what lived where, and re-created functions
834
- that already existed somewhere else. It had not seen them — which is different from not
835
- knowing.
431
+ > **More** — Outline · Verify · Task · Def · Refs · Commands that never finish and 9 more
432
+ >
433
+ > **[Tools in depth read →](docs/en/tools.md#tools)**
836
434
 
837
- ```
838
- ❉ Outline(src/ui) 12 files · 122 places
435
+ ---
839
436
 
840
- src/ui/screen.js (304 lines)
841
- 46 fn 상자쓸까
842
- 65 class LineScreen
843
- 92 method 줄
844
- 198 class BoxScreen
845
- ```
437
+ ## Korean text and Excel
846
438
 
847
- Reading that folder whole costs **25,612 tokens**; `Outline` costs **857** 30x cheaper.
848
- An 8k model can see the shape of a whole project.
439
+ **A file saved as CP949 is written back as CP949.** The encoding is never changed.
440
+ Excel (`.xlsx`) is read as CSV read-only.
849
441
 
850
- It reads js/ts, py, java/kotlin, go, rust, c#, md, html, css, sh and json. Regex, not a
851
- parser (zero dependencies). So it **says what it could not read** — dropping those
852
- silently makes the model believe the file does not exist, and rebuild config that is
853
- already there.
442
+ > **More** Encoding · Excel
443
+ >
444
+ > **[Korean documents and Excel read →](docs/en/documents.md#korean-text-and-excel)**
854
445
 
855
- ### Checking what was built — `Verify`
446
+ ---
856
447
 
857
- The end of a turn used to say:
448
+ ## Serving what you built
858
449
 
859
450
  ```
860
- index.html · 410 lines · 18.2KB
861
- ```
862
-
863
- That proves the file **exists**, not that it **works**. An unclosed `<div>`, a
864
- `src="app.js"` pointing at nothing, a JS file one bracket short — all green.
865
-
866
- What can be run gets run (`node --check`, `py_compile`); what cannot gets read (HTML tag
867
- pairing, missing references, CSS braces, JSON).
451
+ /preview
868
452
 
453
+ ▶ Serving http://127.0.0.1:56801/
454
+ showing .
455
+ Edit a file and the page reloads by itself.
456
+ Only this machine can open it (127.0.0.1). No other PC can see it.
457
+ Stop with /preview off · it shuts down when deel exits.
869
458
  ```
870
- ⏺ Verify 1 broken · 3 checked
871
- ```
872
-
873
- And the part that matters most — **what could not be checked is reported as such.**
874
459
 
875
- Arbitrary commands are **not** run here. That path has to be `Bash` alone: the approval
876
- gate and the safety checks live only there, so running commands from here would break the
877
- strict-mode promise in exactly this one spot. It tells you `npm test` exists instead.
460
+ A browser opens with it. `/preview <folder>` picks what to serve, `/preview off` stops it.
878
461
 
879
- ### Splitting big work off `Task`
462
+ **This is not the same as double-clicking the file (`file://`).** Under `file://` everything
463
+ below is blocked — and the error only shows up in the console while the page stays blank, so
464
+ you end up suspecting your own code. This is a real HTTP server, so it all works:
880
465
 
881
- Building eight files in one window means all eight files pile up in that window. On a 32k
882
- model it fills around the third or fourth, and once it fills, earlier turns get folded
883
- away. From then on the model has forgotten what it was building — **no error appears, the
884
- result just gets worse.** That was the root of "build me a dashboard" ending as a plan.
466
+ | | `file://` | `/preview` |
467
+ |---|---|---|
468
+ | `<script type="module">` · `import` | blocked (CORS) | **works** |
469
+ | `fetch('./data.json')` | blocked | **works** |
470
+ | `new Worker(...)` | blocked | **works** |
471
+ | `WebAssembly.compileStreaming` | blocked (MIME) | **works** |
472
+ | textures · `getImageData` | tainted canvas | **works** |
473
+ | `.glb` / `.gltf` (Three.js) | no MIME type → silently not drawn | **works** |
885
474
 
886
- `Task` runs that chunk in a **fresh conversation** and returns only a summary.
475
+ All seven were run in a real Chrome and confirmed **7/7**.
887
476
 
888
- ```
889
- subtask build the page skeleton separate conversation · max 8 steps
890
- │ ◆ Write(index.html +1) 2 files · 24 lines
891
- ✓ subtask build the page skeleton done · 2 files · 2 steps
892
- ```
477
+ Apps with a router (React Router and friends) get the first page back when you reload on a
478
+ deep link. Never for requests with an extension (`app.js`) though — returning HTML for a
479
+ missing script dies with `Unexpected token '<'`, which hides the real cause (a typo in a filename).
893
480
 
894
- Peak conversation size while building the same four files (system prompt excluded):
481
+ ### It opens exactly as much as it says
895
482
 
896
- | | Peak |
897
- |---|---|
898
- | All in one window | 4,181 chars |
899
- | Split with `Task` | **2,113 chars** |
483
+ Starting a server means opening your disk to somebody else.
900
484
 
901
- The left column keeps growing with each file; the right one does not.
485
+ - Bound to **`127.0.0.1` only**. `0.0.0.0` is not available at all on an office network
486
+ that would let anyone read your source.
487
+ - Port **0** (the kernel hands out a free one). A fixed port steals someone else's.
488
+ - Paths cannot leave the working scope. `../` · `%2e%2e` · double encoding · absolute paths ·
489
+ null bytes · symlinks — eight of these are held shut by tests.
490
+ - **It only serves.** `POST` · `PUT` · `DELETE` are refused with 405.
491
+ - It shuts down when `deel` exits.
902
492
 
903
- **Every guard still applies.** A subtask runs inside the same working folder, follows the
904
- same approval mode, is undone **together with** its parent by one `/undo`, and lands in
905
- the audit log. There is no path for a subtask to edit files under a read-only mode
906
- (architect, plan, ask) — that is blocked both at the mode level and in the tool list.
907
- Nesting stops at two levels.
493
+ ---
908
494
 
909
- ### Commands that never finish — `Bash`'s `background` and `Jobs`
495
+ ## Skills and plugins
910
496
 
911
- `Bash` only returns once the command **ends**. So anything that does not end could not be
912
- run `npm run dev`, `python -m http.server`, `vite`, `npm run watch`. Asking for one used
913
- to mean waiting 120 seconds and then a kill, leaving one line on screen.
497
+ **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
498
+ whatever is there. On a clean PC: zero. On a PC with skills installed: those skills.
914
499
 
915
500
  ```
916
- ▶ Bash(npm run dev)
917
- └ 시간 초과로 중단됨 (120000ms) 2분 0.0초
918
- (timed out)
501
+ project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
502
+ user ~/.deel/skills ~/.claude/skills ~/.claude/commands
503
+ plugins ~/.claude/plugins/** ~/.deel/plugins/**
919
504
  ```
920
505
 
921
- The model concludes the server would not start and gives up, or worse, raises `timeout`
922
- and calls again — which stalls the whole turn. **There was no way at all to start what
923
- you built and check it.** `Verify` gets you as far as "the syntax is valid"; whether it
924
- actually comes up requires bringing it up.
506
+ Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
925
507
 
926
- ```
927
- ▶ Bash(npm run dev)
928
- 1번으로 띄움 (started as job 1)
508
+ > **More** — Loaded in three stages · Fetching plugins · Deliberately not included
509
+ >
510
+ > **[Extending read →](docs/en/extend.md#skills-and-plugins)**
929
511
 
930
- ◈ Edit(src/App.jsx)
931
- └ 1군데 (1 site)
512
+ ---
932
513
 
933
- Jobs(1번)
934
- └ 도는중 · 24초 (running · 24s)
514
+ ### The hidden latency of local models — keeping the prefix cache alive
935
515
 
936
- Jobs(1번 · 끝내기)
937
- 끝냄 · 41초 (stopped · 41s)
938
- ```
516
+ Ollama and llama.cpp reuse computation **only while the request starts the same way as the
517
+ last one.** Change one early character and everything after it — the entire conversation —
518
+ is recomputed. This is the usual hidden reason long local sessions feel slower and slower,
519
+ and it never shows up anywhere, because it is not an error.
939
520
 
940
- It starts and **returns immediately**. Output accumulates and `Jobs` reads it.
521
+ deel routes every message to the right mode automatically, and that mode instruction used to
522
+ sit **early** in the prompt — every mode switch broke the whole cache. So the stable parts
523
+ (rules, folder, project fingerprint, user rules, memory, skills) are frozen at the front and
524
+ the per-turn parts (mode, pins) go last. A test pins this order down (`test/cache.test.js`).
941
525
 
942
- **Something that did not start is never reported as started.** The job is watched briefly
943
- after launch, and if it dies in that window it comes back as a failure. The most common
944
- failure is a port already in use; reporting that as "started" sends the model on to the
945
- next step while you refresh a server that was never there.
526
+ Ollama also gets `keep_alive: 60m` with the 5-minute default, the model unloads while you
527
+ glance at another window, and the first message after you come back recomputes everything.
528
+ Override with `DEEL_KEEP_ALIVE`. If you run llama.cpp directly, `--cache-reuse 256` on the
529
+ server side does the same job.
946
530
 
947
- ```
948
- ▶ Bash(npm run dev)
949
- └ 띄우자마자 끝났습니다 (종료코드 1).
950
- (exited immediately, exit code 1)
951
- ```
531
+ ## Reasoning effort
952
532
 
953
- | Guarantee | Detail |
954
- |---|---|
955
- | Safety checks | **Identical** to `Bash`. This must not become a back door |
956
- | Cleanup | Everything is killed when deel exits **down to grandchildren**, and it says how many |
957
- | Retained | 256KB. Past that the front is dropped and **the drop is stated** |
958
- | Handed to the model | 4,000 chars. This **must** be a different number from the one above |
959
- | On stop | Waits for the dying output, and returns only once the process is **actually dead** |
960
- | Argument names | Korean and English both accepted (`번호`/`job`, `끝내기`/`stop`). Unrecognized ones **are reported** |
961
- | Count | Eight running. Finished jobs keep the most recent eight, and evictions **are stated** |
962
-
963
- Why two different caps: make them equal and every overflow of a `watch` job means
964
- one `Jobs` read dumps 256KB into the window. On an 8k model that single read ends
965
- the window.
966
-
967
- **Stopping a job does not close its pipes immediately.** At the moment the kill is
968
- issued there is still unread data in the pipe, and the last few lines before a
969
- death are the ones that matter — the stack trace a server leaves on the way down.
970
- Printing `last output:` and then withholding the last output is worse than not
971
- printing it. It also waits until the process is **confirmed dead** before dropping
972
- it from the list: dropping a live one means it can never be named again, which is
973
- the exact state this feature exists to prevent.
974
-
975
- Finished jobs are not dropped right away — they are kept so their final output can
976
- be read. Only the most recent eight survive; otherwise thirty short commands leave
977
- thirty entries, each holding up to 256KB.
978
-
979
- **Argument names are accepted in both Korean and English.** Models frequently
980
- translate Korean parameter names into English — not a guess, something this repo
981
- already hit (`Task` accepts both `목적` and `purpose`). `Jobs` did not, which meant:
533
+ One answer means several model calls, and **each needs a different amount of thinking.**
534
+ All-high is slow; all-low wanders off.
535
+
536
+ The default is **one line**. What you want to know is how hard it is thinking right now,
537
+ not a stage table.
982
538
 
983
539
  ```
984
- Jobs({job: 1, stop: true}) -> a listing comes back. The server keeps running.
540
+ $ /think
541
+
542
+ 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
543
+ 더 세게 /think high 더 빠르게 /think low
985
544
  ```
986
545
 
987
- The model asked for a stop and **got what looks like a success** while the port
988
- stays held. So both spellings are accepted, and when nothing is recognized it says
989
- so rather than falling back to a listing. The name mapping lives in exactly **one**
990
- place the on-screen label reads it too. Two copies means the tool works while the
991
- label shows empty parentheses.
546
+ | Profile | Character |
547
+ |---|---|
548
+ | `even` | Same effort everywhere predictable, slower |
549
+ | `save` (default) | Hard on the first decision only |
550
+ | `deep` | Everything one notch up — for hard work |
992
551
 
993
- **Killing grandchildren is where this quietly goes wrong.** `npm run dev` descends
994
- npm node vite, and the thing holding the port is at the bottom. Windows has
995
- `taskkill /t` to walk the tree; Unix has nothing equivalent, so the job is
996
- **started in its own process group** — after the fact there is no way to name a
997
- grandchild at all. Skip that and deel says "killed 3" while the server keeps running.
552
+ Set the profile with `/think 배분 절약`. **Level and profile are different axes, so the
553
+ commands were split** `/think high` and `/think save` used to set different things under
554
+ one name, which made the screen unreadable.
998
555
 
999
- Cleanup is where this quietly goes wrong. Skip it and a process nobody started keeps
1000
- running. Next time you start a dev server you get "port already in use" with **no way to
1001
- find what is holding it**. So `test/jobs.test.js` verifies the process actually died, via
1002
- a file the child keeps appending to.
556
+ The stage table moved to `/think 자세히` (the default at developer level).
1003
557
 
1004
- `deel run` (one-shot mode) does the same. A batch job is hurt worst by missing this — the
1005
- job reports done, the server keeps running, and the next job fails to bind the same port
1006
- with nothing in the log to explain it.
558
+ ```
559
+ $ /think 자세히
1007
560
 
1008
- ### Several at once `Write`'s `files`, `Edit`'s `edits`
561
+ 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
562
+ 배분 절약 첫 판단만 세게, 이어가기는 얕게 — 대개 이게 낫습니다
1009
563
 
1010
- One round trip is 20-40 seconds on a local model. Creating five files with five `Write`
1011
- calls is two to three minutes of nothing but round trips. So they go in one array.
564
+ 단계 강도 출력상한 언제
565
+ 판단 · medium 15,549 무엇을 할지 정하는 자리
566
+ 이어가기 ↓ low 13,605 도구 결과를 읽고 다음 한 수
567
+ 막혔을 때 ↑ high 16,384 직전 도구가 오류를 냄
1012
568
 
1013
- ```
1014
- Edit(src/app.js 2군데)
1015
- └ 2개 파일 · 3군데 (2 files · 3 sites)
1016
- ✓ src/app.js · 2군데
1017
- ✓ src/style.css · 1군데
569
+ 출력 상한은 16,384 (모르는 값이라 기본값) 안에서 나눕니다 — /out
570
+ 컨텍스트 40,960 · 지금 찬 양 2,087
1018
571
  ```
1019
572
 
1020
- **Editing is worth more than writing here.** Creating files happens once; editing happens
1021
- continuously. Six edit sites at six round trips is minutes gone.
573
+ That second-to-last line exists for a reason: **when all three caps are equal, it is the
574
+ only thing that says whether that is correct.** A low known cap makes them equal, and that
575
+ is fine. For a while all three read `16,384` always — which meant the table said nothing.
1022
576
 
1023
- | | Guaranteed |
1024
- |---|---|
1025
- | Applied in order | Editing one file twice is common. Each edit re-reads from disk, so later ones see earlier results |
1026
- | One failure | The rest still run. Stopping at the first failure re-adds the round trips this was meant to remove |
1027
- | On failure | "Resend only what failed — **and `Read` the file again first**" |
1028
- | Undo | Still **one turn**. Six sites in one file is one `/undo` |
1029
- | Single-site form | The result shape is byte-for-byte unchanged |
577
+ > **More** Context length is read off the model · /out · Truncated tool calls
578
+ >
579
+ > **[Speed and spend read →](docs/en/tuning.md#reasoning-effort)**
1030
580
 
1031
- It reports `2 files · 3 sites` rather than a single number. Editing one file at six sites
1032
- is normal, so "3 files" would be false — and once the screen stops matching what you can
1033
- count yourself, you stop trusting the screen.
581
+ ---
1034
582
 
1035
- ### Finding past conversations, and remembering decisions
583
+ ## Auto-compaction
1036
584
 
1037
- deel writes every conversation to `.deel/sessions/*.jsonl`. Until now all you could do was
1038
- list them **a record you cannot search is the same as no record.**
585
+ At 80% context, older turns are **summarised and folded** so work continues.
586
+ Plain truncation makes the model forget: it re-reads files and re-fixes what it already fixed.
1039
587
 
1040
588
  ```
1041
- $ /recall 인코딩을 어떻게
1042
-
1043
- 2026-08-01 10:15 모델 20260801-101500
1044
- CP949 인코딩 문제입니다. 읽을 때 인코딩을 재서 그대로 되돌려 쓰도록…
1045
- ```
1046
-
1047
- Korean particles are handled: `인코딩을` also matches `인코딩`. A morphological analyser is
1048
- out of the question (zero dependencies), so particle-looking tails are stripped and **both**
1049
- forms are searched. No index is built — an index inevitably goes stale, and **a stale index is
1050
- worse than none** ("not found" reads as "never happened"). Instead every search reports how
1051
- much it read and what it could not.
1052
-
1053
- `Recall` is also a **tool**. Left as a human-only command, "do it the way we decided last
1054
- time" leaves the model nothing to do but ask again.
1055
-
1056
- **Memory (`/memory`) is a different thing.** Recall has to be *searched*; memory is *already
1057
- there*. Things you cannot re-explain every session go here.
1058
-
1059
- ```
1060
- $ /memory
1061
-
1062
- 1 사내 문서는 CP949 로 읽고 CP949 로 되돌려 쓴다
1063
- 2 검증할 때 7080 포트는 쓰지 않는다
1064
-
1065
- 2줄 · 약 30토큰이 매 요청마다 함께 나갑니다
1066
- 파일 .deel/memory.md — 직접 고치셔도 됩니다
1067
- ```
1068
-
1069
- `.deel/memory.md` is **prose a human edits**, not a database. That matters: a line the model
1070
- got wrong ships on every request and keeps being wrong. **A wrong memory is worse than none.**
1071
- So `/memory 지우기 2` deletes one.
1072
-
1073
- Because it ships on every request it is bounded: 400 chars per line, 60 lines, 6,000 chars
1074
- total. Overflow drops the oldest and says so. `/context` shows its line count and tokens.
1075
-
1076
- ### Repeatable procedures become skills
1077
-
1078
- When a multi-step job finishes and it is something that will come up again, the model writes
1079
- the procedure to `.deel/skills/<name>/SKILL.md`. Next session it appears in the skill list;
1080
- when it turns out to be wrong somewhere, the model edits that file.
1081
-
1082
- No new tool needed — the existing `Write` writes it and the existing skill sweep reads it.
1083
-
1084
- ### Large files are written in pieces — `Append`
1085
-
1086
- A model with a 4k output cap still has to be able to write a 2,000-line file, eight pieces at
1087
- a time. Stitching with `Edit` does not work in practice — HTML repeats anchors like `</div>`,
1088
- so the match comes back as "found in several places", and a longer anchor eats the tokens that
1089
- should have gone into the body.
1090
-
1091
- `Write` to create, `Append` to continue. Encoding follows `Write` (CP949 for corporate
1092
- documents, the BOM on a `.csv` is preserved). The undo snapshot is taken **only on the first
1093
- `Append`** — eight appends must not leave eight copies in the history, or there is no single
1094
- point to revert to.
1095
-
1096
- ```
1097
- ⏺ Write(dashboard.html)
1098
- └ ⚠ wrote only as far as it arrived — 632 lines
1099
- ↻ the reply hit the cap — retrying with 9,984 → 16,384
1100
- ⏺ Append(dashboard.html)
1101
- └ +567 lines · 1,199 total
1102
-
1103
- ✓ dashboard.html · 1,199 lines · 97.7KB
1104
- ```
1105
-
1106
- That last line matters. If the file does not exist and the model says "created it", you would
1107
- believe it. **The real file is measured at the end of the turn.**
1108
-
1109
- ### Checklists
1110
-
1111
- Keeps the model from losing its place on multi-step work. The list is redrawn whenever the
1112
- model updates it.
1113
-
1114
- ```
1115
- ☰ Todo 1/3 done ← just finished 1
1116
-
1117
- ✓ unify log format
1118
- ▶ fix the tests
1119
- ☐ update the docs
1120
- ```
1121
-
1122
- Only one item may be **in progress** at a time; setting two is refused. Holding several at
1123
- once is how nothing gets finished.
1124
-
1125
- ### Read-only tools run together
1126
-
1127
- When the model asks for three `Read` calls at once, all three run **concurrently** — sweeping
1128
- five files costs about what reading one costs.
1129
-
1130
- ```
1131
- ◧ Read src/a.js ◧ Read src/b.js ◧ Read src/c.js together
1132
- ```
1133
-
1134
- Only `Read`, `Glob`, `Grep`, `Skill` and `WebFetch` are eligible. `Write`, `Edit` and `Bash`
1135
- always run one at a time — two concurrent writes to one file scramble the undo snapshot
1136
- order, and `Bash` can do anything. Results come back **in the order the model asked for
1137
- them**, even when they finish out of order; shuffled results confuse the model about which
1138
- result belongs to which call.
1139
-
1140
- ### Edits survive small mistakes
1141
-
1142
- Models routinely get whitespace, indentation and line endings wrong. deel relaxes matching in
1143
- stages but **refuses outright when the match is ambiguous** — silently editing the wrong place is
1144
- far worse than not finding it.
1145
-
1146
- ```
1147
- exact → ignore trailing space / CRLF → ignore indentation → ignore all whitespace
1148
- ```
1149
-
1150
- Measured with `npm run bench`:
1151
-
1152
- | | Success | Wrong place edited |
1153
- |---|---|---|
1154
- | Exact match only | 20% | 0 |
1155
- | Staged relaxation | **100%** | **0** |
1156
-
1157
- On failure it points at the closest line in the file.
1158
-
1159
- ### It shows you what it changed
1160
-
1161
- `auto` mode edits without asking. That is the speed of this tool — but if all that reaches the
1162
- screen is `1 spot`, you move on without knowing what happened. Undo is the safety net, and
1163
- **you cannot decide whether to undo something you never saw.** So every `Edit` and `Write` is
1164
- followed by the changed lines themselves.
1165
-
1166
- ```
1167
- ◈ Edit(src/runner.js)
1168
- └ 1 spot +1 −2
1169
-
1170
- 11 const id = job.id;
1171
- - console.log("start: " + id);
1172
- - console.log(" opts " + JSON.stringify(opts));
1173
- + 12 logger.info('start', { id, opts });
1174
- 13 return run(job);
1175
- ```
1176
-
1177
- The `+1 −2` next to the summary is how many lines were added and removed.
1178
-
1179
- - **Removed lines carry no line number.** They no longer exist in the file. Printing the old
1180
- number put it directly under a context line's new number — two different files' numbering in
1181
- one column. Line 8 really did appear twice on screen.
1182
- - If only the line endings changed (CRLF/LF), it says so. Otherwise every visually identical
1183
- line shows as changed and the real edit is impossible to find.
1184
- - Large files are compared after trimming the identical head and tail. If it is still too big,
1185
- exact matching is abandoned for "this whole block changed" — a rough answer now beats an
1186
- exact one later.
1187
-
1188
- How many lines are shown depends on the level. Forty lines at someone's first launch means none get read.
1189
-
1190
- | | Simple | Developer |
1191
- |---|---|---|
1192
- | After a tool call | 14 lines | 40 lines |
1193
- | `/diff <file>` | 60 lines | 200 lines |
1194
-
1195
- ### `/diff` — everything changed this session
1196
-
1197
- Those lines scroll away. `/diff` collects every file touched this session onto one page.
1198
-
1199
- ```
1200
- $ /diff
1201
-
1202
- ── files changed this session ─────────────────────────────
1203
- src/runner.js +12 −7 3×
1204
- src/logger.js +40 −0
1205
- ──────────────────────────────────────────────────────────
1206
- 2 files +52 −7
1207
-
1208
- /diff <file> for detail, /undo to revert
1209
- ```
1210
-
1211
- `/diff <file>` compares **the state at the start of the session against now.** Even after
1212
- three edits, what you want to know is "what is different from before I asked", not what the
1213
- last edit did. That original state comes from the earliest undo snapshot.
1214
-
1215
- `/diff` is **in the simple level's command list.** As long as `auto` edits without asking,
1216
- a beginner needs a way to see what changed more than anyone.
1217
-
1218
- ### With a language server, it sees meaning — `Def` and `Refs`
1219
-
1220
- `Grep` finds **text**; a language server knows **meaning**. Grep for `run` and you get the
1221
- `run` in a comment, the `run` in a third-party library, the `run` inside a string. Which of
1222
- those actually call that function is something a person has to open one by one. The model
1223
- cannot afford that, so it edits based on the first few hits, and **the ones it missed only
1224
- surface once you run the thing.**
1225
-
1226
- ```
1227
- ⏺ Refs(add_up)
1228
- add_up — used in 3 places · 2 files
1229
-
1230
- src/use.py (2)
1231
- 4: return add_up(1, 2)
1232
- 9: return add_up(x, x)
1233
- src/other.py (1)
1234
- 2: value = add_up(9, 9)
1235
- ```
1236
-
1237
- `Grep` stays. When you rename something outright you actually need it — a language server
1238
- does not look at comments, config or docs. These two **add to** what was here; they do not
1239
- replace it.
1240
-
1241
- They take a **name**, not a position. LSP asks "this file, this line, this column", but the
1242
- model does not know the column. Finding out means reading the file first, and that throws
1243
- away the whole point of the tool. So it resolves the name through `workspace/symbol` first
1244
- and asks again at that position. When a name exists in several places you get **the list** —
1245
- it does not pick one and pretend.
1246
-
1247
- ### It checks the file you just edited
1248
-
1249
- `Verify` stays too. They do different jobs.
1250
-
1251
- | | When | What |
1252
- |---|---|---|
1253
- | `Verify` | Once, before you finish | **Syntax** (`node --check`, `py_compile`) |
1254
- | Post-edit diagnostics | Right after an edit, that file only | **Meaning** (undefined names, wrong types, missing arguments) |
1255
-
1256
- Some things are syntactically fine and still wrong. `node --check` passes all of them.
1257
-
1258
- ```
1259
- ⏺ Write(pkg/bad.py)
1260
- 3 lines
1261
- language server — pkg/bad.py: 2 errors
1262
- line 1 error: Type "Literal['x']" is not assignable to declared type "int"
1263
- line 2 error: "missing_name" is not defined
1264
- ```
1265
-
1266
- Until now that only showed up **once something was run**, and running goes through user
1267
- approval, so it was several steps later. In between, the model treats that file as finished
1268
- and moves to the next one. When the error finally surfaces you have to trace back, and
1269
- tracing back costs more than the fix.
1270
-
1271
- When everything is fine it **says nothing.** A line of "0 errors" after every edit fills the
1272
- window. And not receiving diagnostics is not the same as having none — when nothing came
1273
- back, it says nothing rather than inventing an answer.
1274
-
1275
- ### It installs nothing
1276
-
1277
- **deel does not install language servers.** It scans PATH; if one is there it uses it, and
1278
- if not it falls back to `Grep` and `Outline`. This program exists for places where you cannot
1279
- bring in unapproved software, so a tool running `npm i -g` on its own is out of the question.
1280
-
1281
- With no server, `Def` and `Refs` **do not appear in the model's tool list at all** — the same
1282
- way web tools are hidden offline. Leave an unusable tool standing and the model calls it,
1283
- gets "not available", and calls it again. That round trip costs more than the schema does.
1284
-
1285
- `/lsp` shows you what is there.
1286
-
1287
- ```
1288
- $ /lsp
1289
-
1290
- ◈ 2 language server(s) on this machine
1291
- ✓ ts typescript-language-server
1292
- ✓ py pyright-langserver
1293
-
1294
- Language of this folder: py · 12 files
1295
- Tools: Def · Refs
1296
- Diagnostics after an edit: on
1297
- Turn post-edit diagnostics on or off: /lsp on · /lsp off
1298
- ```
1299
-
1300
- It looks for `ts`, `py`, `go`, `rs`, `java`, `cs`, `cpp`, `rb`, `php` and `lua`. When one is
1301
- missing it prints the install command **as text only.** Whether to run it is your call.
1302
-
1303
- <details>
1304
- <summary>Four things a real server (pyright) taught us</summary>
1305
-
1306
- A stub server alone would have shown green for all of these.
1307
-
1308
- - **Servers spell URIs differently than we do.** We send `file:///C:/…`; pyright answers with
1309
- `file:///c%3A/…` — lowercase drive letter, percent-encoded colon. Compared as strings they
1310
- never match. Diagnostics arrive correctly, are not found in our table, and become "nothing
1311
- came back" — and **saying nothing means the file is sound**, so a broken file gets reported
1312
- as fine. We compare paths, not URIs.
1313
- - **A server that just started answers empty.** Not because the name is missing but because
1314
- it has not finished indexing. Asked 0.2s after startup it said no; 0.5s later it said yes.
1315
- Turning that into "no such name" makes the model create something that already exists. So
1316
- it asks again a few times, but only while the server is young.
1317
- - **npm installs two names on Windows.** An extension-less sh script and a `.cmd`. Find the
1318
- first one and the file plainly exists, so it reports "installed" — but Windows cannot run
1319
- it. Claiming it is there and then failing is the hardest failure to spot.
1320
- - **`cmd /s /c` strips the outer pair of quotes.** Wrap the command once and it breaks
1321
- entirely, and all you see from the outside is "no language server".
1322
-
1323
- </details>
1324
-
1325
- </details>
1326
-
1327
- ---
1328
-
1329
- ## Korean text and Excel
1330
-
1331
- **A file saved as CP949 is written back as CP949.** The encoding is never changed.
1332
- Excel (`.xlsx`) is read as CSV — read-only.
1333
-
1334
- <details>
1335
- <summary><b>More</b> — Encoding · Excel</summary>
1336
-
1337
- ### Encoding — written back the way it was read
1338
-
1339
- Corporate documents are often not UTF-8. Files saved by old Windows Notepad in a legacy
1340
- codepage (CP949 in Korea, CP932 in Japan, GBK in China) are still around. Reading one as
1341
- UTF-8 garbles it completely: `한글` becomes `�ѱ�`.
1342
-
1343
- Writing is the dangerous part. Read it garbled, save it as UTF-8, and the original is gone.
1344
- So there is one rule: **write it back in the encoding it was read in.**
1345
-
1346
- Which encoding that is comes from **the file's contents, not the machine's settings.**
1347
- Each candidate is decoded strictly, then scored on whether the result looks like real text
1348
- written in that encoding. So the same CP949 document reads identically on Ubuntu, on a US
1349
- Windows machine, and on a Korean one.
1350
-
1351
- ```
1352
- › Read report.txt
1353
- └ 4 lines · CP949
1354
- ```
1355
-
1356
- If you try to insert a character that encoding **cannot hold**, it refuses instead of saving.
1357
-
1358
- ```
1359
- › Edit report.txt note → note 🚀
1360
- └ This file is CP949, and you are inserting a character that encoding does not have: 🚀
1361
- ```
1362
-
1363
- Silently substituting question marks would be worse than not writing at all.
1364
- Newly created files are UTF-8.
1365
-
1366
- Command output is handled the same way. A Windows console is not UTF-8, so taking `Bash`
1367
- output as utf8 garbles non-ASCII text. It is collected as bytes and decoded afterwards.
1368
-
1369
- **Undo snapshots are stored as bytes too.** They used to be stored as UTF-8 text, so undoing
1370
- a CP949 file brought back `가나다` (bytes `b0a1 b3aa b4d9`) as six U+FFFD characters — **the
1371
- safety net itself destroyed the original bytes.** Now every snapshot is round-tripped through
1372
- UTF-8 first; anything that does not come back identical is stored as base64 and restored
1373
- byte-exact.
1374
-
1375
- ### Excel — read as CSV
1376
-
1377
- An Excel file is a compressed archive, not text, so normally you get "this is a binary file"
1378
- and somebody has to export a CSV by hand. `Read` just does it.
1379
-
1380
- ```
1381
- › Read report.xlsx
1382
- └ 3 sheets · 128 rows · unpacked directly
1383
- ```
1384
-
1385
- - **Still zero dependencies.** An xlsx is a zip full of XML, so Node's built-in `zlib` is enough.
1386
- - Every sheet is returned. Hidden sheets too, marked as hidden.
1387
- - Dates come back as dates, not serial numbers — the cell format is read to decide.
1388
- - Formulas come back as **computed values**, and error values like `#REF!` are not dropped.
1389
-
1390
- **Password-protected files and legacy `.xls`** are handed to Excel itself; those cannot be
1391
- unpacked directly. You are asked for the password at that point.
1392
-
1393
- The password is **not stored anywhere**:
1394
-
1395
- - not in the config file
1396
- - not in the session log
1397
- - not in the audit log
1398
- - not as a command-line argument (other people can see your command lines)
1399
-
1400
- The only path out is the child process's stdin, and a test asserts that this stays true.
1401
- Extracted intermediate files are deleted after use.
1402
-
1403
- > **Excel files are read-only here.** `Edit` and `Write` refuse them, and say why and what
1404
- > to do instead. Round-tripping a file with formatting, formulas and charts through CSV
1405
- > always loses something. Better not to write than to write knowing you'll lose data.
1406
-
1407
- </details>
1408
-
1409
- ---
1410
-
1411
- ## Serving what you built
1412
-
1413
- ```
1414
- ❯ /preview
1415
-
1416
- ▶ Serving http://127.0.0.1:56801/
1417
- showing .
1418
- Edit a file and the page reloads by itself.
1419
- Only this machine can open it (127.0.0.1). No other PC can see it.
1420
- Stop with /preview off · it shuts down when deel exits.
1421
- ```
1422
-
1423
- A browser opens with it. `/preview <folder>` picks what to serve, `/preview off` stops it.
1424
-
1425
- **This is not the same as double-clicking the file (`file://`).** Under `file://` everything
1426
- below is blocked — and the error only shows up in the console while the page stays blank, so
1427
- you end up suspecting your own code. This is a real HTTP server, so it all works:
1428
-
1429
- | | `file://` | `/preview` |
1430
- |---|---|---|
1431
- | `<script type="module">` · `import` | blocked (CORS) | **works** |
1432
- | `fetch('./data.json')` | blocked | **works** |
1433
- | `new Worker(...)` | blocked | **works** |
1434
- | `WebAssembly.compileStreaming` | blocked (MIME) | **works** |
1435
- | textures · `getImageData` | tainted canvas | **works** |
1436
- | `.glb` / `.gltf` (Three.js) | no MIME type → silently not drawn | **works** |
1437
-
1438
- All seven were run in a real Chrome and confirmed **7/7**.
1439
-
1440
- Apps with a router (React Router and friends) get the first page back when you reload on a
1441
- deep link. Never for requests with an extension (`app.js`) though — returning HTML for a
1442
- missing script dies with `Unexpected token '<'`, which hides the real cause (a typo in a filename).
1443
-
1444
- ### It opens exactly as much as it says
1445
-
1446
- Starting a server means opening your disk to somebody else.
1447
-
1448
- - Bound to **`127.0.0.1` only**. `0.0.0.0` is not available at all — on an office network
1449
- that would let anyone read your source.
1450
- - Port **0** (the kernel hands out a free one). A fixed port steals someone else's.
1451
- - Paths cannot leave the working scope. `../` · `%2e%2e` · double encoding · absolute paths ·
1452
- null bytes · symlinks — eight of these are held shut by tests.
1453
- - **It only serves.** `POST` · `PUT` · `DELETE` are refused with 405.
1454
- - It shuts down when `deel` exits.
1455
-
1456
- ---
1457
-
1458
- ## Skills and plugins
1459
-
1460
- **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
1461
- whatever is there. On a clean PC: zero. On a PC with skills installed: those skills.
1462
-
1463
- ```
1464
- project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
1465
- user ~/.deel/skills ~/.claude/skills ~/.claude/commands
1466
- plugins ~/.claude/plugins/** ~/.deel/plugins/**
1467
- ```
1468
-
1469
- Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
1470
-
1471
- <details>
1472
- <summary><b>More</b> — Loaded in three stages · Fetching plugins · Deliberately not included</summary>
1473
-
1474
- ### Loaded in three stages
1475
-
1476
- Loading everything would blow the context window.
1477
-
1478
- | Stage | What | Cost |
1479
- |---|---|---|
1480
- | 1 | Name + one-line description in the prompt | ~1,800 tokens for 40 skills |
1481
- | 2 | Body of the one the model picks via `Skill` | one at a time |
1482
- | 3 | Files that body references, via `Read` | on demand |
1483
-
1484
- ### Fetching plugins
1485
-
1486
- ```bash
1487
- # on a connected machine
1488
- /plugin install affaan-m/ECC # git clone, or tarball when git is absent
1489
- /plugin pack import.zip # bundle, excluding executable scripts
1490
-
1491
- # on the air-gapped machine — just unzip
1492
- unzip import.zip -d ~/.deel/plugins/
1493
- ```
1494
-
1495
- `/plugin pack` omits `.js` `.sh` `.ps1` `.py` and friends, and includes a plain-text manifest
1496
- with a licence table — ready to hand to a security reviewer.
1497
-
1498
- ### Deliberately not included
1499
-
1500
- | | Why |
1501
- |---|---|
1502
- | hooks | Executable scripts — fails import review, widens the blast radius of autonomy |
1503
- | sub-agents | Doubles model calls against a gateway quota |
1504
- | MCP | A separate protocol; a project of its own |
1505
-
1506
- </details>
1507
-
1508
- ---
1509
-
1510
- ## Reasoning effort
1511
-
1512
- One answer means several model calls, and **each needs a different amount of thinking.**
1513
- All-high is slow; all-low wanders off.
1514
-
1515
- The default is **one line**. What you want to know is how hard it is thinking right now,
1516
- not a stage table.
1517
-
1518
- ```
1519
- $ /think
1520
-
1521
- 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
1522
- 더 세게 /think high 더 빠르게 /think low
1523
- ```
1524
-
1525
- | Profile | Character |
1526
- |---|---|
1527
- | `even` | Same effort everywhere — predictable, slower |
1528
- | `save` (default) | Hard on the first decision only |
1529
- | `deep` | Everything one notch up — for hard work |
1530
-
1531
- Set the profile with `/think 배분 절약`. **Level and profile are different axes, so the
1532
- commands were split** — `/think high` and `/think save` used to set different things under
1533
- one name, which made the screen unreadable.
1534
-
1535
- The stage table moved to `/think 자세히` (the default at developer level).
1536
-
1537
- ```
1538
- $ /think 자세히
1539
-
1540
- 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
1541
- 배분 절약 첫 판단만 세게, 이어가기는 얕게 — 대개 이게 낫습니다
1542
-
1543
- 단계 강도 출력상한 언제
1544
- 첫 판단 · medium 15,549 무엇을 할지 정하는 자리
1545
- 이어가기 ↓ low 13,605 도구 결과를 읽고 다음 한 수
1546
- 막혔을 때 ↑ high 16,384 직전 도구가 오류를 냄
1547
-
1548
- 출력 상한은 16,384 (모르는 값이라 기본값) 안에서 나눕니다 — /out
1549
- 컨텍스트 40,960 · 지금 찬 양 2,087
1550
- ```
1551
-
1552
- That second-to-last line exists for a reason: **when all three caps are equal, it is the
1553
- only thing that says whether that is correct.** A low known cap makes them equal, and that
1554
- is fine. For a while all three read `16,384` always — which meant the table said nothing.
1555
-
1556
- <details>
1557
- <summary><b>More</b> — Context length is read off the model · /out · Truncated tool calls</summary>
1558
-
1559
- ### Context length is read off the model
1560
-
1561
- This one number sizes the whole program: how many files fit in one read, when the
1562
- conversation gets folded, how long a single reply may be — **all of it comes from here.**
1563
-
1564
- So deel asks the server on every launch rather than trusting the saved value. The same
1565
- model name can be loaded at a different length each time, and if that difference never
1566
- reaches the screen there is no way to notice. **It just quietly gets smaller.**
1567
-
1568
- ```
1569
- │ Model qwen3-coder (640k tokens) │
1570
- ╰─────────────────────────────────────────────────────────╯
1571
- ✓ Context adjusted 32,768 → 655,360 (read from LM Studio)
1572
- ```
1573
-
1574
- Every server puts this number under a different name in a different place, so deel checks
1575
- all of them.
1576
-
1577
- | Server | Where it reads |
1578
- |---|---|
1579
- | LM Studio | `/api/v0/models` — `max_context_length`, `loaded_context_length` |
1580
- | llama.cpp | `/props` — `n_ctx` |
1581
- | vLLM | `/v1/models` — `max_model_len` |
1582
- | Ollama | `/api/show` — `<model>.context_length` |
1583
- | Other OpenAI-compatible | `/v1/models/<model>` — `context_window`, `context_length`, `max_input_tokens`, `max_position_embeddings` (found even when nested) |
1584
-
1585
- **Model maximum and loaded length are not the same thing.** LM Studio will happily load a
1586
- 655,360-capable model at 8,192. Trusting the maximum there gets your requests rejected. So
1587
- the **loaded length is what deel uses**, and the maximum is reported separately.
1588
-
1589
- ```
1590
- ⚠ This model goes up to 655,360 — raise it on the server, then /ctx auto
1591
- ```
1592
-
1593
- | Command | What it does |
1594
- |---|---|
1595
- | `/ctx` | Current length and remaining room |
1596
- | `/ctx auto` | Ask the server again and match the model |
1597
- | `/ctx 655360` | Set it yourself (`640k`, `128k`, `1m` also work) |
1598
- | `/ctx 자세히` | Which endpoints were probed and what each returned — how to see why a lookup failed |
1599
- | `deel --ctx 655360` | Start at this value (skips the lookup) |
1600
-
1601
- **`k` means 1024 here.** Context lengths are all powers of two, so that is the only base
1602
- that lines up: 655,360 is `640k`, not `655k`; 131,072 is `128k`, not `131k`. The display and
1603
- `/ctx` use the same unit, so typing back what you see gives you the same number.
1604
-
1605
- ### Reply length cap — `/out`
1606
-
1607
- Context (how much can be held) and the **output cap** (how much can come back at once) are
1608
- different numbers. Treating them as one makes it impossible to understand why a large file
1609
- never gets written — the context is roomy while the reply is being cut.
1610
-
1611
- | Command | What it does |
1612
- |---|---|
1613
- | `/out` | Current cap and **where it came from** (set by you / discovered / default) |
1614
- | `/out 32k` | Set it yourself (`k` is 1024). Saved to the profile |
1615
- | `/out auto` | Drop your value and go back to the discovered one, or the default |
1616
- | `deel --max-tokens 65536` | Start at this value |
1617
-
1618
- The old name `/ctx out 32k` still works.
1619
-
1620
- **Caps are not fixed numbers.** They are computed from the model's context window and how
1621
- much of it is currently used — the profile decides what share of the remaining room a stage gets.
1622
-
1623
- | Model | First call | Continuing | Stuck | Retry after truncation |
1624
- |---|---|---|---|---|
1625
- | 2k local | 819 | 716 | 921 | 1,638 |
1626
- | 8k local | 3,276 | 2,867 | 3,686 | 6,553 |
1627
- | 40k (qwen3) | 16,384 | 14,336 | 16,384 | 16,384 |
1628
- | 128k gateway | 16,384 | 16,384 | 16,384 | 16,384 |
1629
- | 128k, 80% full | 10,485 | 9,174 | 11,796 | 16,384 |
1630
- | 640k with `/out 65536` | 65,536 | 65,536 | 65,536 | 65,536 |
1631
-
1632
- Caps shrink as the context fills. Handing a 4k model a 4,096-token cap would leave no room for input.
1633
-
1634
- The last row is the point: **a known cap overrides the 16,384 default.** For a while it did
1635
- not — the third argument of `Math.min(cap, max ?? 16384, 16384)` clamped it right back, so a
1636
- configured cap could only be lowered, never raised. Meanwhile the comment, the README, and the
1637
- on-screen help all said it could be raised. A documented escape hatch that is welded shut is
1638
- the worst kind.
1639
-
1640
- If a cap truncates a reply, **the call is retried with the cap lifted** — and the thinking
1641
- level drops one notch, because reasoning tokens eat the same budget first. Without that, more
1642
- headroom just buys more thinking. A truncated reply means a half-written tool call, which fails silently.
1643
-
1644
- **When the server refuses, it is read for the answer.**
1645
-
1646
- ```
1647
- This model's maximum context length is 8192 tokens, however you requested 41003
1648
- ```
1649
-
1650
- The number is extracted, applied, and the call is retried. You never see the failure.
1651
- No spec knowledge is needed, so **this works against servers we have never seen.**
1652
-
1653
- ### Truncated tool calls
1654
-
1655
- This actually happened. A user asked for a dashboard; the model tried to put an entire HTML
1656
- document into `Write`'s arguments, hit the output token limit, and the arguments JSON arrived
1657
- cut off mid-string.
1658
-
1659
- The old code quietly turned that unparseable JSON into `{_raw: "..."}` and handed it to the
1660
- tool. The tool answered `path is empty` — **a message with nothing to do with the real cause.**
1661
- The model had not omitted the path, so there was nothing to fix; it retried identically, and
1662
- was truncated again.
1663
-
1664
- ```
1665
- ◆ Write(dashboard.html)
1666
- └ path is empty ← nine identical times
1667
-
1668
- ── 71s · 13 tool calls · context filled and auto-compacted · no file produced
1669
- ```
1670
-
1671
- One silently swallowed value produced all of that. What happens now:
1672
-
1673
- | | Now |
1674
- |---|---|
1675
- | Unparseable arguments | **Marked as truncated**, not swallowed. Never passed to the tool |
1676
- | The model is told | Exactly what happened, and to stop resending the whole thing — write a short skeleton first, then **build it up with `Edit`** |
1677
- | The truncated payload | Never re-injected into the conversation — it is half a payload and it costs context |
1678
- | Detecting truncation | Even when the gateway reports `finish_reason: "stop"`, **broken arguments are themselves proof it was cut.** No model writes half a JSON object on purpose |
1679
- | Three identical failures | The turn stops, with a suggestion to split the request |
1680
-
1681
- ```
1682
- ⊘ Stopped — it is spinning in the same place.
1683
- the same tool call keeps getting truncated
1684
- What you asked for in one go is larger than the model's output cap. Try splitting it —
1685
- e.g. "just the skeleton first" → "now add the table" → "now add the chart"
1686
- ```
1687
-
1688
- **A step limit (`maxSteps`) cannot catch this.** It cannot tell a long healthy task from a
1689
- spinning one. What is counted here is not steps but **how many times the same tool failed for
1690
- the same reason.**
1691
-
1692
- </details>
1693
-
1694
- ---
1695
-
1696
- ## Auto-compaction
1697
-
1698
- At 80% context, older turns are **summarised and folded** so work continues.
1699
- Plain truncation makes the model forget: it re-reads files and re-fixes what it already fixed.
1700
-
1701
- ```
1702
- ◱ Folded 44 turns into a summary — 10,399 → 3,170 tokens (70% smaller)
589
+ Folded 44 turns into a summary — 10,399 → 3,170 tokens (70% smaller)
1703
590
  ```
1704
591
 
1705
592
  The summary keeps goal / done / learned / decided / remaining. The cut point is chosen so a
@@ -1758,29 +645,9 @@ The model sees it as `mcp__wiki__search`. `/mcp` shows what is attached.
1758
645
  **Dependencies stay at zero.** The stdio transport is nothing but newline-delimited JSON-RPC
1759
646
  2.0 over a child process's stdin/stdout, so `child_process` and `JSON` cover it. No SDK.
1760
647
 
1761
- <details>
1762
- <summary><b>More</b> — But this is somebody else's program</summary>
1763
-
1764
- ### But this is somebody else's program
1765
-
1766
- This project exists because unapproved software is blocked. Turning on MCP carelessly would
1767
- tear down that line with our own hands. So:
1768
-
1769
- | | |
1770
- |---|---|
1771
- | **Off by default** | Nothing runs unless it is in `.deel/mcp.json` |
1772
- | **Never under `--offline`** | We cannot police where a child process connects. **We do not claim to block what we cannot block** |
1773
- | **Outside the working scope** | MCP servers do not honour our fence. The `/mcp` screen says so |
1774
- | **Audited** | What was launched and what was called, in `.deel/audit.jsonl` |
1775
- | **No key passthrough** | Our environment is not forwarded wholesale — a `DEEL_*` gateway key in someone else's process goes somewhere we cannot see |
1776
- | **Not in read-only modes** | A tool named "search" can still write files. Handing an unknown to plan/architect mode would make that promise meaningless |
1777
- | **24 tools per server** | Schemas ship on every request. Past that they are dropped, and **the drop is reported** |
1778
-
1779
- One server crashing, hanging, or talking nonsense does not affect the others. Failures are not
1780
- swallowed — the reason appears in the header, because a silent drop leaves "why is that tool
1781
- missing?" unanswerable.
1782
-
1783
- </details>
648
+ > **More** — But this is somebody else's program
649
+ >
650
+ > **[Extending read →](docs/en/extend.md#attaching-tools-from-outside-mcp)**
1784
651
 
1785
652
  ---
1786
653
 
@@ -1813,30 +680,9 @@ stdio. It is not a command you type yourself.
1813
680
  **Still zero dependencies.** Same reason as MCP — newline-delimited JSON-RPC 2.0 is the whole
1814
681
  transport, so no SDK is needed.
1815
682
 
1816
- <details>
1817
- <summary><b>Details</b> — the places this breaks silently</summary>
1818
-
1819
- This protocol fails quietly. The editor shows "the agent is not responding" and nothing
1820
- anywhere explains why. So these are nailed down by tests (`test/acp.test.js` spawns a real
1821
- process and talks over a real pipe).
1822
-
1823
- | The place | Why it matters |
1824
- |---|---|
1825
- | **Nothing but ACP on stdout** | The spec says `MUST NOT`. deel has dozens of places that print to the screen; one of them firing in this mode breaks the pipe. Rather than guarding each call site, **the pipe itself is swapped out** — so code written later is safe without knowing about this. What gets printed is not dropped, it goes to stderr |
1826
- | **Korean split across chunk boundaries** | Pipes break on bytes, not characters. Decoding each chunk separately turns `안녕` into `안<?>하` — and **the JSON still parses**, so no error is raised. The characters are quietly mangled |
1827
- | **A request with `id: 0`** | ACP clients count from zero. Reading `if (msg.id)` treats the very first `initialize` as a notification and never answers — it hangs the moment it connects |
1828
- | **Cancellation reaching a running turn** | Cancellation always arrives while something is running; that is what cancellation is. Awaiting each incoming line in order means it **never arrives** |
1829
- | **When permission cannot be asked** | It is tempting to just run the tool — otherwise nothing works against a client that has not built the approval dialog yet. But that means "if I can't ask, I do as I please". **It does not** |
1830
-
1831
- **What it does not do yet, stated plainly:**
1832
-
1833
- | | |
1834
- |---|---|
1835
- | `session/load` | Restoring a past conversation means replaying every message as an update. Half-built, the editor opens an empty conversation and the user assumes the history is gone. It reports **`loadSession: false`** |
1836
- | Image / audio attachments | Most local models cannot read them. Rather than dropping them silently, deel tells the model what it could not read |
1837
- | MCP servers passed in by the editor | Not launched. That would mean **deel spawning processes named in the editor's config**. "What does this tool launch?" is the first question in a corporate review, and "whatever the editor says" is not an acceptable answer. Only `.deel/mcp.json`, written by a person, is launched |
1838
-
1839
- </details>
683
+ > **More** — Details — the places this breaks silently
684
+ >
685
+ > **[Extending read →](docs/en/extend.md#inside-your-editor-acp)**
1840
686
 
1841
687
  ---
1842
688
 
@@ -1913,78 +759,9 @@ Undo history stores whole file contents, so repeated edits to large files add up
1913
759
  it keeps the **most recent 50 turns** and drops the rest. What you just did is always
1914
760
  undoable; `/status` shows how large the history currently is.
1915
761
 
1916
- <details>
1917
- <summary><b>More</b> — Files removed through Bash come back too · What it will not read</summary>
1918
-
1919
- ### Files removed through `Bash` come back too
1920
-
1921
- The safety net covered `Write` and `Edit` only. But a model moving a file reaches for
1922
- `Bash` — `mv old.js new.js`, `rm temp.txt`. The file was gone and `/undo` could do
1923
- nothing about it. It was half a safety net.
1924
-
1925
- Now a mutating command snapshots the files it names beforehand, and **says what it saved,
1926
- right there**.
1927
-
1928
- ```
1929
- ▶ Bash(mv src/old.js src/new.js)
1930
- └ 성공
1931
- ↩ src/old.js 는 떠 뒀습니다 — /undo 로 되돌아갑니다
1932
- (saved src/old.js — /undo restores it)
1933
- ```
1934
-
1935
- `mv` and `rm` leave one "success" line on screen. From that line alone there is no way to
1936
- tell whether it is reversible, so people either assume it is and move on, or assume it is
1937
- not and get scared. So the fact is stated.
1938
-
1939
- **What cannot be saved is not hidden.** Shell-expanded wildcards (`rm *.tmp`), deletions
1940
- inside a script, and whole directories are invisible here. In those cases the `↩` line
1941
- simply does not appear — **it never claims "everything is reversible"**. False reassurance
1942
- means people stop checking.
1943
-
1944
- Snapshotting casts a **wider** net than blocking does. The scope guard (`checkPaths`) only
1945
- treats words containing a slash as paths — anything else would block legitimate commands —
1946
- but `del target.txt`, with no slash, is the most common form there is. This is a reading
1947
- site rather than a blocking one, so it scans broadly and saves a file only when one is
1948
- actually there. A wrong guess costs nothing. Up to 24 per command.
1949
-
1950
- ### What it will not read
1951
-
1952
- Walking a folder turns up things that are not project files: the private stores other coding
1953
- tools keep — past conversations, command history, caches, and keys. They have nothing to do
1954
- with the task, but once they appear in a listing the model reads them first.
1955
-
1956
- ```
1957
- ◧ Read(~/.deel/audit.jsonl) 77 lines
1958
- ◧ Read(~/.claude/history.jsonl) 35 lines
1959
- ```
1960
-
1961
- The audit log is **this program's own record of what it just did.** Reading it back into the
1962
- conversation makes the model chase its own shadow. It has nothing to do with what was asked,
1963
- and it fills the context.
1964
-
1965
- The config file is worse. `.deel/config.json` holds the gateway **API key.** Reading it puts
1966
- that key into the conversation, sends it to the model, and writes it into the on-disk session
1967
- log. It hands the key to the very service the key is for.
1968
-
1969
- | Refused | Why |
1970
- |---|---|
1971
- | `.deel/config.json` | Contains the gateway key |
1972
- | `.deel/audit.jsonl` · `.deel/sessions` · `.deel/history` | deel's own records — chasing its own shadow |
1973
- | `.claude` `.codex` `.cursor` `.gemini` `.aider` `.continue` `.cline` `.roo` `.kilocode` `.windsurf` `.opencode` `.zed` `.trae` `.augment` `.qodo` `.tabnine` `.cody` `.sourcegraph` `.copilot` `.amazonq` `.junie` `.codeium` `.goose` `.crush` `.gptme` `.openhands` `.devin` | Other tools' private stores |
1974
- | File-style leftovers like `.aider.chat.history.md` | Same reason |
1975
-
1976
- Writing is blocked too, not just reading. Blocking only reads would still let the agent
1977
- overwrite another tool's settings, and overwriting `.deel/config.json` destroys the connection.
1978
-
1979
- **It blocks, it does not hide** — the refusal says exactly why. New tools keep appearing;
1980
- when a name is missing from the list, **adding it is one line.**
1981
-
1982
- That list lives in **exactly one place in the source.** The directory walker (`SKIP_DIRS`) and
1983
- the read guard look at the same set. They used to be two copies, and two copies means the day
1984
- comes when only one of them learns a new name — a folder that is skipped while walking but
1985
- readable if you name it directly, which is very hard to explain.
1986
-
1987
- </details>
762
+ > **More** — Files removed through Bash come back too · What it will not read
763
+ >
764
+ > **[Safety and corporate review read →](docs/en/safety.md#safety)**
1988
765
 
1989
766
  ---
1990
767
 
@@ -2027,31 +804,9 @@ drift, and **a review document that drifts is worse than none.** Find one wrong
2027
804
  reviewer stops trusting the rest. The audit-log spec is the one hand-written part, so a test
2028
805
  checks it against real log records on every run.
2029
806
 
2030
- <details>
2031
- <summary><b>More</b> — Diagnosing a corporate gateway</summary>
2032
-
2033
- ### Diagnosing a corporate gateway
2034
-
2035
- ```bash
2036
- node bin/deel.js diagnose --url <gateway> --key <key> --model <model> --out report.txt
2037
- ```
2038
-
2039
- Hand over `report.txt` alone — plain text, no colour codes.
2040
-
2041
- | Check | Why it matters |
2042
- |---|---|
2043
- | Basic chat | Address, key and model name are right |
2044
- | System message | Rules (`DEEL.md`) and skills take effect |
2045
- | Streaming | Output can flow token by token |
2046
- | **Tool calls** | **Whether it can read and edit files — the critical one** |
2047
- | **Tool results** | **Whether multi-turn works — the premise of the agent loop** |
2048
- | Structured output | Edit format can be enforced by schema |
2049
- | Reasoning control | Whether `/think` works at the model layer |
2050
- | Context length | How many files can be read at once |
2051
-
2052
- Verdict is one of **ready · limited · blocked · unreachable**.
2053
-
2054
- </details>
807
+ > **More** — Diagnosing a corporate gateway
808
+ >
809
+ > **[Safety and corporate review read →](docs/en/safety.md#corporate-review-package)**
2055
810
 
2056
811
  ---
2057
812
 
@@ -2059,54 +814,9 @@ Verdict is one of **ready · limited · blocked · unreachable**.
2059
814
 
2060
815
  Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder takes precedence.
2061
816
 
2062
- <details>
2063
- <summary><b>More</b> — Supported servers · Environment variables · Flags · Project rules</summary>
2064
-
2065
- ### Supported servers
2066
-
2067
- | | Example address |
2068
- |---|---|
2069
- | Corporate AI gateway (OpenAI-compatible) | `https://ai-gw.example.corp/v1` |
2070
- | Ollama | `http://localhost:11434` |
2071
- | LM Studio | `http://localhost:1234/v1` |
2072
- | llama.cpp · vLLM · LiteLLM | `http://host:port/v1` |
2073
-
2074
- Auth style is detected automatically: `Authorization: Bearer` → `x-api-key` → `api-key` (Azure) → none.
2075
-
2076
- ### Environment variables
2077
-
2078
- | Variable | Use |
2079
- |---|---|
2080
- | `DEEL_API_KEY` | Keep the key out of the config file (takes precedence) |
2081
- | `DEEL_KEY_<PROFILE_ID>` | Per-profile key |
2082
- | `NODE_EXTRA_CA_CERTS` | Corporate TLS certificate |
2083
- | `HTTPS_PROXY` | Behind a proxy |
2084
- | `DEEL_DEBUG=1` | Verbose errors |
2085
- | `NO_COLOR` | Disable colour |
2086
-
2087
- ### Flags
2088
-
2089
- ```bash
2090
- deel --root <folder> Working scope. Defaults to the current folder
2091
- deel --mode <mode> auto (default) / confirm / strict
2092
- deel --work <mode> auto (default) / code / plan / architect / debug / ask / orchestrator
2093
- deel --level <level> 쉬움 (simple) / 개발자 (developer)
2094
- deel --ctx <length> Set the context length yourself (655360 · 640k · 128k)
2095
- deel --max-tokens <len> Cap on a single reply (32k) — same value as /out
2096
- deel --think <level> off / low / medium (default) / high / max
2097
- deel --effort <profile> even / save (default) / deep
2098
- deel --offline Nothing leaves this machine
2099
- deel --continue Resume the most recent conversation
2100
- deel --resume <id> Resume a specific one
2101
- deel --no-tui Turn the input box off; plain scrolling view (see below)
2102
- ```
2103
-
2104
- ### Project rules
2105
-
2106
- If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as project rules.
2107
- `/init` scaffolds one.
2108
-
2109
- </details>
817
+ > **More** — Supported servers · Environment variables · Flags · Project rules
818
+ >
819
+ > **[Configuration read →](docs/en/config.md#configuration)**
2110
820
 
2111
821
  ---
2112
822
 
@@ -2173,436 +883,23 @@ so one run tells you everything.
2173
883
  | `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
2174
884
  | `edit-bench` | 20 cases | Edit success rate |
2175
885
 
2176
- <details>
2177
- <summary><b>More</b> — Coverage · Layout</summary>
2178
-
2179
- ### Coverage
2180
-
2181
- ```bash
2182
- npm run coverage Summary
2183
- node test/coverage.mjs --file src/repl.js One file in detail
2184
- node test/coverage.mjs --json Machine-readable
2185
- ```
2186
-
2187
- Zero dependencies rules out c8 and nyc, so this reads Node's own
2188
- `NODE_V8_COVERAGE` instead — nothing new to get through an import review. It picks up
2189
- child processes too, so the `cli` suite that spawns `deel` counts like everything else.
2190
-
2191
- Currently **92% overall** (7,056 of 7,646 lines). Three files are deliberately left short.
2192
-
2193
- | File | Now | Why it stops there |
2194
- |---|---|---|
2195
- | `tools/excel.js` | 67% | The password path needs Excel installed and a genuinely encrypted file. Faking it would produce a test that only *looks* like it passes |
2196
- | `repl.js` | 77% | The keypress paths — Shift+Tab, Ctrl+C, password entry, paste. Reaching them needs a pty, and a pty is a dependency. What the screen *prints* is measured instead, as a value (`ui` and `tui` suites) |
2197
- | `plugins/manage.js` | 79% | The GitHub download path. **Tests not reaching the network** matters more. Folder installs are covered |
2198
-
2199
- ### Layout
2200
-
2201
- ```
2202
- bin/deel.js entry point
2203
- src/
2204
- repl.js the conversation screen — what a person faces
2205
- oneshot.js run once and exit (-p)
2206
- commands.js 35 slash commands
2207
- setup.js first-run connection setup
2208
- config.js reading and writing config
2209
-
2210
- ui/ansi.js colour · East Asian width
2211
- ui/screen.js picking a screen (line mode / box mode)
2212
- ui/inputbox.js the box at the bottom — overwrite-in-place, cursor position
2213
- ui/status.js status line — model, context, mode, approvals
2214
- ui/working.js working phrases — they follow what is happening
2215
- ui/motion.js the braille drawing next to the phrase
2216
- ui/approve.js approval mode display (auto / risky only / everything)
2217
- ui/diff.js showing what changed, where it changed
2218
- ui/wrap.js wrapping to width without breaking colour
2219
- ui/level.js simple vs developer
2220
-
2221
- agent/loop.js the agent loop
2222
- agent/session.js conversation state + context accounting
2223
- agent/modes.js work modes (auto · code · plan · architect · debug · ask · orchestrator)
2224
- agent/route.js picking the mode from what was said
2225
- agent/effort.js per-stage reasoning effort
2226
- agent/budget.js shares that follow the window — lines read, description length, steps
2227
- agent/project.js working out what kind of project this folder is
2228
- agent/compact.js summarising compaction
2229
- agent/store.js saving and resuming conversations
2230
- agent/recall.js searching past conversations (no index, within budget)
2231
- agent/memory.js what outlives the conversation
2232
- agent/mention.js attaching files with `@`
2233
-
2234
- backend/http.js the single HTTP layer (the only door out)
2235
- backend/detect.js protocol and auth detection
2236
- backend/adapter.js absorbing OpenAI/Ollama differences + streaming parser
2237
- backend/ctxsize.js reading context length off the model
2238
- backend/probe.js 8 diagnostic checks
2239
- backend/scan.js scanning for local servers
2240
- backend/mcp.js attaching outside tools (MCP, stdio)
2241
-
2242
- tools/index.js 17 tools
2243
- tools/edit-match.js staged-relaxation edit matching
2244
- tools/outline.js a file's shape, cheaply
2245
- tools/verify.js checking what was built
2246
- tools/task.js splitting big work off
2247
- tools/jobs.js commands that run in the background
2248
- tools/todo.js checklists
2249
- tools/webfetch.js reading the web (read-only)
2250
- tools/encoding.js writing back in the encoding it was read in
2251
- tools/xlsx.js Excel → CSV (written here)
2252
- tools/lsp.js Def · Refs — asking the language server
2253
-
2254
- lsp/rpc.js LSP framing (Content-Length + JSON-RPC, written here)
2255
- lsp/servers.js finding installed language servers (installs nothing)
2256
- lsp/client.js one server: spawn, talk, time out, clean up
2257
- lsp/diag.js is the file you just edited sound?
2258
-
2259
- preview/serve.js serving what you built (127.0.0.1 only)
2260
- skills/discover.js finding skills, commands and plugins on the machine
2261
- plugins/manage.js installing, removing and packing plugins
2262
- pack/zip.js ZIP writing (written here, keeps non-ASCII names)
2263
- pack/tar.js TAR reading (written here)
2264
- pack/selfpack.js review dossier + source bundle
2265
-
2266
- safety/network.js the lock on the way out
2267
- safety/guard.js working scope + dangerous-command blocking
2268
- safety/undo.js snapshots and undo
2269
- safety/audit.js recording what happened, and when
2270
- test/ tests (excluded from the published package)
2271
- ```
2272
-
2273
- </details>
886
+ > **More** — Coverage · Layout
887
+ >
888
+ > **[Development read →](docs/en/develop.md#development)**
2274
889
 
2275
890
  ---
2276
891
 
2277
892
  ## Release notes
2278
893
 
2279
- <details>
2280
- <summary><b>▸ 1.4.0 — deel gets a face, speaks English, and sees meaning</b> · what changed in seven places</summary>
2281
-
2282
- <br>
2283
-
2284
- | | Before | After |
2285
- |---|---|---|
2286
- | `/undo` | rolled back files only — the conversation still believed it happened | rewinds **the conversation too** |
2287
- | Turns that take minutes | you sat watching the screen | a bell and the window title — you can be in another window |
2288
- | Startup · status bar | looked like any other CLI | letters that grow on start, a status bar that names the boundary |
2289
- | English speakers | the screen was all unreadable | `/lang en` — the screen **and what the model reads** |
2290
- | Models per session | one | a different model per chunk — routine work to a small one |
2291
- | Finding a name | `Grep` only — comments and strings mixed in | `Def` / `Refs` — by **meaning**, when a server is installed |
2292
- | A file you just edited | you found out by running it | checked **right after** the edit, that file only |
2293
-
2294
- <br>
2295
-
2296
- #### 1. `/undo` rewinds the conversation
2297
-
2298
- Roll back only the files and the conversation still holds the edit. The model believes it
2299
- just changed that file and builds the next step on top — and nothing on screen says
2300
- otherwise. Now the messages fold back with the files. Folding can orphan a tool call, which
2301
- the server answers with a 400, so the same `repairToolPairs` runs over the result.
2302
-
2303
- #### 2. It tells you when it is done — `/bell`
2304
-
2305
- A local model can take minutes per turn. A bell and the window title say when it finishes.
2306
- The bell is ``, but **not one byte reaches a pipe** — with no TTY it writes nowhere. The
2307
- title ends with ST, not BEL; ending with BEL rings the bell on every title update.
2308
-
2309
- #### 3. A screen that is deel's own
2310
-
2311
- On start, `deel` grows into `deel-local`. The status bar names the boundary you are inside
2312
- with one glyph (`⌂` this folder · `↗` outside · `?` unknown). The animation uses braille and
2313
- box-drawing only — emoji and geometric shapes are East Asian Ambiguous, so their width
2314
- varies per terminal and the line drifts by a column.
2315
-
2316
- #### 4·5. English on screen, and in what the model reads
2317
-
2318
- `/lang en` switches the screen. Untranslated strings come through in Korean rather than as
2319
- blanks, and `/lang` counts honestly how many are left.
2320
-
2321
- But switching only the screen leaves the model answering in Korean — its rules say to. So
2322
- what the model reads switches too (base rules, mode instructions, all sixteen tool
2323
- descriptions). There is a bonus: Korean costs about one token per character and English
2324
- about one per 3.6, so the fixed share of a 32k window dropped from **4,910 to 3,446 tokens.**
2325
-
2326
- Tool names and argument names are **not** translated. Those are identifiers.
2327
-
2328
- #### 6. Several models in one session — `Task`'s `모델`
2329
-
2330
- A large model and a small one, together, on 8GB of RAM. Routine work (formatting, repetitive
2331
- edits, short summaries) goes to the small one; you keep what needs judgement. The subtask's
2332
- endpoint opens through `allowTemporarily` and **always closes in `finally`** — afterwards
2333
- exactly one endpoint is open again.
2334
-
2335
- #### 7. Language servers — `Def`, `Refs`, post-edit diagnostics
2336
-
2337
- See "With a language server, it sees meaning" above. **It installs nothing.**
2338
-
2339
- </details>
2340
-
2341
- <details>
2342
- <summary><b>▸ 1.3.0 — evidence instead of claims, the editor instead of a terminal</b> · what changed in six places</summary>
2343
-
2344
- <br>
2345
-
2346
- | | Before | After |
2347
- |---|---|---|
2348
- | Rules that must hold | **vanished** when folded or summarised | live outside the message list, where folding cannot reach |
2349
- | This model's habits | the prompt **asked** it to behave | the harness changes instead — no cooperation needed |
2350
- | Finishing | "all done" | what changed, and what proves it. **Including what doesn't** |
2351
- | Where you use it | one more terminal window | inside your editor (Zed · JetBrains · Neovim · Emacs) |
2352
- | Review paperwork | one document, for humans | SBOM · egress list · audit spec — **straight into a scanner** |
2353
- | Keys | one `env` and they were in the conversation and on disk | masked where output enters. Files are **deliberately** left alone |
2354
-
2355
- <br>
2356
-
2357
- #### 1. Rules that must hold were vanishing into the fold — `/pin`
2358
-
2359
- Long conversations fold and summarise earlier turns to make room. A 2026 measurement found
2360
- **summarisation preserves only about 50% of safety constraints.** If "never touch this
2361
- folder" lands in the missing half, the model is in a state where it was never told. Nothing
2362
- appears on screen.
2363
-
2364
- Pinned lines are **not kept with the messages.** They are appended to the end of the system
2365
- prompt — and since folding and compaction only touch messages, they are structurally out of
2366
- reach. Not carefully preserved: **impossible to remove.**
2367
-
2368
- ```
2369
- /pin never touch src/legacy
2370
-
2371
- ✓ Pinned — 2 now (78 tokens)
2372
- ```
2373
-
2374
- Up to 12 lines / 240 tokens. Past that it says so and carries the most recent — dropping
2375
- them quietly would defeat the point of pinning.
2376
-
2377
- #### 2. Observed habits stayed as words — `/model 카드`
2378
-
2379
- deel already watched what the model did. But watching was all it did — it **wrote advice
2380
- into the prompt**: "you keep truncating arguments, use Append." Small models don't follow
2381
- that advice. That is what makes them small models.
2382
-
2383
- Now what it observes becomes **harness settings**. Instead of asking the model, deel changes
2384
- its own behaviour.
2385
-
2386
- | Observed | What changes |
2387
- |---|---|
2388
- | Arguments truncate often (over 15%) | The cap is raised up front — no wasted first call |
2389
- | It repeats itself | Three identical calls tolerated becomes two |
2390
- | Edits miss often | More surrounding lines are shown on a near-miss |
2391
-
2392
- **Nothing changes before 12 steps.** Pinning down a healthy model because of one unlucky
2393
- truncation is worse than not learning at all.
2394
-
2395
- #### 3. Evidence instead of "all done" — `/evidence`
2396
-
2397
- A 2026 survey found **96% of developers don't fully trust AI-written code, while 48% verify
2398
- it every time.** 38% said it is harder to review than human code.
2399
-
2400
- Why harder? Ask a person why they wrote it that way and you get an answer. Agent-written
2401
- code arrives with **one line: "done."** That line cannot be reviewed.
2402
-
2403
- ```
2404
- /evidence
2405
-
2406
- Changed 3 files · +142 −38
2407
- Ran 5 commands (1 failed)
2408
- Unproven 1
2409
-
2410
- ✗ src/worker.js — the last `npm test` failed — an earlier pass
2411
- does not prove the current state.
2412
- ```
2413
-
2414
- Listing what changed is something `/diff` already does. What only this does is **say that
2415
- the unproven is unproven.** Three things get caught —
2416
-
2417
- - Changed something and ran nothing? Nothing was proven
2418
- - Counting a red test as green means **offering a failing test as evidence**
2419
- - A check run *before* the edit proves nothing about it — "I ran it earlier" is the most
2420
- common form of self-deception
2421
-
2422
- If the build passed and the tests broke *after* it, the earlier green is not evidence. The
2423
- last thing you ran is red; it cannot have been verified.
2424
-
2425
- `/evidence filename` writes it as markdown under `.deel/증거/`. The screen scrolls away, and
2426
- the review happens later, by someone else.
2427
-
2428
- #### 4. It made you open one more terminal — `deel acp`
2429
-
2430
- Developers live inside the IDE. A tool that makes you switch windows stops being used after
2431
- about two weeks. A build that cleared corporate review and then nobody uses is the saddest
2432
- possible outcome.
2433
-
2434
- deel now speaks **ACP** (Agent Client Protocol). One line — `deel acp` — in your editor's
2435
- settings and Zed, JetBrains, Neovim and Emacs attach **without changing a line on their side**.
2436
-
2437
- The work isn't connecting the pipe; it is **making the editor able to show something**.
2438
- Kind, location and status are all optional in the spec, so a quick implementation omits all
2439
- three — and then every tool is the same grey dot and no changed file is clickable.
2440
-
2441
- Approval flows through too. deel's safety rails render as the editor's own dialog, and
2442
- "always allow" is remembered for that session. Against a client that cannot ask, it
2443
- **does not run** — "if I can't ask, I do as I please" is not an option.
2444
-
2445
- **Still zero dependencies.** Newline-delimited JSON-RPC 2.0 is the whole transport, so no SDK.
2446
-
2447
- #### 5. Review paperwork only a human could read — `deel sbom`
2448
-
2449
- A Korean financial-sector rule change on 2026-04-20 opened an exemption to the network-
2450
- separation mandate. The paperwork demanded at that door is not prose — security feeds an
2451
- **SBOM to a scanner** for a vulnerability list, and operations reads the audit-log spec to
2452
- write SIEM ingestion rules.
2453
-
2454
- `deel pack` now emits three documents.
2455
-
2456
- | | |
894
+ | Version | What changed |
2457
895
  |---|---|
2458
- | `반입심사서.txt` | The human-readable sheet, as before |
2459
- | `sbom.cdx.json` | CycloneDX 1.5. One component per file with SHA-256. Dependencies as an **explicit empty array** "not declared" and "none" are different claims |
2460
- | `심사명세.json` | Egress list (per lane: when, where, what, how it's stopped, source location) · audit-log spec · file hashes |
2461
-
2462
- All three are generated by scanning the source. The audit-log spec is the one hand-written
2463
- part, so **a test compares it against real log records on every run** — a review document
2464
- that drifts is worse than none, and one wrong line costs you the reviewer's trust in the rest.
2465
-
2466
- #### 6. One `env` put your keys in the conversation and on disk
2467
-
2468
- People rarely paste a key. The leak is almost always command output — `env`, `git remote -v`,
2469
- `curl -v`, a failing test log. That text goes to the model **and** is written to
2470
- `.deel/sessions/*.jsonl`. Leak once, and you have several copies.
2471
-
2472
- Masking now happens at the single point where tool output enters the conversation:
2473
- private-key blocks, OpenAI, Anthropic, GitHub, Slack, AWS, Google, JWTs, credentials in URLs,
2474
- `Authorization`-family headers, and env vars named `…KEY` / `…TOKEN` / `…SECRET` /
2475
- `…PASSWORD`. The configured gateway key is removed regardless of shape — that one is a known
2476
- value, not a guess.
2477
-
2478
- **File contents are deliberately not masked.** Mask `.env` and the model edits the masked text
2479
- and writes it back, landing a placeholder where the real key was — protecting the secret would
2480
- destroy it. So on the file side it reports instead of rewriting.
2481
-
2482
- <br>
2483
-
2484
- Tests 2,578 → **2,860** · 54/54 files. Earlier releases are on the [tags](https://github.com/jysvai/deel-local-cli/tags) page.
2485
-
2486
- </details>
2487
-
2488
- <details>
2489
- <summary><b>▸ 1.2.0 — so the conversation doesn't break</b> · what changed in six places</summary>
2490
-
2491
- <br>
2492
-
2493
- | | Before | After |
2494
- |---|---|---|
2495
- | Resuming | a conversation cut mid-tool-call **would not reopen** | unmatched calls are cleared, then it opens |
2496
- | Counting tokens | it guessed, and stayed wrong | it corrects itself against the server |
2497
- | Making room | summarising arrived at turn 49 | it holds out to turn **102** |
2498
- | Side questions | piled up in the main context | live in their own thread |
2499
- | Yesterday's lesson | vanished when you quit | carries over to the next session |
2500
- | The answer on screen | `**bold**` showed up as characters | it is drawn |
2501
-
2502
- <br>
2503
-
2504
- #### 1. A conversation cut mid-tool-call would not reopen
2505
-
2506
- Close the window or hit <kbd>Ctrl</kbd>+<kbd>C</kbd> while a tool is running and the saved conversation keeps **a call with no result under it**. Reopen it with `--resume` and the server rejects the mismatch with a 400 — the conversation was written down perfectly well, and you still could not carry on.
2507
-
2508
- Now the pairs are checked before it opens. Calls with no result go, results with no parent go, and **whatever the model said stays.**
2509
-
2510
- ```
2511
- $ deel --resume 20260826-140217
2512
-
2513
- ✓ 20260826-140217 — 메시지 48개를 이어 받았습니다.
2514
- 중단된 도구 호출 2개를 걷어냈습니다 — 그때 하던 일은 다시 시켜 주세요.
2515
- ```
2516
-
2517
- #### 2. It misjudged the room left
2518
-
2519
- Token counts are estimated from character counts. Mix Korean, code and JSON and that estimate drifts from the truth — so deel folded early with room to spare, or did not fold when there was none and the server refused.
2520
-
2521
- It now **corrects the multiplier against what the server actually reports** with every answer. One line at the foot of `/context`:
2522
-
2523
- ```
2524
- 서버가 알려 준 실제값에 맞춰 +12% 보정했습니다 (7번 재봄).
2525
- ```
2526
-
2527
- The multiplier is kept per model, so **the next session starts from it** instead of measuring again from scratch.
2528
-
2529
- #### 3. Summarising arrived too early
2530
-
2531
- At 80% of the window, earlier turns get folded into a summary. That **cannot be undone**, and the file contents the reasoning rested on are gone with it.
2532
-
2533
- There is now a step before it. At 55%, **only older tool results** are folded — the four most recent are left alone, and nothing the model or you said is touched at all.
2534
-
2535
- ```
2536
- ◲ 오래된 도구 결과 6개를 접었습니다 (2,148 토큰을 비움)
2537
- ```
2538
-
2539
- What was there is left in its place:
2540
-
2541
- ```
2542
- (접힘) Read(src/runner.js) — 61줄. 자리를 비우려고 내용을 접었습니다. 필요하면 다시 읽으세요.
2543
- ```
2544
-
2545
- Measured by streaming the same conversation through: summarising is pushed from **turn 49 to turn 102 — 2.1×**.
2546
-
2547
- #### 4. Side questions polluted the main line
2548
-
2549
- "Just check this one thing" piles into the main context and stays there long after the checking is done.
2550
-
2551
- ```
2552
- /thread new 로그확인
2553
- ⑂ 로그확인 갈래로 왔습니다. 빈 대화입니다
2554
- 본줄기로 돌아가려면 /thread 1
2555
- ```
2556
-
2557
- `fork` carries the conversation so far with you. What threads **keep apart is the messages, the token count and the checklist**; what they **share is the connection, undo and the audit log** — a file changed inside a thread still comes back with `/undo`. The `⑂` marker appears in the status line only once there is more than one thread.
2558
-
2559
- #### 5. Yesterday's lesson vanished when you quit
2560
-
2561
- deel could work out that `pnpm` is not on this machine, and lose it the moment you quit. Tomorrow it calls it again, fails again, works around it again.
2562
-
2563
- ```
2564
- /learned
2565
- ── 겪어 본 것 ──────────────────────────────────────
2566
-
2567
- 이 폴더에서 돌려 본 명령
2568
- ✓ npm test 됨 12 · 안 됨 0
2569
- ✗ pnpm 됨 0 · 안 됨 3
2570
-
2571
- 이 모델에 대해 qwen2.5-coder:7b
2572
- 같이 걸어 본 걸음 86
2573
- 인자가 잘림 14 (16%)
2574
- 토큰 추정 보정 ×1.12
2575
-
2576
- 이 중 프롬프트에 실리는 것
2577
- - 여기서 되는 명령: `npm test`
2578
- - 이 PC 에서 안 되는 명령(다시 부르지 마라): `pnpm`
2579
- ```
2580
-
2581
- **This is not training.** It does not touch the model and it does not hoard conversations — that would only eat context. It counts, and it carries over **only what it has seen twice**, all of it **within 220 tokens**. Something seen once may be a coincidence, and writing a coincidence down as fact sends the model around a road that actually works.
2582
-
2583
- Commands that work live with the folder (`.deel/배운것.json`); the model's habits live in the config folder. So **move the folder and what it learned about the model comes along.** `/learned 지우기` empties it whenever you want.
2584
-
2585
- #### 6. The answer showed up as raw characters
2586
-
2587
- The model speaks Markdown and the screen did not know it, so asterisks, backticks and hashes came through mixed into the prose. You had to re-read it in your head to see what was a heading and what was code.
2588
-
2589
- ```
2590
- before after
2591
- ▌ ## 고친 것 ▌ ▍ 고친 것
2592
- ▌ **src/runner.js** 의 `console.log` ▌ src/runner.js 의 console.log
2593
- ▌ - [ ] 남은 것: `src/worker.js` ▌ ☐ 남은 것: src/worker.js
2594
- ▌ ```js ▌ ┌──────────────── js
2595
- ▌ log.info('시작', { id }) ▌ │ log.info('시작', { id })
2596
- ▌ |---|---| ▌ ┼────────┼─────────┼
2597
- ```
2598
-
2599
- Answers arrive in fragments, so a line can only be drawn **once it ends**. But a whole paragraph on one line would leave the screen still for seconds — so once a line grows past the screen width, everything up to there is **streamed raw**. Looking alive comes before looking neat.
2600
-
2601
- <br>
2602
-
2603
- Tests 2,532 → **2,578** · 48/48 files. Earlier releases are in the [tags](https://github.com/jysvai/deel-local-cli/tags).
896
+ | **[1.4.2](docs/en/releases.md#142)** | 1.4.1 shipped before its own security fixes — this corrects that |
897
+ | [1.4.1](docs/en/releases.md#141) | No new features, only what was actually found and fixedWindows abort, ReDoS, XSS |
898
+ | [1.4.0](docs/en/releases.md#140) | deel gets a face, speaks English, and sees meaning eleven places |
899
+ | [1.3.0](docs/en/releases.md#130) | Evidence instead of claims, the editor instead of a terminal — six places |
900
+ | [1.2.0](docs/en/releases.md#120) | So the conversation doesn't break six places |
2604
901
 
2605
- </details>
902
+ What changed and why is in the **[release notes](docs/en/releases.md)**.
2606
903
 
2607
904
  ---
2608
905