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