deel-local-cli 1.2.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.en.md +301 -1537
  2. package/README.md +272 -1582
  3. package/bin/deel.js +73 -4
  4. package/package.json +8 -3
  5. package/src/acp/jsonrpc.js +230 -0
  6. package/src/acp/map.js +219 -0
  7. package/src/acp/serve.js +556 -0
  8. package/src/agent/card.js +129 -0
  9. package/src/agent/compact.js +10 -2
  10. package/src/agent/effort.js +5 -0
  11. package/src/agent/evidence.js +186 -0
  12. package/src/agent/grade.js +20 -0
  13. package/src/agent/loop.js +134 -13
  14. package/src/agent/models.js +169 -0
  15. package/src/agent/modes.js +47 -1
  16. package/src/agent/pins.js +140 -0
  17. package/src/agent/preset.js +113 -0
  18. package/src/agent/project.js +10 -4
  19. package/src/agent/session.js +233 -13
  20. package/src/agent/store.js +31 -0
  21. package/src/backend/adapter.js +14 -0
  22. package/src/commands.js +616 -52
  23. package/src/i18n/en.js +265 -0
  24. package/src/i18n/index.js +126 -0
  25. package/src/i18n/ko.js +252 -0
  26. package/src/lsp/client.js +459 -0
  27. package/src/lsp/diag.js +112 -0
  28. package/src/lsp/rpc.js +84 -0
  29. package/src/lsp/servers.js +218 -0
  30. package/src/oneshot.js +19 -0
  31. package/src/pack/sbom.js +218 -0
  32. package/src/pack/selfpack.js +19 -3
  33. package/src/preview/serve.js +19 -2
  34. package/src/repl.js +193 -8
  35. package/src/safety/secrets.js +205 -0
  36. package/src/safety/undo.js +10 -3
  37. package/src/tools/desc.en.js +221 -0
  38. package/src/tools/docs.js +252 -0
  39. package/src/tools/index.js +190 -6
  40. package/src/tools/lsp.js +327 -0
  41. package/src/tools/task.js +30 -2
  42. package/src/ui/ansi.js +45 -0
  43. package/src/ui/approve.js +25 -21
  44. package/src/ui/banner.js +245 -0
  45. package/src/ui/export.js +217 -0
  46. package/src/ui/inputbox.js +37 -7
  47. package/src/ui/intro.js +206 -0
  48. package/src/ui/level.js +11 -5
  49. package/src/ui/notify.js +101 -0
  50. package/src/ui/status.js +181 -35
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)
@@ -59,6 +78,8 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
59
78
  - [Auto-compaction](#auto-compaction)
60
79
  - [Resuming a conversation](#resuming-a-conversation)
61
80
  - [Attaching tools from outside (MCP)](#attaching-tools-from-outside-mcp)
81
+ - [Inside your editor (ACP)](#inside-your-editor-acp)
82
+ - [Keeping secrets out of the conversation](#keeping-secrets-out-of-the-conversation)
62
83
  - [Safety](#safety)
63
84
  - [Corporate review package](#corporate-review-package)
64
85
  - [Configuration](#configuration)
@@ -66,7 +87,19 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
66
87
  - [Development](#development)
67
88
  - [Release notes](#release-notes)
68
89
 
69
- 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.0](docs/en/releases.md#140) · [1.3.0](docs/en/releases.md#130) · [1.2.0](docs/en/releases.md#120) |
70
103
 
71
104
  ---
72
105
 
@@ -97,6 +130,29 @@ deel audit # full review sheet
97
130
 
98
131
  ## Quick start
99
132
 
133
+ ### The screen speaks English too
134
+
135
+ deel is written in Korean — the code, the function names, the comments. That part stays.
136
+ What you see on screen does not have to.
137
+
138
+ ```bash
139
+ DEEL_LANG=en deel # this run only
140
+ /lang en # and remember it
141
+ /lang # how much is translated so far
142
+ ```
143
+
144
+ Untranslated lines come through in Korean rather than as blanks, and `/lang` tells you exactly
145
+ how many are left.
146
+
147
+ What the model reads follows the same switch. Set it to English and the rules, the mode
148
+ instructions, and the tool descriptions all go out in English — so the model answers you in
149
+ English instead of Korean. That side is cheaper, too: the part of the window that ships on every
150
+ single request drops from about 4,900 tokens to about 3,450 — on a 32k model, from 15% of the
151
+ window to 10.5%.
152
+
153
+ Tool names and argument names stay Korean (`목적`, `할일`, `번호`). Those are identifiers, not
154
+ prose — rename them and the tool stops being called at all.
155
+
100
156
  ### Install
101
157
 
102
158
  ```bash
@@ -207,140 +263,9 @@ $ deel scan
207
263
 
208
264
  Switch with `/model` mid-conversation — **the conversation carries over.**
209
265
 
210
- <details>
211
- <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>
212
-
213
- ### It adapts to whatever model is attached
214
-
215
- If you move between models, any number tuned for one of them is wrong for all the others.
216
- So **no number is hardcoded.**
217
-
218
- There are two axes. They are easy to confuse, so they have separate commands.
219
-
220
- | | What it measures | Command |
221
- |---|---|---|
222
- | **Window size** | how much it can hold | `/ctx` |
223
- | **Model grade** | how much it can do on its own | `/grade` |
224
-
225
- They do not move together. A 3B model with a 128k window exists; so does a very good model
226
- with 32k. Treat them as one axis and you hold one back while overrunning the other.
227
-
228
- **Derived from window size** (`src/agent/budget.js`):
229
-
230
- | | 8k | 32k | 131k | 655k |
231
- |---|---|---|---|---|
232
- | Steps per turn (code) | 16 | 48 | 192 | 200 |
233
- | `Read` lines | 200 | 384 | 1,536 | 4,000 |
234
- | `Glob` results | 50 | 192 | 768 | 1,000 |
235
- | `Outline` lines | 120 | 480 | 1,920 | 2,500 |
236
- | `WebFetch` chars | 4,000 | 12,800 | 51,200 | 120,000 |
237
- | Subtask summary | 400 | 1,600 | 4,000 | 4,000 |
238
-
239
- **Derived from model grade** (`src/agent/grade.js`):
240
-
241
- | | small | medium | large |
242
- |---|---|---|---|
243
- | Files per `Write` | 3 | 6 | 12 |
244
- | Split-writing threshold | 200 lines | 400 lines | 800 lines |
245
- | Spell out the procedure | yes | yes | **no — give the goal** |
246
- | Require verification | yes | yes | **yes** (grade-independent) |
247
-
248
- The grade is decided like this:
249
-
250
- 1. **First guess from the name.** `qwen2.5-coder-7b` -> small, `llama-3.3-70b` -> large.
251
- Version numbers (`2.5`) and quantisation tags (`q4_k_m`) are not read as sizes.
252
- A name that says nothing means **medium**, not small — a corporate gateway is exactly
253
- that case, and the models behind one are usually big. Guessing small holds them back.
254
- 2. **Corrected by what actually happened.** Truncated tool arguments, empty answers,
255
- failed edits and repeats are counted. A model labelled 70B that truncates every step is
256
- dropped to **small**; a 7B that runs ten clean steps is raised one level. The name is a
257
- guess; what happened is a fact.
258
- 3. **You win if you say so.** `/grade large`, and `/grade auto` hands it back.
259
-
260
- The status line shows `◈ small?`. The question mark means **still a guess** — a guess is
261
- not presented with the same face as something measured.
262
-
263
- The grade only changes **how much hand-holding you get**. Working scope, approval mode,
264
- undo and the audit log are identical at every grade. There is no "it is a good model, so
265
- skip verification" — that is exactly how a good model's mistake goes unnoticed.
266
-
267
- ### Small windows get a smaller fixed share
268
-
269
- The system prompt and the tool definitions go out **in full on every request**. Compaction
270
- (`/compact`) cannot shrink them. Once that share passes half the window there is no room
271
- left however well you fold, and it looks like "the model suddenly got stupid".
272
-
273
- Adding three tools (`Outline`, `Verify`, `Task`) pushed it to **49%** at 8k. Dropping a
274
- tool would have fixed it — and would have made small models capable of different things,
275
- which is the thing to avoid. The descriptions were trimmed to the window instead.
276
-
277
- | | 8k | 16k | 32k+ |
278
- |---|---|---|---|
279
- | Base rules | short form | short form | full |
280
- | Mode description | short form | short form | full |
281
- | Tool descriptions | 90 chars | 140 chars | 220 / full |
282
- | Obvious param descriptions | dropped | kept | kept |
283
- | Descriptions inside array items | dropped | dropped | kept |
284
- | **Fixed share** | **2,712 tokens (33%)** | 3,290 (21%) | 4,745 (4% at 131k) |
285
-
286
- The **folder brief** adds roughly 80 more tokens at 8k (below). That value varies by
287
- folder, so it is not in the table.
288
-
289
- **Tool names and arguments are untouched.** What is possible is identical in every window;
290
- what disappears is only the argument for *why* to use a tool. Large windows keep it,
291
- because that argument earns its keep — those two sentences are what make a model call
292
- `Outline` before `Read`.
293
-
294
- A test pins these numbers (`test/compact.test.js`).
295
-
296
- ### On startup it reads what kind of project this folder is
297
-
298
- Started in a folder of someone else's code, the model began knowing nothing. So it
299
- retraced the same three steps every time — scan the top level, read `package.json`, find
300
- out how tests are run. **On a local model each step is 20-40 seconds, so two minutes go
301
- by before the work even starts.**
302
-
303
- The worse case is the model *skipping* those three steps. Then it creates files by its
304
- own conventions without knowing what the project already uses — a `requirements.txt`
305
- dropped into an npm project.
306
-
307
- The answers are all knowable at startup, so they are read once and put in the prompt.
308
-
309
- The brief itself is written in Korean, like everything else deel puts on screen — this is
310
- deel's own folder, wrapped here for width:
311
-
312
- ```
313
- --- 이 폴더 --- (this folder)
314
- node 프로젝트 (deel-local-cli) · git main
315
- 돌릴 수 있는 것: npm start · npm test · npm run bench · npm run chat · npm run check ·
316
- npm run coverage · npm run demo · npm run diagnose (runnable)
317
- 위쪽: bin/ src/ test/ LICENSE README.en.md README.md package.json report.txt (top level)
318
- 위쪽 한 겹만 본 것이다. 안을 알아야 하면 Outline 을 불러라.
319
- (top level only — call Outline to see inside)
320
- ```
321
-
322
- **Nothing is invented.** Runnable commands are copied verbatim from `scripts` in
323
- `package.json`. Advertising a command that does not exist means the model calls it,
324
- fails, and spends the steps you just saved looking for the real one.
325
-
326
- **The last line matters.** What is listed is the top level and `package.json`, nothing
327
- inside subfolders. Without saying so, the model treats this as a map of the whole project
328
- and stops calling `Outline` — at which point the brief costs more than it saves.
329
-
330
- | Not done | Why |
331
- |---|---|
332
- | No `git` subprocess | `.git/HEAD` is read directly. Spawning one freezes startup for seconds on a large repo |
333
- | No directory walk | Top level **only**. Descending is slow on large repos, and only one line goes in the prompt anyway |
334
- | Not re-read each turn | Once, at startup. A prompt that changes mid-conversation makes it impossible to tell why an answer changed |
335
-
336
- It scales with the window — 10 entries and 4 commands at 8k, 24 and 8 on a large one.
337
- If only one thing survives a narrow window it is the **commands**. The top-level listing
338
- can be recovered with one `Glob`; "tests run with `npm test`" requires opening
339
- `package.json`.
340
-
341
- 7ms on a folder with 600 files (`test/project.test.js`).
342
-
343
- </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)**
344
269
 
345
270
  ---
346
271
 
@@ -351,6 +276,11 @@ Names follow Claude Code / Codex conventions.
351
276
  | Command | What it does |
352
277
  |---|---|
353
278
  | `/help` | Command list |
279
+ | `/lang [ko\|en]` | Screen language. Falls back to Korean for anything not translated yet |
280
+ | `/bell [on\|off]` | Ring and set the window title when a turn ends, or when deel needs an answer |
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 |
283
+ | `/lsp [on\|off]` | Language servers — what is installed, and whether `Def`/`Refs` are available. `off` turns post-edit diagnostics off only |
354
284
  | `/context` | What is consuming the context window |
355
285
  | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
356
286
  | `/grade [small\|medium\|large\|auto]` | Model **grade** — how much it does on its own. A different axis from `/ctx` |
@@ -359,7 +289,10 @@ Names follow Claude Code / Codex conventions.
359
289
  | `/clear` | Clear the conversation (keeps link and rules) |
360
290
  | `/thread [new\|fork\|close\|n]` | Conversation threads — side work in its own context. Link and undo stay shared |
361
291
  | `/learned [clear]` | What deel has picked up on its own — commands that work here, this model's habits |
292
+ | `/pin <text>` | Pin a line — folding and compaction **cannot reach it** |
293
+ | `/evidence [file]` | Evidence — what changed, and what proves it. **What is unproven is listed too** |
362
294
  | `/model` | Switch connection / model |
295
+ | `/model 카드` | Model card — what this model has actually done here, and what deel changed because of it |
363
296
  | `/think <level>` | Reasoning level (`off·low·medium·high·max`) |
364
297
  | `/think 배분 <profile>` | Per-stage profile (`even·save·deep`) |
365
298
  | `/think 자세히` | Stage table — which stage runs at which level and cap |
@@ -402,68 +335,9 @@ the conversation.
402
335
 
403
336
  Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
404
337
 
405
- <details>
406
- <summary><b>More</b> — Attaching a file with @ · Interrupting</summary>
407
-
408
- ### Attaching a file with `@`
409
-
410
- Write `@` followed by a path and that file is sent along with your message.
411
-
412
- ```
413
- ❯ @src/a.js why is this slow?
414
- ◧ attached src/a.js
415
- ```
416
-
417
- That removes **one round-trip** — the one where the model has to call `Read` itself. Local
418
- models are weak at tool calling and that round-trip often misfires: it calls the wrong path,
419
- or skips the call and invents the contents. You already know which file it is; there is no
420
- reason to make the model go looking.
421
-
422
- The hard part is not attaching. It is **not mistaking something else for a file.**
423
- Things starting with `@` are everywhere.
424
-
425
- | What you type | What happens |
426
- |---|---|
427
- | `@src/a.js` | Attached — only when the path actually exists |
428
- | `hong@example.com` | Not a mention at all. A letter before the `@` means it is an address |
429
- | `@media` · `@dataclass` · `@scope/pkg` | Left alone — no such path exists |
430
- | `@src/` (a directory) | A listing of what is inside is attached |
431
- | `@"draft report.txt"` | Quote names containing spaces |
432
- | Outside the working scope | Refused, and the refusal is printed |
433
- | A CP949 corporate document | Decoded by content and attached correctly |
434
-
435
- There is one rule: **attach only when the path really exists.** Otherwise the text is left
436
- exactly as typed, silently — it probably wasn't a mention.
437
-
438
- Attachments are capped at **25% of the context length** (20,000 tokens at most). Anything larger is attached from the
439
- top only, and a truncated file is **not marked as already read.** Marking it read would let the
440
- model edit a part it never saw. Only a fully attached file lets it skip `Read`.
441
-
442
- Every attachment is announced on screen. Text the user did not type is now in the conversation;
443
- not showing it would also leave them wondering where the context went.
444
-
445
- ### Interrupting
446
-
447
- Press **Ctrl+C** to stop the model mid-answer when it is heading the wrong way.
448
-
449
- ```
450
- ❯ rewrite the whole test suite
451
- ◧ Read test/smoke.js
452
- ◧ Read test/loop.test.js
453
- ^C
454
- ⚠ Stopped (after step 2)
455
-
456
- ❯ ▊
457
- ```
458
-
459
- The conversation stays valid. If the model had announced tool calls, each unanswered one is
460
- filled with a `stopped by user` result so the call/result pairing holds — a conversation with
461
- broken pairing is rejected with HTTP 400 on the next request, which would waste the whole
462
- session. Tools already running finish; **tools not yet started never run.**
463
-
464
- Pressing Ctrl+C again on an empty line quits.
465
-
466
- </details>
338
+ > **More** — Attaching a file with @ · Interrupting
339
+ >
340
+ > **[The screen read →](docs/en/interface.md#slash-commands)**
467
341
 
468
342
  ---
469
343
 
@@ -493,52 +367,9 @@ Don't confuse this with `/mode`. They are separate axes:
493
367
  If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
494
368
  overrides something a person chose.
495
369
 
496
- <details>
497
- <summary><b>More</b> — Switching by itself (Auto mode)</summary>
498
-
499
- ### Switching by itself (Auto mode)
500
-
501
- You start in **Auto**. Nothing has been decided about what kind of work is coming.
502
- Every turn, deel reads what you typed, picks the mode that fits, and works in it.
503
-
504
- ```
505
- ❯ why won't the login go through?
506
-
507
- ◉ Debug because your message contained "why won't", "won't"
508
- Not what you wanted? Type /code to pin a mode yourself.
509
- ```
510
-
511
- Switching brings **the whole mode** with it — its working protocol, its tool set, its
512
- reasoning settings. It isn't a label saying "debug mode": the model is actually walked
513
- through symptom → reproduce → hypothesis → evidence, and in Plan mode `Write` and `Edit`
514
- are not handed over at all.
515
-
516
- | When you say | It goes to |
517
- |---|---|
518
- | why won't · error · fails · crashes · what's causing | ◉ Debug |
519
- | plan · roadmap · what order · let's map it out first | ☰ Plan |
520
- | design · architecture · how should this be structured · how to split | ◈ Architect |
521
- | what is · explain · how does it work · difference between | ◇ Ask |
522
- | all of · everything · one by one · to the end · unify | ❋ Orchestrator |
523
- | fix · add · implement · rename · delete | ◆ Code |
524
-
525
- **When it's close, it doesn't switch.** "ok", "go on", "that thing from earlier" leave you
526
- in Auto. So does a near-tie between first and second place — a wrong switch into a read-only
527
- mode leaves you blocked without knowing *why*. Read-only modes (Plan, Architect, Ask)
528
- therefore carry a higher bar: "explain this and fix it" routes to Code, not Ask.
529
-
530
- A switch lasts **one turn only.** The next message is judged fresh.
531
- A `~` in the status line means it switched by itself; no `~` means you chose it.
532
-
533
- ```
534
- ◎ Auto ← waiting
535
- ~◉ Debug ← this turn only, chosen for you
536
- ◉ Debug ← you typed /debug. It stays.
537
- ```
538
-
539
- Choosing a mode yourself **pins** it. `/auto` (or `/work auto`) hands the wheel back.
540
-
541
- </details>
370
+ > **More** — Switching by itself (Auto mode)
371
+ >
372
+ > **[The screen read →](docs/en/interface.md#work-modes)**
542
373
 
543
374
  ---
544
375
 
@@ -561,202 +392,9 @@ Two things matter here:
561
392
  - **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
562
393
  blocking are identical. A beginner needs the undo more, not less.
563
394
 
564
- <details>
565
- <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>
566
-
567
- ### The input box
568
-
569
- Launched in a terminal, **the conversation scrolls normally and an input box is pinned at
570
- the bottom.** Only the box is erased and redrawn — nothing above it is touched.
571
-
572
- ```
573
- ❊ Grep(console.log)
574
- └ 3 files · 11 hits
575
- ◈ Edit(src/runner.js)
576
- └ 1 spot +3-1
577
- - 12 console.log('시작', 이름)
578
- + 12 logger.info({ 단계: '시작', 이름 })
579
-
580
- ▌ Unified log calls to the logger format. One change in runner.js.
581
-
582
- ── 4.2s · 3 tools · ↑3,900 ↓180
583
-
584
- ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱ 22% ▏ ◎ 종합 · ◇ medium · ⏵⏵ 자동
585
- ╭─────────────────────────────────────────────────────────────────────────────╮
586
- │ ❯ also shrink the aggregate helpers │
587
- ╰─────────────────────────────────────────────────────────────────────────────╯
588
- ```
589
-
590
- Terminal scrollback, selection and `Ctrl+F` all keep working, because the conversation is
591
- never trapped inside a pane of ours. Long input grows the box to as many lines as it needs.
592
-
593
- **It switches itself off where it would do harm.** Piped or redirected output, `CI` set,
594
- `TERM=dumb`, or a window under 40 columns: no box, no asking. `deel … | tee log.txt` must
595
- not become a pile of escape codes. Passing `--tui` does not override a pipe, and `--no-tui`
596
- turns it off at any time.
597
-
598
- Line editing stays entirely with Node's readline — Korean IME composition, paste, history,
599
- Ctrl+A/E, backspace. We only *draw* the string readline is holding. Hand-rolling a line
600
- editor is how you break IME input first.
601
-
602
- > **A path taken and abandoned** — the first version borrowed the whole terminal (an
603
- > alternate screen, like vim) and split it into conversation, changed-files and todo panes.
604
- > It looked the part, and **every slash command went dead.** Six modules including
605
- > `commands.js` write straight to the terminal rather than through the screen object, and a
606
- > full repaint erased their output the instant it appeared. Not "the command didn't run" —
607
- > "you can't see that it ran", which is worse. Fixing it would mean threading every one of
608
- > those six through the screen object, plus every one added later, with a silent return of
609
- > the same symptom if one is missed. So the design went the other way: let the conversation
610
- > flow, manage only the box. ([`test/box.test.js`](test/box.test.js) spawns a child that
611
- > pretends to be a terminal, so this one cannot ship again.)
612
-
613
- ---
614
-
615
- ### You don't have to type the whole command
616
-
617
- There are over thirty commands. The only person who has them memorised is the one who
618
- wrote them, and even he gets as far as `/mem…` and stops to wonder whether it was `memory`
619
- or `memo`. If the only recourse at that point is typing `/help` and scanning thirty lines,
620
- that isn't a command, it's a quiz.
621
-
622
- So they show up **while you type.**
623
-
624
- ```
625
- ╭─────────────────────────────────────────────────────────────────────────────╮
626
- │ ❯ /mo │
627
- ╰─────────────────────────────────────────────────────────────────────────────╯
628
- › /model [이름|list|models] 연결·모델 바꾸기 (이름 일부 · list · models)
629
- /mode <모드> 승인 정책 — 얼마나 물어보나
630
- /memory [지우기 <번호>|…] 대화가 끝나도 남는 기억 — 보기·지우기
631
- ```
632
-
633
- `Tab` fills it in: all the way if only one matches, otherwise **only as far as they all
634
- agree** (`/mo` + Tab → `/mode`). It doesn't pick one for you, because deleting the wrong
635
- guess costs more than typing the rest. Commands that take an argument get a trailing space
636
- so you can keep going.
637
-
638
- Prefix matches come first and **substring matches follow** — typos cluster in the first
639
- character, so `/emo` still surfaces `/memory`. The moment you type a space (`/mode auto`)
640
- the list folds away: the command is already decided.
641
-
642
- The list sits **below** the box. Putting it inside would push what you are typing upward,
643
- so you could no longer see it.
644
-
645
- > No up/down selection. That would have to take over **input history** (up arrow), which
646
- > is used far more often. What is needed here isn't picking, it's recognising — and once
647
- > you recognise it, one `Tab` is enough.
648
- >
649
- > Commands hidden at the `쉬움` level still surface when you type their prefix. Hidden is
650
- > not the same as unavailable — someone who knows `/recall` should not be told it doesn't
651
- > exist because of a display setting.
652
-
653
- ---
654
-
655
- ### The box stays while it works
656
-
657
- Local models are slow. A single step can take tens of seconds, and if the bottom of the
658
- screen goes blank for that long, **people assume it hung and hit Ctrl+C** — losing work that
659
- was nearly done. So the border stays and only the contents change.
660
-
661
- ```
662
- ◧ Read(집계.py)
663
- └ 6 lines
664
-
665
- ▏myproject · qwen2.5-coder:7b ▏ ▰▱▱▱▱▱▱▱▱▱ 2% ▏ ◎ 종합 · ◇ medium · ⏵⏵ 자동 ▏ ↑3.8k ↓180
666
- ╭─────────────────────────────────────────────────────────────────────────────╮
667
- │ ⠹ 파일 들여다보는 중… 12초 · 생각 1,240자 · Ctrl+C 중단 │
668
- ╰─────────────────────────────────────────────────────────────────────────────╯
669
- ```
670
-
671
- The phrase tracks **what is actually happening**. This is not decoration: a message that
672
- cycles at random stops being read after the second time, and from then on it is worth no
673
- more than a blank screen. One turn reads like this:
674
-
675
- ```
676
- 머리 굴리는 중 → 파일 들여다보는 중 → 코드 짜는 중 → 답 쓰는 중
677
- (turning it over) (looking at files) (writing code) (writing the answer)
678
- ```
679
-
680
- The phrases are Korean, because the interface is. Here is what each set means:
681
-
682
- | Activity | On screen | Roughly |
683
- |---|---|---|
684
- | Thinking | 머리 굴리는 중 · 어떻게 할지 궁리하는 중 · 수 읽는 중 · 따져 보는 중 | turning it over · working out how · reading ahead · weighing it up |
685
- | `Read` `Grep` `Glob` | 파일 들여다보는 중 · 코드 훑는 중 · 어디 있나 뒤지는 중 · 단서 찾는 중 | looking at files · skimming code · hunting for where it is · looking for a clue |
686
- | `Write` `Edit` `Append` | 코드 짜는 중 · 고쳐 넣는 중 · 손보는 중 · 한 줄씩 옮기는 중 | writing code · patching it in · touching it up · moving it a line at a time |
687
- | `Bash` | 명령 돌리는 중 · 터미널 두드리는 중 · 결과 기다리는 중 | running a command · at the terminal · waiting on output |
688
- | `WebFetch` | 문서 찾아보는 중 · 읽어 오는 중 | looking up docs · fetching |
689
- | Answering | 답 쓰는 중 · 정리해서 말하는 중 | writing the answer · putting it together |
690
- | **Past 45 seconds** | 아직 하는 중 · 조금만 더 · 생각보다 오래 걸리는 중 | still going · nearly there · taking longer than expected |
691
-
692
- Within a category the phrase advances every 4 seconds — text frozen for 30 seconds reads as
693
- hung too. On the right: **elapsed time**, and while the model is reasoning, **how many
694
- characters of thinking have arrived**. One number that genuinely increases is what turns
695
- "still alive" from a claim into a fact.
696
-
697
- ### The picture on the left moves too
698
-
699
- What spins next to the phrase is not a spinner — it is **a small drawing of the work being
700
- done right now**.
701
-
702
- | Doing | One cycle | The picture |
703
- |---|---|---|
704
- | Thinking | `⠀⠶⠀` `⠰⣿⠆` `⢾⣿⡷` `⠰⣿⠆` | swells and shrinks |
705
- | Reading | `⠉⠉⠉` `⠒⠒⠒` `⠤⠤⠤` `⣀⣀⣀` | a scanning line travels down |
706
- | Writing | `⡼⠭⠧` `⠼⡯⠧` `⠼⠿⡧` `⠼⡭⠧` | **a laptop typing** |
707
- | Commands | `⠉⠀⠀` `⠉⠈⠀` `⠛⠊⠀` `⠿⠮⠄` | output piles up a line at a time |
708
- | Answering | `⠉⠀⠀` `⠛⠉⠀` `⠿⠛⠉` `⣿⠿⠛` | text fills up |
709
- | Web | `⣀⣀⣀` `⣤⣀⣀` `⣶⣤⣀` `⣿⣶⣤` | signal bars grow |
710
- | Compacting | `⣿⣿⣿` `⣶⣶⣶` `⣤⣤⣤` `⣀⣀⣀` | pressed down into one line |
711
- | Past 45 seconds | `⠶⠀⠀` `⠰⠆⠀` `⠀⠶⠀` `⠀⠰⠆` | back and forth — "waiting" |
712
-
713
- Braille only. One braille cell is **2 wide by 4 dots tall**, so three cells make a 6×4 grid
714
- that is exactly three columns in any terminal. Emoji and `●` `▪` include glyphs that East
715
- Asian locales measure as two columns, which knocks the border out of line every 90ms.
716
-
717
- If the drawing does not render, or you use a screen reader, `DEEL_NO_MOTION=1` turns it off
718
- and you get the old single-cell spinner.
719
-
720
- ### What gets asked, and what just happens
721
-
722
- Whether your files change **with or without being asked** is the one thing that has to be
723
- readable at a glance. It sits on the right of the status line at all times.
724
-
725
- | Indicator | Command | What it asks about |
726
- |---|---|---|
727
- | `⏵⏵ 자동 승인` (auto) | `/mode auto` | Nothing is asked. `/undo` is the safety net |
728
- | `⏵ 위험만 확인` (risky only) | `/mode confirm` | Only irreversible commands. Files change unasked |
729
- | `⏸ 모두 확인` (everything) | `/mode strict` | Every file change and every command is confirmed first |
730
-
731
- `/mode` on its own lists all three and marks the current one with ●. The startup header
732
- spells it out in a sentence, so the glyph is enough from then on.
733
-
734
- **`Shift+Tab` cycles it without typing.** Each press moves one step and leaves a line
735
- saying where it went. Whatever you were typing stays put.
736
-
737
- ```
738
- ⏵ 위험만 확인 되돌릴 수 없는 명령만 물어봅니다. 파일은 안 묻고 고칩니다
739
- 자동 승인 → 위험만 확인 · Shift+Tab 으로 계속 바꿉니다
740
- ```
741
-
742
- The cycle runs **loose → strict** (auto → risky only → everything → auto). A mistaken
743
- press only makes it ask more; it never drops you into "changes files unasked" in one hit.
744
-
745
- > That key used to cycle the work mode (`종합`, `코드`, …). The swap is about **who
746
- > reaches for it more often.** Work mode follows your request on its own, while approval
747
- > policy is what you want to change mid-task when a particular job deserves a look.
748
- > Work mode moved to `Ctrl+O`; `/work` still does the same thing.
749
-
750
- > It used to be the bare word `auto`. Next to `종합` and `medium·절약` it looked like just
751
- > another mode, and nothing on screen said that one of them meant **files change without
752
- > 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
753
396
  >
754
- > That also changed what gets truncated when space runs out. A corporate gateway model name
755
- > like `databricks-gpt-5-6-luna` eats twenty-three columns, and that alone was pushing the
756
- > approval indicator off the line entirely. Now **the model name shortens first** — you
757
- > already know what you are running; whether your files change unasked is what you need now.
758
-
759
- </details>
397
+ > **[The screen read →](docs/en/interface.md#simple-vs-developer)**
760
398
 
761
399
  ---
762
400
 
@@ -766,7 +404,7 @@ Names and arguments match Claude Code, so skills written for that convention wor
766
404
 
767
405
  | Tool | What it does |
768
406
  |---|---|
769
- | `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**) |
770
408
  | `Write` | Write / overwrite a file (**several at once via the `files` array**) |
771
409
  | `Append` | Append to the end of a file — **how large files get written in pieces** |
772
410
  | `Edit` | Replace an exact string (`replace_all`; **several sites at once via the `edits` array**) |
@@ -782,773 +420,163 @@ Names and arguments match Claude Code, so skills written for that convention wor
782
420
  | `Verify` | Check that what was built **actually works** |
783
421
  | `Task` | Run one chunk of a big job in a **separate context** |
784
422
  | `Jobs` | Inspect, read and stop **background commands** — the other half of `Bash`'s `background` |
423
+ | `Def` | **Where a name is defined** — only shown when a language server is installed |
424
+ | `Refs` | **Every place a name is used** — only shown when a language server is installed |
785
425
 
786
426
  Seven tools here are not in Claude Code — `Append`, `Recall`, `Remember`, `Outline`,
787
427
  `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
788
428
  so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
789
429
  their cost; here is why.
790
430
 
791
- <details>
792
- <summary><b>More</b> — Outline · Verify · Task · Commands that never finish and 9 more</summary>
793
-
794
- ### Seeing a project's shape cheaply — `Outline`
795
-
796
- There used to be only two ways to understand someone else's code. `Glob` gives you paths;
797
- `Read` pulls a whole file into the window. **The middle was missing.**
798
-
799
- So the model started editing without knowing what lived where, and re-created functions
800
- that already existed somewhere else. It had not seen them — which is different from not
801
- 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)**
802
434
 
803
- ```
804
- ❉ Outline(src/ui) 12 files · 122 places
435
+ ---
805
436
 
806
- src/ui/screen.js (304 lines)
807
- 46 fn 상자쓸까
808
- 65 class LineScreen
809
- 92 method 줄
810
- 198 class BoxScreen
811
- ```
437
+ ## Korean text and Excel
812
438
 
813
- Reading that folder whole costs **25,612 tokens**; `Outline` costs **857** 30x cheaper.
814
- 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.
815
441
 
816
- It reads js/ts, py, java/kotlin, go, rust, c#, md, html, css, sh and json. Regex, not a
817
- parser (zero dependencies). So it **says what it could not read** — dropping those
818
- silently makes the model believe the file does not exist, and rebuild config that is
819
- already there.
442
+ > **More** Encoding · Excel
443
+ >
444
+ > **[Korean documents and Excel read →](docs/en/documents.md#korean-text-and-excel)**
820
445
 
821
- ### Checking what was built — `Verify`
446
+ ---
822
447
 
823
- The end of a turn used to say:
448
+ ## Serving what you built
824
449
 
825
450
  ```
826
- index.html · 410 lines · 18.2KB
827
- ```
828
-
829
- That proves the file **exists**, not that it **works**. An unclosed `<div>`, a
830
- `src="app.js"` pointing at nothing, a JS file one bracket short — all green.
831
-
832
- What can be run gets run (`node --check`, `py_compile`); what cannot gets read (HTML tag
833
- pairing, missing references, CSS braces, JSON).
451
+ /preview
834
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.
835
458
  ```
836
- ⏺ Verify 1 broken · 3 checked
837
- ```
838
-
839
- And the part that matters most — **what could not be checked is reported as such.**
840
459
 
841
- Arbitrary commands are **not** run here. That path has to be `Bash` alone: the approval
842
- gate and the safety checks live only there, so running commands from here would break the
843
- 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.
844
461
 
845
- ### 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:
846
465
 
847
- Building eight files in one window means all eight files pile up in that window. On a 32k
848
- model it fills around the third or fourth, and once it fills, earlier turns get folded
849
- away. From then on the model has forgotten what it was building — **no error appears, the
850
- 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** |
851
474
 
852
- `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**.
853
476
 
854
- ```
855
- subtask build the page skeleton separate conversation · max 8 steps
856
- │ ◆ Write(index.html +1) 2 files · 24 lines
857
- ✓ subtask build the page skeleton done · 2 files · 2 steps
858
- ```
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).
859
480
 
860
- Peak conversation size while building the same four files (system prompt excluded):
481
+ ### It opens exactly as much as it says
861
482
 
862
- | | Peak |
863
- |---|---|
864
- | All in one window | 4,181 chars |
865
- | Split with `Task` | **2,113 chars** |
483
+ Starting a server means opening your disk to somebody else.
866
484
 
867
- 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.
868
492
 
869
- **Every guard still applies.** A subtask runs inside the same working folder, follows the
870
- same approval mode, is undone **together with** its parent by one `/undo`, and lands in
871
- the audit log. There is no path for a subtask to edit files under a read-only mode
872
- (architect, plan, ask) — that is blocked both at the mode level and in the tool list.
873
- Nesting stops at two levels.
493
+ ---
874
494
 
875
- ### Commands that never finish — `Bash`'s `background` and `Jobs`
495
+ ## Skills and plugins
876
496
 
877
- `Bash` only returns once the command **ends**. So anything that does not end could not be
878
- run `npm run dev`, `python -m http.server`, `vite`, `npm run watch`. Asking for one used
879
- 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.
880
499
 
881
500
  ```
882
- ▶ Bash(npm run dev)
883
- └ 시간 초과로 중단됨 (120000ms) 2분 0.0초
884
- (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/**
885
504
  ```
886
505
 
887
- The model concludes the server would not start and gives up, or worse, raises `timeout`
888
- and calls again — which stalls the whole turn. **There was no way at all to start what
889
- you built and check it.** `Verify` gets you as far as "the syntax is valid"; whether it
890
- actually comes up requires bringing it up.
506
+ Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
891
507
 
892
- ```
893
- ▶ Bash(npm run dev)
894
- 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)**
895
511
 
896
- ◈ Edit(src/App.jsx)
897
- └ 1군데 (1 site)
512
+ ---
898
513
 
899
- Jobs(1번)
900
- └ 도는중 · 24초 (running · 24s)
514
+ ### The hidden latency of local models — keeping the prefix cache alive
901
515
 
902
- Jobs(1번 · 끝내기)
903
- 끝냄 · 41초 (stopped · 41s)
904
- ```
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.
905
520
 
906
- 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`).
907
525
 
908
- **Something that did not start is never reported as started.** The job is watched briefly
909
- after launch, and if it dies in that window it comes back as a failure. The most common
910
- failure is a port already in use; reporting that as "started" sends the model on to the
911
- 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.
912
530
 
913
- ```
914
- ▶ Bash(npm run dev)
915
- └ 띄우자마자 끝났습니다 (종료코드 1).
916
- (exited immediately, exit code 1)
917
- ```
531
+ ## Reasoning effort
918
532
 
919
- | Guarantee | Detail |
920
- |---|---|
921
- | Safety checks | **Identical** to `Bash`. This must not become a back door |
922
- | Cleanup | Everything is killed when deel exits **down to grandchildren**, and it says how many |
923
- | Retained | 256KB. Past that the front is dropped and **the drop is stated** |
924
- | Handed to the model | 4,000 chars. This **must** be a different number from the one above |
925
- | On stop | Waits for the dying output, and returns only once the process is **actually dead** |
926
- | Argument names | Korean and English both accepted (`번호`/`job`, `끝내기`/`stop`). Unrecognized ones **are reported** |
927
- | Count | Eight running. Finished jobs keep the most recent eight, and evictions **are stated** |
928
-
929
- Why two different caps: make them equal and every overflow of a `watch` job means
930
- one `Jobs` read dumps 256KB into the window. On an 8k model that single read ends
931
- the window.
932
-
933
- **Stopping a job does not close its pipes immediately.** At the moment the kill is
934
- issued there is still unread data in the pipe, and the last few lines before a
935
- death are the ones that matter — the stack trace a server leaves on the way down.
936
- Printing `last output:` and then withholding the last output is worse than not
937
- printing it. It also waits until the process is **confirmed dead** before dropping
938
- it from the list: dropping a live one means it can never be named again, which is
939
- the exact state this feature exists to prevent.
940
-
941
- Finished jobs are not dropped right away — they are kept so their final output can
942
- be read. Only the most recent eight survive; otherwise thirty short commands leave
943
- thirty entries, each holding up to 256KB.
944
-
945
- **Argument names are accepted in both Korean and English.** Models frequently
946
- translate Korean parameter names into English — not a guess, something this repo
947
- 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.
948
538
 
949
539
  ```
950
- 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
951
544
  ```
952
545
 
953
- The model asked for a stop and **got what looks like a success** while the port
954
- stays held. So both spellings are accepted, and when nothing is recognized it says
955
- so rather than falling back to a listing. The name mapping lives in exactly **one**
956
- place the on-screen label reads it too. Two copies means the tool works while the
957
- 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 |
958
551
 
959
- **Killing grandchildren is where this quietly goes wrong.** `npm run dev` descends
960
- npm node vite, and the thing holding the port is at the bottom. Windows has
961
- `taskkill /t` to walk the tree; Unix has nothing equivalent, so the job is
962
- **started in its own process group** — after the fact there is no way to name a
963
- 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.
964
555
 
965
- Cleanup is where this quietly goes wrong. Skip it and a process nobody started keeps
966
- running. Next time you start a dev server you get "port already in use" with **no way to
967
- find what is holding it**. So `test/jobs.test.js` verifies the process actually died, via
968
- a file the child keeps appending to.
556
+ The stage table moved to `/think 자세히` (the default at developer level).
969
557
 
970
- `deel run` (one-shot mode) does the same. A batch job is hurt worst by missing this — the
971
- job reports done, the server keeps running, and the next job fails to bind the same port
972
- with nothing in the log to explain it.
558
+ ```
559
+ $ /think 자세히
973
560
 
974
- ### Several at once `Write`'s `files`, `Edit`'s `edits`
561
+ 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
562
+ 배분 절약 첫 판단만 세게, 이어가기는 얕게 — 대개 이게 낫습니다
975
563
 
976
- One round trip is 20-40 seconds on a local model. Creating five files with five `Write`
977
- 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 직전 도구가 오류를 냄
978
568
 
979
- ```
980
- Edit(src/app.js 2군데)
981
- └ 2개 파일 · 3군데 (2 files · 3 sites)
982
- ✓ src/app.js · 2군데
983
- ✓ src/style.css · 1군데
569
+ 출력 상한은 16,384 (모르는 값이라 기본값) 안에서 나눕니다 — /out
570
+ 컨텍스트 40,960 · 지금 찬 양 2,087
984
571
  ```
985
572
 
986
- **Editing is worth more than writing here.** Creating files happens once; editing happens
987
- 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.
988
576
 
989
- | | Guaranteed |
990
- |---|---|
991
- | Applied in order | Editing one file twice is common. Each edit re-reads from disk, so later ones see earlier results |
992
- | One failure | The rest still run. Stopping at the first failure re-adds the round trips this was meant to remove |
993
- | On failure | "Resend only what failed — **and `Read` the file again first**" |
994
- | Undo | Still **one turn**. Six sites in one file is one `/undo` |
995
- | Single-site form | The result shape is byte-for-byte unchanged |
996
-
997
- It reports `2 files · 3 sites` rather than a single number. Editing one file at six sites
998
- is normal, so "3 files" would be false — and once the screen stops matching what you can
999
- count yourself, you stop trusting the screen.
1000
-
1001
- ### Finding past conversations, and remembering decisions
1002
-
1003
- deel writes every conversation to `.deel/sessions/*.jsonl`. Until now all you could do was
1004
- list them — **a record you cannot search is the same as no record.**
1005
-
1006
- ```
1007
- $ /recall 인코딩을 어떻게
1008
-
1009
- 2026-08-01 10:15 모델 20260801-101500
1010
- CP949 인코딩 문제입니다. 읽을 때 인코딩을 재서 그대로 되돌려 쓰도록…
1011
- ```
1012
-
1013
- Korean particles are handled: `인코딩을` also matches `인코딩`. A morphological analyser is
1014
- out of the question (zero dependencies), so particle-looking tails are stripped and **both**
1015
- forms are searched. No index is built — an index inevitably goes stale, and **a stale index is
1016
- worse than none** ("not found" reads as "never happened"). Instead every search reports how
1017
- much it read and what it could not.
1018
-
1019
- `Recall` is also a **tool**. Left as a human-only command, "do it the way we decided last
1020
- time" leaves the model nothing to do but ask again.
1021
-
1022
- **Memory (`/memory`) is a different thing.** Recall has to be *searched*; memory is *already
1023
- there*. Things you cannot re-explain every session go here.
1024
-
1025
- ```
1026
- $ /memory
1027
-
1028
- 1 사내 문서는 CP949 로 읽고 CP949 로 되돌려 쓴다
1029
- 2 검증할 때 7080 포트는 쓰지 않는다
1030
-
1031
- 2줄 · 약 30토큰이 매 요청마다 함께 나갑니다
1032
- 파일 .deel/memory.md — 직접 고치셔도 됩니다
1033
- ```
1034
-
1035
- `.deel/memory.md` is **prose a human edits**, not a database. That matters: a line the model
1036
- got wrong ships on every request and keeps being wrong. **A wrong memory is worse than none.**
1037
- So `/memory 지우기 2` deletes one.
1038
-
1039
- Because it ships on every request it is bounded: 400 chars per line, 60 lines, 6,000 chars
1040
- total. Overflow drops the oldest and says so. `/context` shows its line count and tokens.
1041
-
1042
- ### Repeatable procedures become skills
1043
-
1044
- When a multi-step job finishes and it is something that will come up again, the model writes
1045
- the procedure to `.deel/skills/<name>/SKILL.md`. Next session it appears in the skill list;
1046
- when it turns out to be wrong somewhere, the model edits that file.
1047
-
1048
- No new tool needed — the existing `Write` writes it and the existing skill sweep reads it.
1049
-
1050
- ### Large files are written in pieces — `Append`
1051
-
1052
- A model with a 4k output cap still has to be able to write a 2,000-line file, eight pieces at
1053
- a time. Stitching with `Edit` does not work in practice — HTML repeats anchors like `</div>`,
1054
- so the match comes back as "found in several places", and a longer anchor eats the tokens that
1055
- should have gone into the body.
1056
-
1057
- `Write` to create, `Append` to continue. Encoding follows `Write` (CP949 for corporate
1058
- documents, the BOM on a `.csv` is preserved). The undo snapshot is taken **only on the first
1059
- `Append`** — eight appends must not leave eight copies in the history, or there is no single
1060
- point to revert to.
1061
-
1062
- ```
1063
- ⏺ Write(dashboard.html)
1064
- └ ⚠ wrote only as far as it arrived — 632 lines
1065
- ↻ the reply hit the cap — retrying with 9,984 → 16,384
1066
- ⏺ Append(dashboard.html)
1067
- └ +567 lines · 1,199 total
1068
-
1069
- ✓ dashboard.html · 1,199 lines · 97.7KB
1070
- ```
1071
-
1072
- That last line matters. If the file does not exist and the model says "created it", you would
1073
- believe it. **The real file is measured at the end of the turn.**
1074
-
1075
- ### Checklists
1076
-
1077
- Keeps the model from losing its place on multi-step work. The list is redrawn whenever the
1078
- model updates it.
1079
-
1080
- ```
1081
- ☰ Todo 1/3 done ← just finished 1
1082
-
1083
- ✓ unify log format
1084
- ▶ fix the tests
1085
- ☐ update the docs
1086
- ```
1087
-
1088
- Only one item may be **in progress** at a time; setting two is refused. Holding several at
1089
- once is how nothing gets finished.
1090
-
1091
- ### Read-only tools run together
1092
-
1093
- When the model asks for three `Read` calls at once, all three run **concurrently** — sweeping
1094
- five files costs about what reading one costs.
1095
-
1096
- ```
1097
- ◧ Read src/a.js ◧ Read src/b.js ◧ Read src/c.js together
1098
- ```
1099
-
1100
- Only `Read`, `Glob`, `Grep`, `Skill` and `WebFetch` are eligible. `Write`, `Edit` and `Bash`
1101
- always run one at a time — two concurrent writes to one file scramble the undo snapshot
1102
- order, and `Bash` can do anything. Results come back **in the order the model asked for
1103
- them**, even when they finish out of order; shuffled results confuse the model about which
1104
- result belongs to which call.
1105
-
1106
- ### Edits survive small mistakes
1107
-
1108
- Models routinely get whitespace, indentation and line endings wrong. deel relaxes matching in
1109
- stages but **refuses outright when the match is ambiguous** — silently editing the wrong place is
1110
- far worse than not finding it.
1111
-
1112
- ```
1113
- exact → ignore trailing space / CRLF → ignore indentation → ignore all whitespace
1114
- ```
1115
-
1116
- Measured with `npm run bench`:
1117
-
1118
- | | Success | Wrong place edited |
1119
- |---|---|---|
1120
- | Exact match only | 20% | 0 |
1121
- | Staged relaxation | **100%** | **0** |
1122
-
1123
- On failure it points at the closest line in the file.
1124
-
1125
- ### It shows you what it changed
1126
-
1127
- `auto` mode edits without asking. That is the speed of this tool — but if all that reaches the
1128
- screen is `1 spot`, you move on without knowing what happened. Undo is the safety net, and
1129
- **you cannot decide whether to undo something you never saw.** So every `Edit` and `Write` is
1130
- followed by the changed lines themselves.
1131
-
1132
- ```
1133
- ◈ Edit(src/runner.js)
1134
- └ 1 spot +1 −2
1135
-
1136
- 11 const id = job.id;
1137
- - console.log("start: " + id);
1138
- - console.log(" opts " + JSON.stringify(opts));
1139
- + 12 logger.info('start', { id, opts });
1140
- 13 return run(job);
1141
- ```
1142
-
1143
- The `+1 −2` next to the summary is how many lines were added and removed.
1144
-
1145
- - **Removed lines carry no line number.** They no longer exist in the file. Printing the old
1146
- number put it directly under a context line's new number — two different files' numbering in
1147
- one column. Line 8 really did appear twice on screen.
1148
- - If only the line endings changed (CRLF/LF), it says so. Otherwise every visually identical
1149
- line shows as changed and the real edit is impossible to find.
1150
- - Large files are compared after trimming the identical head and tail. If it is still too big,
1151
- exact matching is abandoned for "this whole block changed" — a rough answer now beats an
1152
- exact one later.
1153
-
1154
- How many lines are shown depends on the level. Forty lines at someone's first launch means none get read.
1155
-
1156
- | | Simple | Developer |
1157
- |---|---|---|
1158
- | After a tool call | 14 lines | 40 lines |
1159
- | `/diff <file>` | 60 lines | 200 lines |
1160
-
1161
- ### `/diff` — everything changed this session
1162
-
1163
- Those lines scroll away. `/diff` collects every file touched this session onto one page.
1164
-
1165
- ```
1166
- $ /diff
1167
-
1168
- ── files changed this session ─────────────────────────────
1169
- src/runner.js +12 −7 3×
1170
- src/logger.js +40 −0
1171
- ──────────────────────────────────────────────────────────
1172
- 2 files +52 −7
1173
-
1174
- /diff <file> for detail, /undo to revert
1175
- ```
1176
-
1177
- `/diff <file>` compares **the state at the start of the session against now.** Even after
1178
- three edits, what you want to know is "what is different from before I asked", not what the
1179
- last edit did. That original state comes from the earliest undo snapshot.
1180
-
1181
- `/diff` is **in the simple level's command list.** As long as `auto` edits without asking,
1182
- a beginner needs a way to see what changed more than anyone.
1183
-
1184
- </details>
1185
-
1186
- ---
1187
-
1188
- ## Korean text and Excel
1189
-
1190
- **A file saved as CP949 is written back as CP949.** The encoding is never changed.
1191
- Excel (`.xlsx`) is read as CSV — read-only.
1192
-
1193
- <details>
1194
- <summary><b>More</b> — Encoding · Excel</summary>
1195
-
1196
- ### Encoding — written back the way it was read
1197
-
1198
- Corporate documents are often not UTF-8. Files saved by old Windows Notepad in a legacy
1199
- codepage (CP949 in Korea, CP932 in Japan, GBK in China) are still around. Reading one as
1200
- UTF-8 garbles it completely: `한글` becomes `�ѱ�`.
1201
-
1202
- Writing is the dangerous part. Read it garbled, save it as UTF-8, and the original is gone.
1203
- So there is one rule: **write it back in the encoding it was read in.**
1204
-
1205
- Which encoding that is comes from **the file's contents, not the machine's settings.**
1206
- Each candidate is decoded strictly, then scored on whether the result looks like real text
1207
- written in that encoding. So the same CP949 document reads identically on Ubuntu, on a US
1208
- Windows machine, and on a Korean one.
1209
-
1210
- ```
1211
- › Read report.txt
1212
- └ 4 lines · CP949
1213
- ```
1214
-
1215
- If you try to insert a character that encoding **cannot hold**, it refuses instead of saving.
1216
-
1217
- ```
1218
- › Edit report.txt note → note 🚀
1219
- └ This file is CP949, and you are inserting a character that encoding does not have: 🚀
1220
- ```
1221
-
1222
- Silently substituting question marks would be worse than not writing at all.
1223
- Newly created files are UTF-8.
1224
-
1225
- Command output is handled the same way. A Windows console is not UTF-8, so taking `Bash`
1226
- output as utf8 garbles non-ASCII text. It is collected as bytes and decoded afterwards.
1227
-
1228
- **Undo snapshots are stored as bytes too.** They used to be stored as UTF-8 text, so undoing
1229
- a CP949 file brought back `가나다` (bytes `b0a1 b3aa b4d9`) as six U+FFFD characters — **the
1230
- safety net itself destroyed the original bytes.** Now every snapshot is round-tripped through
1231
- UTF-8 first; anything that does not come back identical is stored as base64 and restored
1232
- byte-exact.
1233
-
1234
- ### Excel — read as CSV
1235
-
1236
- An Excel file is a compressed archive, not text, so normally you get "this is a binary file"
1237
- and somebody has to export a CSV by hand. `Read` just does it.
1238
-
1239
- ```
1240
- › Read report.xlsx
1241
- └ 3 sheets · 128 rows · unpacked directly
1242
- ```
1243
-
1244
- - **Still zero dependencies.** An xlsx is a zip full of XML, so Node's built-in `zlib` is enough.
1245
- - Every sheet is returned. Hidden sheets too, marked as hidden.
1246
- - Dates come back as dates, not serial numbers — the cell format is read to decide.
1247
- - Formulas come back as **computed values**, and error values like `#REF!` are not dropped.
1248
-
1249
- **Password-protected files and legacy `.xls`** are handed to Excel itself; those cannot be
1250
- unpacked directly. You are asked for the password at that point.
1251
-
1252
- The password is **not stored anywhere**:
1253
-
1254
- - not in the config file
1255
- - not in the session log
1256
- - not in the audit log
1257
- - not as a command-line argument (other people can see your command lines)
1258
-
1259
- The only path out is the child process's stdin, and a test asserts that this stays true.
1260
- Extracted intermediate files are deleted after use.
1261
-
1262
- > **Excel files are read-only here.** `Edit` and `Write` refuse them, and say why and what
1263
- > to do instead. Round-tripping a file with formatting, formulas and charts through CSV
1264
- > always loses something. Better not to write than to write knowing you'll lose data.
1265
-
1266
- </details>
1267
-
1268
- ---
1269
-
1270
- ## Serving what you built
1271
-
1272
- ```
1273
- ❯ /preview
1274
-
1275
- ▶ Serving http://127.0.0.1:56801/
1276
- showing .
1277
- Edit a file and the page reloads by itself.
1278
- Only this machine can open it (127.0.0.1). No other PC can see it.
1279
- Stop with /preview off · it shuts down when deel exits.
1280
- ```
1281
-
1282
- A browser opens with it. `/preview <folder>` picks what to serve, `/preview off` stops it.
1283
-
1284
- **This is not the same as double-clicking the file (`file://`).** Under `file://` everything
1285
- below is blocked — and the error only shows up in the console while the page stays blank, so
1286
- you end up suspecting your own code. This is a real HTTP server, so it all works:
1287
-
1288
- | | `file://` | `/preview` |
1289
- |---|---|---|
1290
- | `<script type="module">` · `import` | blocked (CORS) | **works** |
1291
- | `fetch('./data.json')` | blocked | **works** |
1292
- | `new Worker(...)` | blocked | **works** |
1293
- | `WebAssembly.compileStreaming` | blocked (MIME) | **works** |
1294
- | textures · `getImageData` | tainted canvas | **works** |
1295
- | `.glb` / `.gltf` (Three.js) | no MIME type → silently not drawn | **works** |
1296
-
1297
- All seven were run in a real Chrome and confirmed **7/7**.
1298
-
1299
- Apps with a router (React Router and friends) get the first page back when you reload on a
1300
- deep link. Never for requests with an extension (`app.js`) though — returning HTML for a
1301
- missing script dies with `Unexpected token '<'`, which hides the real cause (a typo in a filename).
1302
-
1303
- ### It opens exactly as much as it says
1304
-
1305
- Starting a server means opening your disk to somebody else.
1306
-
1307
- - Bound to **`127.0.0.1` only**. `0.0.0.0` is not available at all — on an office network
1308
- that would let anyone read your source.
1309
- - Port **0** (the kernel hands out a free one). A fixed port steals someone else's.
1310
- - Paths cannot leave the working scope. `../` · `%2e%2e` · double encoding · absolute paths ·
1311
- null bytes · symlinks — eight of these are held shut by tests.
1312
- - **It only serves.** `POST` · `PUT` · `DELETE` are refused with 405.
1313
- - It shuts down when `deel` exits.
1314
-
1315
- ---
1316
-
1317
- ## Skills and plugins
1318
-
1319
- **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
1320
- whatever is there. On a clean PC: zero. On a PC with skills installed: those skills.
1321
-
1322
- ```
1323
- project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
1324
- user ~/.deel/skills ~/.claude/skills ~/.claude/commands
1325
- plugins ~/.claude/plugins/** ~/.deel/plugins/**
1326
- ```
1327
-
1328
- Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
1329
-
1330
- <details>
1331
- <summary><b>More</b> — Loaded in three stages · Fetching plugins · Deliberately not included</summary>
1332
-
1333
- ### Loaded in three stages
1334
-
1335
- Loading everything would blow the context window.
1336
-
1337
- | Stage | What | Cost |
1338
- |---|---|---|
1339
- | 1 | Name + one-line description in the prompt | ~1,800 tokens for 40 skills |
1340
- | 2 | Body of the one the model picks via `Skill` | one at a time |
1341
- | 3 | Files that body references, via `Read` | on demand |
1342
-
1343
- ### Fetching plugins
1344
-
1345
- ```bash
1346
- # on a connected machine
1347
- /plugin install affaan-m/ECC # git clone, or tarball when git is absent
1348
- /plugin pack import.zip # bundle, excluding executable scripts
1349
-
1350
- # on the air-gapped machine — just unzip
1351
- unzip import.zip -d ~/.deel/plugins/
1352
- ```
1353
-
1354
- `/plugin pack` omits `.js` `.sh` `.ps1` `.py` and friends, and includes a plain-text manifest
1355
- with a licence table — ready to hand to a security reviewer.
1356
-
1357
- ### Deliberately not included
1358
-
1359
- | | Why |
1360
- |---|---|
1361
- | hooks | Executable scripts — fails import review, widens the blast radius of autonomy |
1362
- | sub-agents | Doubles model calls against a gateway quota |
1363
- | MCP | A separate protocol; a project of its own |
1364
-
1365
- </details>
1366
-
1367
- ---
1368
-
1369
- ## Reasoning effort
1370
-
1371
- One answer means several model calls, and **each needs a different amount of thinking.**
1372
- All-high is slow; all-low wanders off.
1373
-
1374
- The default is **one line**. What you want to know is how hard it is thinking right now,
1375
- not a stage table.
1376
-
1377
- ```
1378
- $ /think
1379
-
1380
- 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
1381
- 더 세게 /think high 더 빠르게 /think low
1382
- ```
1383
-
1384
- | Profile | Character |
1385
- |---|---|
1386
- | `even` | Same effort everywhere — predictable, slower |
1387
- | `save` (default) | Hard on the first decision only |
1388
- | `deep` | Everything one notch up — for hard work |
1389
-
1390
- Set the profile with `/think 배분 절약`. **Level and profile are different axes, so the
1391
- commands were split** — `/think high` and `/think save` used to set different things under
1392
- one name, which made the screen unreadable.
1393
-
1394
- The stage table moved to `/think 자세히` (the default at developer level).
1395
-
1396
- ```
1397
- $ /think 자세히
1398
-
1399
- 추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
1400
- 배분 절약 첫 판단만 세게, 이어가기는 얕게 — 대개 이게 낫습니다
1401
-
1402
- 단계 강도 출력상한 언제
1403
- 첫 판단 · medium 15,549 무엇을 할지 정하는 자리
1404
- 이어가기 ↓ low 13,605 도구 결과를 읽고 다음 한 수
1405
- 막혔을 때 ↑ high 16,384 직전 도구가 오류를 냄
1406
-
1407
- 출력 상한은 16,384 (모르는 값이라 기본값) 안에서 나눕니다 — /out
1408
- 컨텍스트 40,960 · 지금 찬 양 2,087
1409
- ```
1410
-
1411
- That second-to-last line exists for a reason: **when all three caps are equal, it is the
1412
- only thing that says whether that is correct.** A low known cap makes them equal, and that
1413
- is fine. For a while all three read `16,384` always — which meant the table said nothing.
1414
-
1415
- <details>
1416
- <summary><b>More</b> — Context length is read off the model · /out · Truncated tool calls</summary>
1417
-
1418
- ### Context length is read off the model
1419
-
1420
- This one number sizes the whole program: how many files fit in one read, when the
1421
- conversation gets folded, how long a single reply may be — **all of it comes from here.**
1422
-
1423
- So deel asks the server on every launch rather than trusting the saved value. The same
1424
- model name can be loaded at a different length each time, and if that difference never
1425
- reaches the screen there is no way to notice. **It just quietly gets smaller.**
1426
-
1427
- ```
1428
- │ Model qwen3-coder (640k tokens) │
1429
- ╰─────────────────────────────────────────────────────────╯
1430
- ✓ Context adjusted 32,768 → 655,360 (read from LM Studio)
1431
- ```
1432
-
1433
- Every server puts this number under a different name in a different place, so deel checks
1434
- all of them.
1435
-
1436
- | Server | Where it reads |
1437
- |---|---|
1438
- | LM Studio | `/api/v0/models` — `max_context_length`, `loaded_context_length` |
1439
- | llama.cpp | `/props` — `n_ctx` |
1440
- | vLLM | `/v1/models` — `max_model_len` |
1441
- | Ollama | `/api/show` — `<model>.context_length` |
1442
- | Other OpenAI-compatible | `/v1/models/<model>` — `context_window`, `context_length`, `max_input_tokens`, `max_position_embeddings` (found even when nested) |
1443
-
1444
- **Model maximum and loaded length are not the same thing.** LM Studio will happily load a
1445
- 655,360-capable model at 8,192. Trusting the maximum there gets your requests rejected. So
1446
- the **loaded length is what deel uses**, and the maximum is reported separately.
1447
-
1448
- ```
1449
- ⚠ This model goes up to 655,360 — raise it on the server, then /ctx auto
1450
- ```
1451
-
1452
- | Command | What it does |
1453
- |---|---|
1454
- | `/ctx` | Current length and remaining room |
1455
- | `/ctx auto` | Ask the server again and match the model |
1456
- | `/ctx 655360` | Set it yourself (`640k`, `128k`, `1m` also work) |
1457
- | `/ctx 자세히` | Which endpoints were probed and what each returned — how to see why a lookup failed |
1458
- | `deel --ctx 655360` | Start at this value (skips the lookup) |
1459
-
1460
- **`k` means 1024 here.** Context lengths are all powers of two, so that is the only base
1461
- that lines up: 655,360 is `640k`, not `655k`; 131,072 is `128k`, not `131k`. The display and
1462
- `/ctx` use the same unit, so typing back what you see gives you the same number.
1463
-
1464
- ### Reply length cap — `/out`
1465
-
1466
- Context (how much can be held) and the **output cap** (how much can come back at once) are
1467
- different numbers. Treating them as one makes it impossible to understand why a large file
1468
- never gets written — the context is roomy while the reply is being cut.
1469
-
1470
- | Command | What it does |
1471
- |---|---|
1472
- | `/out` | Current cap and **where it came from** (set by you / discovered / default) |
1473
- | `/out 32k` | Set it yourself (`k` is 1024). Saved to the profile |
1474
- | `/out auto` | Drop your value and go back to the discovered one, or the default |
1475
- | `deel --max-tokens 65536` | Start at this value |
1476
-
1477
- The old name `/ctx out 32k` still works.
1478
-
1479
- **Caps are not fixed numbers.** They are computed from the model's context window and how
1480
- much of it is currently used — the profile decides what share of the remaining room a stage gets.
1481
-
1482
- | Model | First call | Continuing | Stuck | Retry after truncation |
1483
- |---|---|---|---|---|
1484
- | 2k local | 819 | 716 | 921 | 1,638 |
1485
- | 8k local | 3,276 | 2,867 | 3,686 | 6,553 |
1486
- | 40k (qwen3) | 16,384 | 14,336 | 16,384 | 16,384 |
1487
- | 128k gateway | 16,384 | 16,384 | 16,384 | 16,384 |
1488
- | 128k, 80% full | 10,485 | 9,174 | 11,796 | 16,384 |
1489
- | 640k with `/out 65536` | 65,536 | 65,536 | 65,536 | 65,536 |
1490
-
1491
- Caps shrink as the context fills. Handing a 4k model a 4,096-token cap would leave no room for input.
1492
-
1493
- The last row is the point: **a known cap overrides the 16,384 default.** For a while it did
1494
- not — the third argument of `Math.min(cap, max ?? 16384, 16384)` clamped it right back, so a
1495
- configured cap could only be lowered, never raised. Meanwhile the comment, the README, and the
1496
- on-screen help all said it could be raised. A documented escape hatch that is welded shut is
1497
- the worst kind.
1498
-
1499
- If a cap truncates a reply, **the call is retried with the cap lifted** — and the thinking
1500
- level drops one notch, because reasoning tokens eat the same budget first. Without that, more
1501
- headroom just buys more thinking. A truncated reply means a half-written tool call, which fails silently.
1502
-
1503
- **When the server refuses, it is read for the answer.**
1504
-
1505
- ```
1506
- This model's maximum context length is 8192 tokens, however you requested 41003
1507
- ```
1508
-
1509
- The number is extracted, applied, and the call is retried. You never see the failure.
1510
- No spec knowledge is needed, so **this works against servers we have never seen.**
1511
-
1512
- ### Truncated tool calls
1513
-
1514
- This actually happened. A user asked for a dashboard; the model tried to put an entire HTML
1515
- document into `Write`'s arguments, hit the output token limit, and the arguments JSON arrived
1516
- cut off mid-string.
1517
-
1518
- The old code quietly turned that unparseable JSON into `{_raw: "..."}` and handed it to the
1519
- tool. The tool answered `path is empty` — **a message with nothing to do with the real cause.**
1520
- The model had not omitted the path, so there was nothing to fix; it retried identically, and
1521
- was truncated again.
1522
-
1523
- ```
1524
- ◆ Write(dashboard.html)
1525
- └ path is empty ← nine identical times
1526
-
1527
- ── 71s · 13 tool calls · context filled and auto-compacted · no file produced
1528
- ```
1529
-
1530
- One silently swallowed value produced all of that. What happens now:
1531
-
1532
- | | Now |
1533
- |---|---|
1534
- | Unparseable arguments | **Marked as truncated**, not swallowed. Never passed to the tool |
1535
- | 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`** |
1536
- | The truncated payload | Never re-injected into the conversation — it is half a payload and it costs context |
1537
- | 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 |
1538
- | Three identical failures | The turn stops, with a suggestion to split the request |
1539
-
1540
- ```
1541
- ⊘ Stopped — it is spinning in the same place.
1542
- the same tool call keeps getting truncated
1543
- What you asked for in one go is larger than the model's output cap. Try splitting it —
1544
- e.g. "just the skeleton first" → "now add the table" → "now add the chart"
1545
- ```
1546
-
1547
- **A step limit (`maxSteps`) cannot catch this.** It cannot tell a long healthy task from a
1548
- spinning one. What is counted here is not steps but **how many times the same tool failed for
1549
- the same reason.**
1550
-
1551
- </details>
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)**
1552
580
 
1553
581
  ---
1554
582
 
@@ -1617,29 +645,90 @@ The model sees it as `mcp__wiki__search`. `/mcp` shows what is attached.
1617
645
  **Dependencies stay at zero.** The stdio transport is nothing but newline-delimited JSON-RPC
1618
646
  2.0 over a child process's stdin/stdout, so `child_process` and `JSON` cover it. No SDK.
1619
647
 
1620
- <details>
1621
- <summary><b>More</b> — But this is somebody else's program</summary>
648
+ > **More** — But this is somebody else's program
649
+ >
650
+ > **[Extending read →](docs/en/extend.md#attaching-tools-from-outside-mcp)**
1622
651
 
1623
- ### But this is somebody else's program
652
+ ---
1624
653
 
1625
- This project exists because unapproved software is blocked. Turning on MCP carelessly would
1626
- tear down that line with our own hands. So:
654
+ ## Inside your editor (ACP)
1627
655
 
1628
- | | |
656
+ A tool that makes you open one more terminal window stops being used after about two weeks.
657
+ Developers live inside the IDE. So deel speaks **ACP** (Agent Client Protocol) — Zed,
658
+ JetBrains, Neovim and Emacs attach to it **without changing a line on their side**.
659
+
660
+ One command in your editor's settings:
661
+
662
+ ```
663
+ deel acp
664
+ ```
665
+
666
+ The editor spawns that as a child process and exchanges newline-delimited JSON-RPC 2.0 over
667
+ stdio. It is not a command you type yourself.
668
+
669
+ **What you get once it is attached:**
670
+
671
+ | In the editor | From deel |
1629
672
  |---|---|
1630
- | **Off by default** | Nothing runs unless it is in `.deel/mcp.json` |
1631
- | **Never under `--offline`** | We cannot police where a child process connects. **We do not claim to block what we cannot block** |
1632
- | **Outside the working scope** | MCP servers do not honour our fence. The `/mcp` screen says so |
1633
- | **Audited** | What was launched and what was called, in `.deel/audit.jsonl` |
1634
- | **No key passthrough** | Our environment is not forwarded wholesale a `DEEL_*` gateway key in someone else's process goes somewhere we cannot see |
1635
- | **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 |
1636
- | **24 tools per server** | Schemas ship on every request. Past that they are dropped, and **the drop is reported** |
673
+ | Streaming reply pane | The model's text and its reasoning |
674
+ | Tool list with icons and status | `Read` is a read, `Edit` is an edit, `Bash` is an execution **the kind is sent**, not just a name |
675
+ | Clickable file links | The **absolute path** of every file touched |
676
+ | Approval dialog | deel's safety rails, rendered as the editor's own prompt (`allow once` · `always allow` · `reject`) |
677
+ | Mode picker | deel's seven work modes (auto · code · plan · architect · debug · ask · orchestrator) |
678
+ | Stop button | Reaches the turn mid-flight, even while waiting on the model |
1637
679
 
1638
- One server crashing, hanging, or talking nonsense does not affect the others. Failures are not
1639
- swallowed — the reason appears in the header, because a silent drop leaves "why is that tool
1640
- missing?" unanswerable.
680
+ **Still zero dependencies.** Same reason as MCP newline-delimited JSON-RPC 2.0 is the whole
681
+ transport, so no SDK is needed.
1641
682
 
1642
- </details>
683
+ > **More** — Details — the places this breaks silently
684
+ >
685
+ > **[Extending read →](docs/en/extend.md#inside-your-editor-acp)**
686
+
687
+ ---
688
+
689
+ ## Keeping secrets out of the conversation
690
+
691
+ People rarely paste a key. The leak is almost always **command output**.
692
+
693
+ ```
694
+ env OPENAI_API_KEY=sk-proj-…
695
+ git remote -v https://user:token@github.com/…
696
+ curl -v > Authorization: Bearer eyJ…
697
+ a failing test log the whole connection string
698
+ ```
699
+
700
+ That text goes to the model **and** gets written to `.deel/sessions/*.jsonl` on disk. That
701
+ file is later re-read by `/recall` and can end up inside a `deel pack` bundle. Leak once and
702
+ you have several copies.
703
+
704
+ So it is masked at the single point where tool output enters the conversation.
705
+
706
+ ```
707
+ ⏺ Bash(env | grep API) done
708
+ ⊘ 2 secret-looking values entered the conversation (openai · env var) — masked before the model
709
+ ```
710
+
711
+ What it looks for: private-key blocks · OpenAI/Anthropic keys · GitHub tokens · Slack tokens ·
712
+ AWS keys · Google keys · JWTs · credentials embedded in URLs · `Authorization`-family headers ·
713
+ env vars named `…KEY` / `…TOKEN` / `…SECRET` / `…PASSWORD`. Plus **the configured gateway key
714
+ regardless of its shape** — that one is not a guess, it is a known value.
715
+
716
+ ### File contents are deliberately not masked
717
+
718
+ `.env` is exactly where masking feels most tempting, and exactly where it backfires: the model
719
+ sees the masked text, edits it, writes it back — and `«가림»` lands where the real key was.
720
+ **Protecting the secret would destroy it.**
721
+
722
+ So on the file side it reports instead of rewriting.
723
+
724
+ ```
725
+ ⏺ Read(.env) 12 lines
726
+ ! 3 secret-looking values entered the conversation (env var)
727
+ — file contents are not masked (masking them would erase the key on write-back)
728
+ ```
729
+
730
+ Saying plainly what cannot be stopped beats claiming it was stopped while corrupting the file.
731
+ Either way it lands in the audit log.
1643
732
 
1644
733
  ---
1645
734
 
@@ -1670,78 +759,9 @@ Undo history stores whole file contents, so repeated edits to large files add up
1670
759
  it keeps the **most recent 50 turns** and drops the rest. What you just did is always
1671
760
  undoable; `/status` shows how large the history currently is.
1672
761
 
1673
- <details>
1674
- <summary><b>More</b> — Files removed through Bash come back too · What it will not read</summary>
1675
-
1676
- ### Files removed through `Bash` come back too
1677
-
1678
- The safety net covered `Write` and `Edit` only. But a model moving a file reaches for
1679
- `Bash` — `mv old.js new.js`, `rm temp.txt`. The file was gone and `/undo` could do
1680
- nothing about it. It was half a safety net.
1681
-
1682
- Now a mutating command snapshots the files it names beforehand, and **says what it saved,
1683
- right there**.
1684
-
1685
- ```
1686
- ▶ Bash(mv src/old.js src/new.js)
1687
- └ 성공
1688
- ↩ src/old.js 는 떠 뒀습니다 — /undo 로 되돌아갑니다
1689
- (saved src/old.js — /undo restores it)
1690
- ```
1691
-
1692
- `mv` and `rm` leave one "success" line on screen. From that line alone there is no way to
1693
- tell whether it is reversible, so people either assume it is and move on, or assume it is
1694
- not and get scared. So the fact is stated.
1695
-
1696
- **What cannot be saved is not hidden.** Shell-expanded wildcards (`rm *.tmp`), deletions
1697
- inside a script, and whole directories are invisible here. In those cases the `↩` line
1698
- simply does not appear — **it never claims "everything is reversible"**. False reassurance
1699
- means people stop checking.
1700
-
1701
- Snapshotting casts a **wider** net than blocking does. The scope guard (`checkPaths`) only
1702
- treats words containing a slash as paths — anything else would block legitimate commands —
1703
- but `del target.txt`, with no slash, is the most common form there is. This is a reading
1704
- site rather than a blocking one, so it scans broadly and saves a file only when one is
1705
- actually there. A wrong guess costs nothing. Up to 24 per command.
1706
-
1707
- ### What it will not read
1708
-
1709
- Walking a folder turns up things that are not project files: the private stores other coding
1710
- tools keep — past conversations, command history, caches, and keys. They have nothing to do
1711
- with the task, but once they appear in a listing the model reads them first.
1712
-
1713
- ```
1714
- ◧ Read(~/.deel/audit.jsonl) 77 lines
1715
- ◧ Read(~/.claude/history.jsonl) 35 lines
1716
- ```
1717
-
1718
- The audit log is **this program's own record of what it just did.** Reading it back into the
1719
- conversation makes the model chase its own shadow. It has nothing to do with what was asked,
1720
- and it fills the context.
1721
-
1722
- The config file is worse. `.deel/config.json` holds the gateway **API key.** Reading it puts
1723
- that key into the conversation, sends it to the model, and writes it into the on-disk session
1724
- log. It hands the key to the very service the key is for.
1725
-
1726
- | Refused | Why |
1727
- |---|---|
1728
- | `.deel/config.json` | Contains the gateway key |
1729
- | `.deel/audit.jsonl` · `.deel/sessions` · `.deel/history` | deel's own records — chasing its own shadow |
1730
- | `.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 |
1731
- | File-style leftovers like `.aider.chat.history.md` | Same reason |
1732
-
1733
- Writing is blocked too, not just reading. Blocking only reads would still let the agent
1734
- overwrite another tool's settings, and overwriting `.deel/config.json` destroys the connection.
1735
-
1736
- **It blocks, it does not hide** — the refusal says exactly why. New tools keep appearing;
1737
- when a name is missing from the list, **adding it is one line.**
1738
-
1739
- That list lives in **exactly one place in the source.** The directory walker (`SKIP_DIRS`) and
1740
- the read guard look at the same set. They used to be two copies, and two copies means the day
1741
- comes when only one of them learns a new name — a folder that is skipped while walking but
1742
- readable if you name it directly, which is very hard to explain.
1743
-
1744
- </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)**
1745
765
 
1746
766
  ---
1747
767
 
@@ -1753,51 +773,40 @@ deel pack --out deel-import.zip
1753
773
 
1754
774
  ```
1755
775
  ✓ deel-import.zip
1756
- 39 files · 100.2KB
776
+ 94 files · 509.6KB
1757
777
 
1758
778
  Dependencies 0
1759
779
  Install scripts none
1760
780
  External imports 0
1761
781
  Network calls 3 sites (configured address only)
1762
- Ports opened none
782
+ Ports opened 1 site (/preview only)
1763
783
  ```
1764
784
 
1765
- The bundled review sheet contains:
1766
-
1767
- - Dependency list and every external `import` in the source
1768
- - Presence of `preinstall` / `install` / `postinstall` / `prepare`
1769
- - **Every network and process-spawn call site found by scanning the source** (file:line)
1770
- - The three outbound lanes, explained
1771
- - SHA-256 per file (verify with `certutil -hashfile`)
1772
-
1773
- It is generated by scanning the source, not written by hand — hand-written sheets drift from reality.
1774
- Use `deel audit` to read it without building a zip.
785
+ The zip carries **one document for people and two for machines.** A corporate review is
786
+ not a human-only process — security feeds an SBOM to a scanner, and operations reads the
787
+ audit-log spec to write SIEM ingestion rules.
1775
788
 
1776
- <details>
1777
- <summary><b>More</b> — Diagnosing a corporate gateway</summary>
1778
-
1779
- ### Diagnosing a corporate gateway
789
+ | File | What |
790
+ |---|---|
791
+ | `반입심사서.txt` | Dependencies · install scripts · **every network and process-spawn call site found by scanning the source** (file:line) · the three outbound lanes · SHA-256 per file |
792
+ | `sbom.cdx.json` | **SBOM (CycloneDX 1.5).** Feed it straight to a scanner. One component per file with SHA-256; dependencies stated as an **explicit empty array** — "not declared" and "none" are different claims |
793
+ | `심사명세.json` | Egress list (per lane: when, where, what, how it's stopped, and the source location) · **audit-log spec** (field names and meanings, plus what is never recorded) · file hashes |
1780
794
 
1781
795
  ```bash
1782
- node bin/deel.js diagnose --url <gateway> --key <key> --model <model> --out report.txt
796
+ deel audit # the human-readable sheet only
797
+ deel sbom # the two machine-readable ones, on stdout (deel sbom | jq)
798
+ deel sbom --out review.json # to a file
799
+ deel sbom --only sbom # just the SBOM
1783
800
  ```
1784
801
 
1785
- Hand over `report.txt` alone plain text, no colour codes.
1786
-
1787
- | Check | Why it matters |
1788
- |---|---|
1789
- | Basic chat | Address, key and model name are right |
1790
- | System message | Rules (`DEEL.md`) and skills take effect |
1791
- | Streaming | Output can flow token by token |
1792
- | **Tool calls** | **Whether it can read and edit files — the critical one** |
1793
- | **Tool results** | **Whether multi-turn works — the premise of the agent loop** |
1794
- | Structured output | Edit format can be enforced by schema |
1795
- | Reasoning control | Whether `/think` works at the model layer |
1796
- | Context length | How many files can be read at once |
802
+ All three are generated by scanning the source, never written by hand — hand-written sheets
803
+ drift, and **a review document that drifts is worse than none.** Find one wrong line and the
804
+ reviewer stops trusting the rest. The audit-log spec is the one hand-written part, so a test
805
+ checks it against real log records on every run.
1797
806
 
1798
- Verdict is one of **ready · limited · blocked · unreachable**.
1799
-
1800
- </details>
807
+ > **More** Diagnosing a corporate gateway
808
+ >
809
+ > **[Safety and corporate review read →](docs/en/safety.md#corporate-review-package)**
1801
810
 
1802
811
  ---
1803
812
 
@@ -1805,54 +814,9 @@ Verdict is one of **ready · limited · blocked · unreachable**.
1805
814
 
1806
815
  Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder takes precedence.
1807
816
 
1808
- <details>
1809
- <summary><b>More</b> — Supported servers · Environment variables · Flags · Project rules</summary>
1810
-
1811
- ### Supported servers
1812
-
1813
- | | Example address |
1814
- |---|---|
1815
- | Corporate AI gateway (OpenAI-compatible) | `https://ai-gw.example.corp/v1` |
1816
- | Ollama | `http://localhost:11434` |
1817
- | LM Studio | `http://localhost:1234/v1` |
1818
- | llama.cpp · vLLM · LiteLLM | `http://host:port/v1` |
1819
-
1820
- Auth style is detected automatically: `Authorization: Bearer` → `x-api-key` → `api-key` (Azure) → none.
1821
-
1822
- ### Environment variables
1823
-
1824
- | Variable | Use |
1825
- |---|---|
1826
- | `DEEL_API_KEY` | Keep the key out of the config file (takes precedence) |
1827
- | `DEEL_KEY_<PROFILE_ID>` | Per-profile key |
1828
- | `NODE_EXTRA_CA_CERTS` | Corporate TLS certificate |
1829
- | `HTTPS_PROXY` | Behind a proxy |
1830
- | `DEEL_DEBUG=1` | Verbose errors |
1831
- | `NO_COLOR` | Disable colour |
1832
-
1833
- ### Flags
1834
-
1835
- ```bash
1836
- deel --root <folder> Working scope. Defaults to the current folder
1837
- deel --mode <mode> auto (default) / confirm / strict
1838
- deel --work <mode> auto (default) / code / plan / architect / debug / ask / orchestrator
1839
- deel --level <level> 쉬움 (simple) / 개발자 (developer)
1840
- deel --ctx <length> Set the context length yourself (655360 · 640k · 128k)
1841
- deel --max-tokens <len> Cap on a single reply (32k) — same value as /out
1842
- deel --think <level> off / low / medium (default) / high / max
1843
- deel --effort <profile> even / save (default) / deep
1844
- deel --offline Nothing leaves this machine
1845
- deel --continue Resume the most recent conversation
1846
- deel --resume <id> Resume a specific one
1847
- deel --no-tui Turn the input box off; plain scrolling view (see below)
1848
- ```
1849
-
1850
- ### Project rules
1851
-
1852
- If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as project rules.
1853
- `/init` scaffolds one.
1854
-
1855
- </details>
817
+ > **More** — Supported servers · Environment variables · Flags · Project rules
818
+ >
819
+ > **[Configuration read →](docs/en/config.md#configuration)**
1856
820
 
1857
821
  ---
1858
822
 
@@ -1919,221 +883,21 @@ so one run tells you everything.
1919
883
  | `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
1920
884
  | `edit-bench` | 20 cases | Edit success rate |
1921
885
 
1922
- <details>
1923
- <summary><b>More</b> — Coverage · Layout</summary>
1924
-
1925
- ### Coverage
1926
-
1927
- ```bash
1928
- npm run coverage Summary
1929
- node test/coverage.mjs --file src/repl.js One file in detail
1930
- node test/coverage.mjs --json Machine-readable
1931
- ```
1932
-
1933
- Zero dependencies rules out c8 and nyc, so this reads Node's own
1934
- `NODE_V8_COVERAGE` instead — nothing new to get through an import review. It picks up
1935
- child processes too, so the `cli` suite that spawns `deel` counts like everything else.
1936
-
1937
- Currently **92% overall** (7,056 of 7,646 lines). Three files are deliberately left short.
1938
-
1939
- | File | Now | Why it stops there |
1940
- |---|---|---|
1941
- | `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 |
1942
- | `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) |
1943
- | `plugins/manage.js` | 79% | The GitHub download path. **Tests not reaching the network** matters more. Folder installs are covered |
1944
-
1945
- ### Layout
1946
-
1947
- ```
1948
- bin/deel.js entry point
1949
- src/
1950
- repl.js the conversation screen — what a person faces
1951
- oneshot.js run once and exit (-p)
1952
- commands.js 35 slash commands
1953
- setup.js first-run connection setup
1954
- config.js reading and writing config
1955
-
1956
- ui/ansi.js colour · East Asian width
1957
- ui/screen.js picking a screen (line mode / box mode)
1958
- ui/inputbox.js the box at the bottom — overwrite-in-place, cursor position
1959
- ui/status.js status line — model, context, mode, approvals
1960
- ui/working.js working phrases — they follow what is happening
1961
- ui/motion.js the braille drawing next to the phrase
1962
- ui/approve.js approval mode display (auto / risky only / everything)
1963
- ui/diff.js showing what changed, where it changed
1964
- ui/wrap.js wrapping to width without breaking colour
1965
- ui/level.js simple vs developer
1966
-
1967
- agent/loop.js the agent loop
1968
- agent/session.js conversation state + context accounting
1969
- agent/modes.js work modes (auto · code · plan · architect · debug · ask · orchestrator)
1970
- agent/route.js picking the mode from what was said
1971
- agent/effort.js per-stage reasoning effort
1972
- agent/budget.js shares that follow the window — lines read, description length, steps
1973
- agent/project.js working out what kind of project this folder is
1974
- agent/compact.js summarising compaction
1975
- agent/store.js saving and resuming conversations
1976
- agent/recall.js searching past conversations (no index, within budget)
1977
- agent/memory.js what outlives the conversation
1978
- agent/mention.js attaching files with `@`
1979
-
1980
- backend/http.js the single HTTP layer (the only door out)
1981
- backend/detect.js protocol and auth detection
1982
- backend/adapter.js absorbing OpenAI/Ollama differences + streaming parser
1983
- backend/ctxsize.js reading context length off the model
1984
- backend/probe.js 8 diagnostic checks
1985
- backend/scan.js scanning for local servers
1986
- backend/mcp.js attaching outside tools (MCP, stdio)
1987
-
1988
- tools/index.js 15 tools
1989
- tools/edit-match.js staged-relaxation edit matching
1990
- tools/outline.js a file's shape, cheaply
1991
- tools/verify.js checking what was built
1992
- tools/task.js splitting big work off
1993
- tools/jobs.js commands that run in the background
1994
- tools/todo.js checklists
1995
- tools/webfetch.js reading the web (read-only)
1996
- tools/encoding.js writing back in the encoding it was read in
1997
- tools/xlsx.js Excel → CSV (written here)
1998
-
1999
- preview/serve.js serving what you built (127.0.0.1 only)
2000
- skills/discover.js finding skills, commands and plugins on the machine
2001
- plugins/manage.js installing, removing and packing plugins
2002
- pack/zip.js ZIP writing (written here, keeps non-ASCII names)
2003
- pack/tar.js TAR reading (written here)
2004
- pack/selfpack.js review dossier + source bundle
2005
-
2006
- safety/network.js the lock on the way out
2007
- safety/guard.js working scope + dangerous-command blocking
2008
- safety/undo.js snapshots and undo
2009
- safety/audit.js recording what happened, and when
2010
- test/ tests (excluded from the published package)
2011
- ```
2012
-
2013
- </details>
886
+ > **More** — Coverage · Layout
887
+ >
888
+ > **[Development read →](docs/en/develop.md#development)**
2014
889
 
2015
890
  ---
2016
891
 
2017
892
  ## Release notes
2018
893
 
2019
- <details>
2020
- <summary><b>▸ 1.2.0 — so the conversation doesn't break</b> · what changed in six places</summary>
2021
-
2022
- <br>
2023
-
2024
- | | Before | After |
2025
- |---|---|---|
2026
- | Resuming | a conversation cut mid-tool-call **would not reopen** | unmatched calls are cleared, then it opens |
2027
- | Counting tokens | it guessed, and stayed wrong | it corrects itself against the server |
2028
- | Making room | summarising arrived at turn 49 | it holds out to turn **102** |
2029
- | Side questions | piled up in the main context | live in their own thread |
2030
- | Yesterday's lesson | vanished when you quit | carries over to the next session |
2031
- | The answer on screen | `**bold**` showed up as characters | it is drawn |
2032
-
2033
- <br>
2034
-
2035
- #### 1. A conversation cut mid-tool-call would not reopen
2036
-
2037
- 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.
2038
-
2039
- Now the pairs are checked before it opens. Calls with no result go, results with no parent go, and **whatever the model said stays.**
2040
-
2041
- ```
2042
- $ deel --resume 20260826-140217
2043
-
2044
- ✓ 20260826-140217 — 메시지 48개를 이어 받았습니다.
2045
- 중단된 도구 호출 2개를 걷어냈습니다 — 그때 하던 일은 다시 시켜 주세요.
2046
- ```
2047
-
2048
- #### 2. It misjudged the room left
2049
-
2050
- 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.
2051
-
2052
- It now **corrects the multiplier against what the server actually reports** with every answer. One line at the foot of `/context`:
2053
-
2054
- ```
2055
- 서버가 알려 준 실제값에 맞춰 +12% 보정했습니다 (7번 재봄).
2056
- ```
2057
-
2058
- The multiplier is kept per model, so **the next session starts from it** instead of measuring again from scratch.
2059
-
2060
- #### 3. Summarising arrived too early
2061
-
2062
- 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.
2063
-
2064
- 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.
2065
-
2066
- ```
2067
- ◲ 오래된 도구 결과 6개를 접었습니다 (2,148 토큰을 비움)
2068
- ```
2069
-
2070
- What was there is left in its place:
2071
-
2072
- ```
2073
- (접힘) Read(src/runner.js) — 61줄. 자리를 비우려고 내용을 접었습니다. 필요하면 다시 읽으세요.
2074
- ```
2075
-
2076
- Measured by streaming the same conversation through: summarising is pushed from **turn 49 to turn 102 — 2.1×**.
2077
-
2078
- #### 4. Side questions polluted the main line
2079
-
2080
- "Just check this one thing" piles into the main context and stays there long after the checking is done.
2081
-
2082
- ```
2083
- /thread new 로그확인
2084
- ⑂ 로그확인 갈래로 왔습니다. 빈 대화입니다
2085
- 본줄기로 돌아가려면 /thread 1
2086
- ```
2087
-
2088
- `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.
2089
-
2090
- #### 5. Yesterday's lesson vanished when you quit
2091
-
2092
- 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.
2093
-
2094
- ```
2095
- /learned
2096
- ── 겪어 본 것 ──────────────────────────────────────
2097
-
2098
- 이 폴더에서 돌려 본 명령
2099
- ✓ npm test 됨 12 · 안 됨 0
2100
- ✗ pnpm 됨 0 · 안 됨 3
2101
-
2102
- 이 모델에 대해 qwen2.5-coder:7b
2103
- 같이 걸어 본 걸음 86
2104
- 인자가 잘림 14 (16%)
2105
- 토큰 추정 보정 ×1.12
2106
-
2107
- 이 중 프롬프트에 실리는 것
2108
- - 여기서 되는 명령: `npm test`
2109
- - 이 PC 에서 안 되는 명령(다시 부르지 마라): `pnpm`
2110
- ```
2111
-
2112
- **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.
2113
-
2114
- 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.
2115
-
2116
- #### 6. The answer showed up as raw characters
2117
-
2118
- 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.
2119
-
2120
- ```
2121
- before after
2122
- ▌ ## 고친 것 ▌ ▍ 고친 것
2123
- ▌ **src/runner.js** 의 `console.log` ▌ src/runner.js 의 console.log
2124
- ▌ - [ ] 남은 것: `src/worker.js` ▌ ☐ 남은 것: src/worker.js
2125
- ▌ ```js ▌ ┌──────────────── js
2126
- ▌ log.info('시작', { id }) ▌ │ log.info('시작', { id })
2127
- ▌ |---|---| ▌ ┼────────┼─────────┼
2128
- ```
2129
-
2130
- 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.
2131
-
2132
- <br>
2133
-
2134
- Tests 2,532 → **2,578** · 48/48 files. Earlier releases are in the [tags](https://github.com/jysvai/deel-local-cli/tags).
894
+ | Version | What changed |
895
+ |---|---|
896
+ | **[1.4.0](docs/en/releases.md#140)** | deel gets a face, speaks English, and sees meaning — eleven places |
897
+ | [1.3.0](docs/en/releases.md#130) | Evidence instead of claims, the editor instead of a terminal — six places |
898
+ | [1.2.0](docs/en/releases.md#120) | So the conversation doesn't break — six places |
2135
899
 
2136
- </details>
900
+ What changed and why is in the **[release notes](docs/en/releases.md)**.
2137
901
 
2138
902
  ---
2139
903