deel-local-cli 1.1.1 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.en.md +826 -19
  2. package/README.md +689 -14
  3. package/bin/deel.js +73 -4
  4. package/package.json +7 -3
  5. package/src/acp/jsonrpc.js +230 -0
  6. package/src/acp/map.js +219 -0
  7. package/src/acp/serve.js +556 -0
  8. package/src/agent/budget.js +0 -14
  9. package/src/agent/card.js +110 -0
  10. package/src/agent/compact.js +95 -0
  11. package/src/agent/evidence.js +186 -0
  12. package/src/agent/evolve.js +213 -0
  13. package/src/agent/grade.js +20 -0
  14. package/src/agent/loop.js +172 -14
  15. package/src/agent/models.js +169 -0
  16. package/src/agent/modes.js +49 -1
  17. package/src/agent/pins.js +140 -0
  18. package/src/agent/project.js +10 -4
  19. package/src/agent/session.js +359 -11
  20. package/src/agent/store.js +31 -0
  21. package/src/agent/threads.js +154 -0
  22. package/src/commands.js +751 -55
  23. package/src/config.js +10 -1
  24. package/src/i18n/en.js +244 -0
  25. package/src/i18n/index.js +126 -0
  26. package/src/i18n/ko.js +231 -0
  27. package/src/lsp/client.js +459 -0
  28. package/src/lsp/diag.js +112 -0
  29. package/src/lsp/rpc.js +84 -0
  30. package/src/lsp/servers.js +218 -0
  31. package/src/oneshot.js +23 -0
  32. package/src/pack/sbom.js +218 -0
  33. package/src/pack/selfpack.js +19 -3
  34. package/src/repl.js +273 -22
  35. package/src/safety/guard.js +37 -2
  36. package/src/safety/secrets.js +205 -0
  37. package/src/safety/undo.js +11 -4
  38. package/src/setup.js +2 -2
  39. package/src/skills/discover.js +1 -1
  40. package/src/tools/desc.en.js +219 -0
  41. package/src/tools/edit-match.js +16 -5
  42. package/src/tools/excel-com.js +1 -1
  43. package/src/tools/excel.js +1 -1
  44. package/src/tools/fsutil.js +0 -8
  45. package/src/tools/index.js +149 -6
  46. package/src/tools/lsp.js +327 -0
  47. package/src/tools/task.js +30 -2
  48. package/src/tools/todo.js +19 -3
  49. package/src/ui/ansi.js +42 -2
  50. package/src/ui/approve.js +25 -21
  51. package/src/ui/inputbox.js +69 -13
  52. package/src/ui/intro.js +174 -0
  53. package/src/ui/level.js +11 -5
  54. package/src/ui/md.js +227 -0
  55. package/src/ui/notify.js +101 -0
  56. package/src/ui/prompt.js +1 -1
  57. package/src/ui/screen.js +1 -1
  58. package/src/ui/status.js +190 -34
  59. package/src/ui/working.js +0 -3
  60. package/src/ui/wrap.js +1 -8
package/README.en.md CHANGED
@@ -53,16 +53,22 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
53
53
  - [Simple vs developer](#simple-vs-developer)
54
54
  - [Tools](#tools)
55
55
  - [Korean text and Excel](#korean-text-and-excel)
56
+ - [Serving what you built](#serving-what-you-built)
56
57
  - [Skills and plugins](#skills-and-plugins)
57
58
  - [Reasoning effort](#reasoning-effort)
58
59
  - [Auto-compaction](#auto-compaction)
59
60
  - [Resuming a conversation](#resuming-a-conversation)
60
61
  - [Attaching tools from outside (MCP)](#attaching-tools-from-outside-mcp)
62
+ - [Inside your editor (ACP)](#inside-your-editor-acp)
63
+ - [Keeping secrets out of the conversation](#keeping-secrets-out-of-the-conversation)
61
64
  - [Safety](#safety)
62
65
  - [Corporate review package](#corporate-review-package)
63
66
  - [Configuration](#configuration)
64
67
  - [Troubleshooting](#troubleshooting)
65
68
  - [Development](#development)
69
+ - [Release notes](#release-notes)
70
+
71
+ Each section is open at the summary. Click **▸ More** to unfold the detail behind it.
66
72
 
67
73
  ---
68
74
 
@@ -93,6 +99,29 @@ deel audit # full review sheet
93
99
 
94
100
  ## Quick start
95
101
 
102
+ ### The screen speaks English too
103
+
104
+ deel is written in Korean — the code, the function names, the comments. That part stays.
105
+ What you see on screen does not have to.
106
+
107
+ ```bash
108
+ DEEL_LANG=en deel # this run only
109
+ /lang en # and remember it
110
+ /lang # how much is translated so far
111
+ ```
112
+
113
+ Untranslated lines come through in Korean rather than as blanks, and `/lang` tells you exactly
114
+ how many are left.
115
+
116
+ What the model reads follows the same switch. Set it to English and the rules, the mode
117
+ instructions, and the tool descriptions all go out in English — so the model answers you in
118
+ English instead of Korean. That side is cheaper, too: the part of the window that ships on every
119
+ single request drops from about 4,900 tokens to about 3,450 — on a 32k model, from 15% of the
120
+ window to 10.5%.
121
+
122
+ Tool names and argument names stay Korean (`목적`, `할일`, `번호`). Those are identifiers, not
123
+ prose — rename them and the tool stops being called at all.
124
+
96
125
  ### Install
97
126
 
98
127
  ```bash
@@ -203,6 +232,9 @@ $ deel scan
203
232
 
204
233
  Switch with `/model` mid-conversation — **the conversation carries over.**
205
234
 
235
+ <details>
236
+ <summary><b>More</b> — It adapts to whatever model is attached · Small windows get a smaller fixed share · On startup it reads what kind of project this folder is</summary>
237
+
206
238
  ### It adapts to whatever model is attached
207
239
 
208
240
  If you move between models, any number tuned for one of them is wrong for all the others.
@@ -333,6 +365,8 @@ can be recovered with one `Glob`; "tests run with `npm test`" requires opening
333
365
 
334
366
  7ms on a folder with 600 files (`test/project.test.js`).
335
367
 
368
+ </details>
369
+
336
370
  ---
337
371
 
338
372
  ## Slash commands
@@ -342,13 +376,22 @@ Names follow Claude Code / Codex conventions.
342
376
  | Command | What it does |
343
377
  |---|---|
344
378
  | `/help` | Command list |
379
+ | `/lang [ko\|en]` | Screen language. Falls back to Korean for anything not translated yet |
380
+ | `/bell [on\|off]` | Ring and set the window title when a turn ends, or when deel needs an answer |
381
+ | `/consult <profile> <question>` | Ask a second model one question. Your current model stays put |
382
+ | `/lsp [on\|off]` | Language servers — what is installed, and whether `Def`/`Refs` are available. `off` turns post-edit diagnostics off only |
345
383
  | `/context` | What is consuming the context window |
346
384
  | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
347
385
  | `/grade [small\|medium\|large\|auto]` | Model **grade** — how much it does on its own. A different axis from `/ctx` |
348
386
  | `/out [number\|auto]` | Cap on a **single reply** — raise it when large files get cut |
349
387
  | `/compact` | Summarise and fold older turns |
350
388
  | `/clear` | Clear the conversation (keeps link and rules) |
389
+ | `/thread [new\|fork\|close\|n]` | Conversation threads — side work in its own context. Link and undo stay shared |
390
+ | `/learned [clear]` | What deel has picked up on its own — commands that work here, this model's habits |
391
+ | `/pin <text>` | Pin a line — folding and compaction **cannot reach it** |
392
+ | `/evidence [file]` | Evidence — what changed, and what proves it. **What is unproven is listed too** |
351
393
  | `/model` | Switch connection / model |
394
+ | `/model 카드` | Model card — what this model has actually done here, and what deel changed because of it |
352
395
  | `/think <level>` | Reasoning level (`off·low·medium·high·max`) |
353
396
  | `/think 배분 <profile>` | Per-stage profile (`even·save·deep`) |
354
397
  | `/think 자세히` | Stage table — which stage runs at which level and cap |
@@ -359,6 +402,7 @@ Names follow Claude Code / Codex conventions.
359
402
  | `/level [level]` | How much to show (`쉬움` simple · `개발자` developer) |
360
403
  | `/undo [turns]` | Revert file changes |
361
404
  | `/diff [file]` | Files changed this session, and the changed lines |
405
+ | `/preview [folder\|file\|off]` | Serve what you built, right here — a browser opens with it |
362
406
  | `/tools` | Available tools |
363
407
  | `/skills [query\|all\|off]` | Browse, search, load skills |
364
408
  | `/plugin [install\|remove\|pack]` | Manage plugins |
@@ -390,6 +434,9 @@ the conversation.
390
434
 
391
435
  Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
392
436
 
437
+ <details>
438
+ <summary><b>More</b> — Attaching a file with @ · Interrupting</summary>
439
+
393
440
  ### Attaching a file with `@`
394
441
 
395
442
  Write `@` followed by a path and that file is sent along with your message.
@@ -448,6 +495,8 @@ session. Tools already running finish; **tools not yet started never run.**
448
495
 
449
496
  Pressing Ctrl+C again on an empty line quits.
450
497
 
498
+ </details>
499
+
451
500
  ---
452
501
 
453
502
  ## Work modes
@@ -476,6 +525,9 @@ Don't confuse this with `/mode`. They are separate axes:
476
525
  If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
477
526
  overrides something a person chose.
478
527
 
528
+ <details>
529
+ <summary><b>More</b> — Switching by itself (Auto mode)</summary>
530
+
479
531
  ### Switching by itself (Auto mode)
480
532
 
481
533
  You start in **Auto**. Nothing has been decided about what kind of work is coming.
@@ -518,6 +570,8 @@ A `~` in the status line means it switched by itself; no `~` means you chose it.
518
570
 
519
571
  Choosing a mode yourself **pins** it. `/auto` (or `/work auto`) hands the wheel back.
520
572
 
573
+ </details>
574
+
521
575
  ---
522
576
 
523
577
  ## Simple vs developer
@@ -539,6 +593,9 @@ Two things matter here:
539
593
  - **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
540
594
  blocking are identical. A beginner needs the undo more, not less.
541
595
 
596
+ <details>
597
+ <summary><b>More</b> — The input box · You don't have to type the whole command · The box stays while it works · The picture on the left moves too and 1 more</summary>
598
+
542
599
  ### The input box
543
600
 
544
601
  Launched in a terminal, **the conversation scrolls normally and an input box is pinned at
@@ -652,21 +709,46 @@ more than a blank screen. One turn reads like this:
652
709
  (turning it over) (looking at files) (writing code) (writing the answer)
653
710
  ```
654
711
 
655
- | Activity | Phrases |
656
- |---|---|
657
- | Thinking | 머리 굴리는 · 어떻게 할지 궁리하는 중 · 수 읽는 중 · 따져 보는 중 |
658
- | `Read` `Grep` `Glob` | 파일 들여다보는 중 · 코드 훑는 중 · 어디 있나 뒤지는 중 · 단서 찾는 중 |
659
- | `Write` `Edit` `Append` | 코드 짜는 중 · 고쳐 넣는 중 · 손보는 중 · 줄씩 옮기는 |
660
- | `Bash` | 명령 돌리는 중 · 터미널 두드리는 중 · 결과 기다리는 중 |
661
- | `WebFetch` | 문서 찾아보는 중 · 읽어 오는 중 |
662
- | Answering | 쓰는 중 · 정리해서 말하는 중 |
663
- | **Past 45 seconds** | 아직 하는 중 · 조금만 · 생각보다 오래 걸리는 |
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 |
664
723
 
665
724
  Within a category the phrase advances every 4 seconds — text frozen for 30 seconds reads as
666
725
  hung too. On the right: **elapsed time**, and while the model is reasoning, **how many
667
726
  characters of thinking have arrived**. One number that genuinely increases is what turns
668
727
  "still alive" from a claim into a fact.
669
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
+
670
752
  ### What gets asked, and what just happens
671
753
 
672
754
  Whether your files change **with or without being asked** is the one thing that has to be
@@ -706,6 +788,8 @@ press only makes it ask more; it never drops you into "changes files unasked" in
706
788
  > approval indicator off the line entirely. Now **the model name shortens first** — you
707
789
  > already know what you are running; whether your files change unasked is what you need now.
708
790
 
791
+ </details>
792
+
709
793
  ---
710
794
 
711
795
  ## Tools
@@ -730,12 +814,17 @@ Names and arguments match Claude Code, so skills written for that convention wor
730
814
  | `Verify` | Check that what was built **actually works** |
731
815
  | `Task` | Run one chunk of a big job in a **separate context** |
732
816
  | `Jobs` | Inspect, read and stop **background commands** — the other half of `Bash`'s `background` |
817
+ | `Def` | **Where a name is defined** — only shown when a language server is installed |
818
+ | `Refs` | **Every place a name is used** — only shown when a language server is installed |
733
819
 
734
820
  Seven tools here are not in Claude Code — `Append`, `Recall`, `Remember`, `Outline`,
735
821
  `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
736
822
  so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
737
823
  their cost; here is why.
738
824
 
825
+ <details>
826
+ <summary><b>More</b> — Outline · Verify · Task · Def · Refs · Commands that never finish and 9 more</summary>
827
+
739
828
  ### Seeing a project's shape cheaply — `Outline`
740
829
 
741
830
  There used to be only two ways to understand someone else's code. `Glob` gives you paths;
@@ -1126,10 +1215,125 @@ last edit did. That original state comes from the earliest undo snapshot.
1126
1215
  `/diff` is **in the simple level's command list.** As long as `auto` edits without asking,
1127
1216
  a beginner needs a way to see what changed more than anyone.
1128
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
+
1129
1327
  ---
1130
1328
 
1131
1329
  ## Korean text and Excel
1132
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
+
1133
1337
  ### Encoding — written back the way it was read
1134
1338
 
1135
1339
  Corporate documents are often not UTF-8. Files saved by old Windows Notepad in a legacy
@@ -1200,6 +1404,55 @@ Extracted intermediate files are deleted after use.
1200
1404
  > to do instead. Round-tripping a file with formatting, formulas and charts through CSV
1201
1405
  > always loses something. Better not to write than to write knowing you'll lose data.
1202
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
+
1203
1456
  ---
1204
1457
 
1205
1458
  ## Skills and plugins
@@ -1215,6 +1468,9 @@ plugins ~/.claude/plugins/** ~/.deel/plugins/**
1215
1468
 
1216
1469
  Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
1217
1470
 
1471
+ <details>
1472
+ <summary><b>More</b> — Loaded in three stages · Fetching plugins · Deliberately not included</summary>
1473
+
1218
1474
  ### Loaded in three stages
1219
1475
 
1220
1476
  Loading everything would blow the context window.
@@ -1247,6 +1503,8 @@ with a licence table — ready to hand to a security reviewer.
1247
1503
  | sub-agents | Doubles model calls against a gateway quota |
1248
1504
  | MCP | A separate protocol; a project of its own |
1249
1505
 
1506
+ </details>
1507
+
1250
1508
  ---
1251
1509
 
1252
1510
  ## Reasoning effort
@@ -1295,6 +1553,9 @@ That second-to-last line exists for a reason: **when all three caps are equal, i
1295
1553
  only thing that says whether that is correct.** A low known cap makes them equal, and that
1296
1554
  is fine. For a while all three read `16,384` always — which meant the table said nothing.
1297
1555
 
1556
+ <details>
1557
+ <summary><b>More</b> — Context length is read off the model · /out · Truncated tool calls</summary>
1558
+
1298
1559
  ### Context length is read off the model
1299
1560
 
1300
1561
  This one number sizes the whole program: how many files fit in one read, when the
@@ -1428,6 +1689,8 @@ One silently swallowed value produced all of that. What happens now:
1428
1689
  spinning one. What is counted here is not steps but **how many times the same tool failed for
1429
1690
  the same reason.**
1430
1691
 
1692
+ </details>
1693
+
1431
1694
  ---
1432
1695
 
1433
1696
  ## Auto-compaction
@@ -1495,6 +1758,9 @@ The model sees it as `mcp__wiki__search`. `/mcp` shows what is attached.
1495
1758
  **Dependencies stay at zero.** The stdio transport is nothing but newline-delimited JSON-RPC
1496
1759
  2.0 over a child process's stdin/stdout, so `child_process` and `JSON` cover it. No SDK.
1497
1760
 
1761
+ <details>
1762
+ <summary><b>More</b> — But this is somebody else's program</summary>
1763
+
1498
1764
  ### But this is somebody else's program
1499
1765
 
1500
1766
  This project exists because unapproved software is blocked. Turning on MCP carelessly would
@@ -1514,6 +1780,110 @@ One server crashing, hanging, or talking nonsense does not affect the others. Fa
1514
1780
  swallowed — the reason appears in the header, because a silent drop leaves "why is that tool
1515
1781
  missing?" unanswerable.
1516
1782
 
1783
+ </details>
1784
+
1785
+ ---
1786
+
1787
+ ## Inside your editor (ACP)
1788
+
1789
+ A tool that makes you open one more terminal window stops being used after about two weeks.
1790
+ Developers live inside the IDE. So deel speaks **ACP** (Agent Client Protocol) — Zed,
1791
+ JetBrains, Neovim and Emacs attach to it **without changing a line on their side**.
1792
+
1793
+ One command in your editor's settings:
1794
+
1795
+ ```
1796
+ deel acp
1797
+ ```
1798
+
1799
+ The editor spawns that as a child process and exchanges newline-delimited JSON-RPC 2.0 over
1800
+ stdio. It is not a command you type yourself.
1801
+
1802
+ **What you get once it is attached:**
1803
+
1804
+ | In the editor | From deel |
1805
+ |---|---|
1806
+ | Streaming reply pane | The model's text and its reasoning |
1807
+ | Tool list with icons and status | `Read` is a read, `Edit` is an edit, `Bash` is an execution — **the kind is sent**, not just a name |
1808
+ | Clickable file links | The **absolute path** of every file touched |
1809
+ | Approval dialog | deel's safety rails, rendered as the editor's own prompt (`allow once` · `always allow` · `reject`) |
1810
+ | Mode picker | deel's seven work modes (auto · code · plan · architect · debug · ask · orchestrator) |
1811
+ | Stop button | Reaches the turn mid-flight, even while waiting on the model |
1812
+
1813
+ **Still zero dependencies.** Same reason as MCP — newline-delimited JSON-RPC 2.0 is the whole
1814
+ transport, so no SDK is needed.
1815
+
1816
+ <details>
1817
+ <summary><b>Details</b> — the places this breaks silently</summary>
1818
+
1819
+ This protocol fails quietly. The editor shows "the agent is not responding" and nothing
1820
+ anywhere explains why. So these are nailed down by tests (`test/acp.test.js` spawns a real
1821
+ process and talks over a real pipe).
1822
+
1823
+ | The place | Why it matters |
1824
+ |---|---|
1825
+ | **Nothing but ACP on stdout** | The spec says `MUST NOT`. deel has dozens of places that print to the screen; one of them firing in this mode breaks the pipe. Rather than guarding each call site, **the pipe itself is swapped out** — so code written later is safe without knowing about this. What gets printed is not dropped, it goes to stderr |
1826
+ | **Korean split across chunk boundaries** | Pipes break on bytes, not characters. Decoding each chunk separately turns `안녕` into `안<?>하` — and **the JSON still parses**, so no error is raised. The characters are quietly mangled |
1827
+ | **A request with `id: 0`** | ACP clients count from zero. Reading `if (msg.id)` treats the very first `initialize` as a notification and never answers — it hangs the moment it connects |
1828
+ | **Cancellation reaching a running turn** | Cancellation always arrives while something is running; that is what cancellation is. Awaiting each incoming line in order means it **never arrives** |
1829
+ | **When permission cannot be asked** | It is tempting to just run the tool — otherwise nothing works against a client that has not built the approval dialog yet. But that means "if I can't ask, I do as I please". **It does not** |
1830
+
1831
+ **What it does not do yet, stated plainly:**
1832
+
1833
+ | | |
1834
+ |---|---|
1835
+ | `session/load` | Restoring a past conversation means replaying every message as an update. Half-built, the editor opens an empty conversation and the user assumes the history is gone. It reports **`loadSession: false`** |
1836
+ | Image / audio attachments | Most local models cannot read them. Rather than dropping them silently, deel tells the model what it could not read |
1837
+ | MCP servers passed in by the editor | Not launched. That would mean **deel spawning processes named in the editor's config**. "What does this tool launch?" is the first question in a corporate review, and "whatever the editor says" is not an acceptable answer. Only `.deel/mcp.json`, written by a person, is launched |
1838
+
1839
+ </details>
1840
+
1841
+ ---
1842
+
1843
+ ## Keeping secrets out of the conversation
1844
+
1845
+ People rarely paste a key. The leak is almost always **command output**.
1846
+
1847
+ ```
1848
+ env OPENAI_API_KEY=sk-proj-…
1849
+ git remote -v https://user:token@github.com/…
1850
+ curl -v > Authorization: Bearer eyJ…
1851
+ a failing test log the whole connection string
1852
+ ```
1853
+
1854
+ That text goes to the model **and** gets written to `.deel/sessions/*.jsonl` on disk. That
1855
+ file is later re-read by `/recall` and can end up inside a `deel pack` bundle. Leak once and
1856
+ you have several copies.
1857
+
1858
+ So it is masked at the single point where tool output enters the conversation.
1859
+
1860
+ ```
1861
+ ⏺ Bash(env | grep API) done
1862
+ ⊘ 2 secret-looking values entered the conversation (openai · env var) — masked before the model
1863
+ ```
1864
+
1865
+ What it looks for: private-key blocks · OpenAI/Anthropic keys · GitHub tokens · Slack tokens ·
1866
+ AWS keys · Google keys · JWTs · credentials embedded in URLs · `Authorization`-family headers ·
1867
+ env vars named `…KEY` / `…TOKEN` / `…SECRET` / `…PASSWORD`. Plus **the configured gateway key
1868
+ regardless of its shape** — that one is not a guess, it is a known value.
1869
+
1870
+ ### File contents are deliberately not masked
1871
+
1872
+ `.env` is exactly where masking feels most tempting, and exactly where it backfires: the model
1873
+ sees the masked text, edits it, writes it back — and `«가림»` lands where the real key was.
1874
+ **Protecting the secret would destroy it.**
1875
+
1876
+ So on the file side it reports instead of rewriting.
1877
+
1878
+ ```
1879
+ ⏺ Read(.env) 12 lines
1880
+ ! 3 secret-looking values entered the conversation (env var)
1881
+ — file contents are not masked (masking them would erase the key on write-back)
1882
+ ```
1883
+
1884
+ Saying plainly what cannot be stopped beats claiming it was stopped while corrupting the file.
1885
+ Either way it lands in the audit log.
1886
+
1517
1887
  ---
1518
1888
 
1519
1889
  ## Safety
@@ -1543,6 +1913,9 @@ Undo history stores whole file contents, so repeated edits to large files add up
1543
1913
  it keeps the **most recent 50 turns** and drops the rest. What you just did is always
1544
1914
  undoable; `/status` shows how large the history currently is.
1545
1915
 
1916
+ <details>
1917
+ <summary><b>More</b> — Files removed through Bash come back too · What it will not read</summary>
1918
+
1546
1919
  ### Files removed through `Bash` come back too
1547
1920
 
1548
1921
  The safety net covered `Write` and `Edit` only. But a model moving a file reaches for
@@ -1611,6 +1984,8 @@ the read guard look at the same set. They used to be two copies, and two copies
1611
1984
  comes when only one of them learns a new name — a folder that is skipped while walking but
1612
1985
  readable if you name it directly, which is very hard to explain.
1613
1986
 
1987
+ </details>
1988
+
1614
1989
  ---
1615
1990
 
1616
1991
  ## Corporate review package
@@ -1621,25 +1996,39 @@ deel pack --out deel-import.zip
1621
1996
 
1622
1997
  ```
1623
1998
  ✓ deel-import.zip
1624
- 39 files · 100.2KB
1999
+ 94 files · 509.6KB
1625
2000
 
1626
2001
  Dependencies 0
1627
2002
  Install scripts none
1628
2003
  External imports 0
1629
2004
  Network calls 3 sites (configured address only)
1630
- Ports opened none
2005
+ Ports opened 1 site (/preview only)
1631
2006
  ```
1632
2007
 
1633
- The bundled review sheet contains:
2008
+ The zip carries **one document for people and two for machines.** A corporate review is
2009
+ not a human-only process — security feeds an SBOM to a scanner, and operations reads the
2010
+ audit-log spec to write SIEM ingestion rules.
2011
+
2012
+ | File | What |
2013
+ |---|---|
2014
+ | `반입심사서.txt` | Dependencies · install scripts · **every network and process-spawn call site found by scanning the source** (file:line) · the three outbound lanes · SHA-256 per file |
2015
+ | `sbom.cdx.json` | **SBOM (CycloneDX 1.5).** Feed it straight to a scanner. One component per file with SHA-256; dependencies stated as an **explicit empty array** — "not declared" and "none" are different claims |
2016
+ | `심사명세.json` | Egress list (per lane: when, where, what, how it's stopped, and the source location) · **audit-log spec** (field names and meanings, plus what is never recorded) · file hashes |
2017
+
2018
+ ```bash
2019
+ deel audit # the human-readable sheet only
2020
+ deel sbom # the two machine-readable ones, on stdout (deel sbom | jq)
2021
+ deel sbom --out review.json # to a file
2022
+ deel sbom --only sbom # just the SBOM
2023
+ ```
1634
2024
 
1635
- - Dependency list and every external `import` in the source
1636
- - Presence of `preinstall` / `install` / `postinstall` / `prepare`
1637
- - **Every network and process-spawn call site found by scanning the source** (file:line)
1638
- - The three outbound lanes, explained
1639
- - SHA-256 per file (verify with `certutil -hashfile`)
2025
+ All three are generated by scanning the source, never written by hand — hand-written sheets
2026
+ drift, and **a review document that drifts is worse than none.** Find one wrong line and the
2027
+ reviewer stops trusting the rest. The audit-log spec is the one hand-written part, so a test
2028
+ checks it against real log records on every run.
1640
2029
 
1641
- It is generated by scanning the source, not written by hand — hand-written sheets drift from reality.
1642
- Use `deel audit` to read it without building a zip.
2030
+ <details>
2031
+ <summary><b>More</b> Diagnosing a corporate gateway</summary>
1643
2032
 
1644
2033
  ### Diagnosing a corporate gateway
1645
2034
 
@@ -1662,12 +2051,17 @@ Hand over `report.txt` alone — plain text, no colour codes.
1662
2051
 
1663
2052
  Verdict is one of **ready · limited · blocked · unreachable**.
1664
2053
 
2054
+ </details>
2055
+
1665
2056
  ---
1666
2057
 
1667
2058
  ## Configuration
1668
2059
 
1669
2060
  Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder takes precedence.
1670
2061
 
2062
+ <details>
2063
+ <summary><b>More</b> — Supported servers · Environment variables · Flags · Project rules</summary>
2064
+
1671
2065
  ### Supported servers
1672
2066
 
1673
2067
  | | Example address |
@@ -1712,6 +2106,8 @@ deel --no-tui Turn the input box off; plain scrolling view (see below
1712
2106
  If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as project rules.
1713
2107
  `/init` scaffolds one.
1714
2108
 
2109
+ </details>
2110
+
1715
2111
  ---
1716
2112
 
1717
2113
  ## Troubleshooting
@@ -1777,6 +2173,9 @@ so one run tells you everything.
1777
2173
  | `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
1778
2174
  | `edit-bench` | 20 cases | Edit success rate |
1779
2175
 
2176
+ <details>
2177
+ <summary><b>More</b> — Coverage · Layout</summary>
2178
+
1780
2179
  ### Coverage
1781
2180
 
1782
2181
  ```bash
@@ -1797,6 +2196,414 @@ Currently **92% overall** (7,056 of 7,646 lines). Three files are deliberately l
1797
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) |
1798
2197
  | `plugins/manage.js` | 79% | The GitHub download path. **Tests not reaching the network** matters more. Folder installs are covered |
1799
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>
2274
+
2275
+ ---
2276
+
2277
+ ## Release notes
2278
+
2279
+ <details>
2280
+ <summary><b>▸ 1.4.0 — deel gets a face, speaks English, and sees meaning</b> · what changed in seven places</summary>
2281
+
2282
+ <br>
2283
+
2284
+ | | Before | After |
2285
+ |---|---|---|
2286
+ | `/undo` | rolled back files only — the conversation still believed it happened | rewinds **the conversation too** |
2287
+ | Turns that take minutes | you sat watching the screen | a bell and the window title — you can be in another window |
2288
+ | Startup · status bar | looked like any other CLI | letters that grow on start, a status bar that names the boundary |
2289
+ | English speakers | the screen was all unreadable | `/lang en` — the screen **and what the model reads** |
2290
+ | Models per session | one | a different model per chunk — routine work to a small one |
2291
+ | Finding a name | `Grep` only — comments and strings mixed in | `Def` / `Refs` — by **meaning**, when a server is installed |
2292
+ | A file you just edited | you found out by running it | checked **right after** the edit, that file only |
2293
+
2294
+ <br>
2295
+
2296
+ #### 1. `/undo` rewinds the conversation
2297
+
2298
+ Roll back only the files and the conversation still holds the edit. The model believes it
2299
+ just changed that file and builds the next step on top — and nothing on screen says
2300
+ otherwise. Now the messages fold back with the files. Folding can orphan a tool call, which
2301
+ the server answers with a 400, so the same `repairToolPairs` runs over the result.
2302
+
2303
+ #### 2. It tells you when it is done — `/bell`
2304
+
2305
+ A local model can take minutes per turn. A bell and the window title say when it finishes.
2306
+ The bell is ``, but **not one byte reaches a pipe** — with no TTY it writes nowhere. The
2307
+ title ends with ST, not BEL; ending with BEL rings the bell on every title update.
2308
+
2309
+ #### 3. A screen that is deel's own
2310
+
2311
+ On start, `deel` grows into `deel-local`. The status bar names the boundary you are inside
2312
+ with one glyph (`⌂` this folder · `↗` outside · `?` unknown). The animation uses braille and
2313
+ box-drawing only — emoji and geometric shapes are East Asian Ambiguous, so their width
2314
+ varies per terminal and the line drifts by a column.
2315
+
2316
+ #### 4·5. English on screen, and in what the model reads
2317
+
2318
+ `/lang en` switches the screen. Untranslated strings come through in Korean rather than as
2319
+ blanks, and `/lang` counts honestly how many are left.
2320
+
2321
+ But switching only the screen leaves the model answering in Korean — its rules say to. So
2322
+ what the model reads switches too (base rules, mode instructions, all sixteen tool
2323
+ descriptions). There is a bonus: Korean costs about one token per character and English
2324
+ about one per 3.6, so the fixed share of a 32k window dropped from **4,910 to 3,446 tokens.**
2325
+
2326
+ Tool names and argument names are **not** translated. Those are identifiers.
2327
+
2328
+ #### 6. Several models in one session — `Task`'s `모델`
2329
+
2330
+ A large model and a small one, together, on 8GB of RAM. Routine work (formatting, repetitive
2331
+ edits, short summaries) goes to the small one; you keep what needs judgement. The subtask's
2332
+ endpoint opens through `allowTemporarily` and **always closes in `finally`** — afterwards
2333
+ exactly one endpoint is open again.
2334
+
2335
+ #### 7. Language servers — `Def`, `Refs`, post-edit diagnostics
2336
+
2337
+ See "With a language server, it sees meaning" above. **It installs nothing.**
2338
+
2339
+ </details>
2340
+
2341
+ <details>
2342
+ <summary><b>▸ 1.3.0 — evidence instead of claims, the editor instead of a terminal</b> · what changed in six places</summary>
2343
+
2344
+ <br>
2345
+
2346
+ | | Before | After |
2347
+ |---|---|---|
2348
+ | Rules that must hold | **vanished** when folded or summarised | live outside the message list, where folding cannot reach |
2349
+ | This model's habits | the prompt **asked** it to behave | the harness changes instead — no cooperation needed |
2350
+ | Finishing | "all done" | what changed, and what proves it. **Including what doesn't** |
2351
+ | Where you use it | one more terminal window | inside your editor (Zed · JetBrains · Neovim · Emacs) |
2352
+ | Review paperwork | one document, for humans | SBOM · egress list · audit spec — **straight into a scanner** |
2353
+ | Keys | one `env` and they were in the conversation and on disk | masked where output enters. Files are **deliberately** left alone |
2354
+
2355
+ <br>
2356
+
2357
+ #### 1. Rules that must hold were vanishing into the fold — `/pin`
2358
+
2359
+ Long conversations fold and summarise earlier turns to make room. A 2026 measurement found
2360
+ **summarisation preserves only about 50% of safety constraints.** If "never touch this
2361
+ folder" lands in the missing half, the model is in a state where it was never told. Nothing
2362
+ appears on screen.
2363
+
2364
+ Pinned lines are **not kept with the messages.** They are appended to the end of the system
2365
+ prompt — and since folding and compaction only touch messages, they are structurally out of
2366
+ reach. Not carefully preserved: **impossible to remove.**
2367
+
2368
+ ```
2369
+ /pin never touch src/legacy
2370
+
2371
+ ✓ Pinned — 2 now (78 tokens)
2372
+ ```
2373
+
2374
+ Up to 12 lines / 240 tokens. Past that it says so and carries the most recent — dropping
2375
+ them quietly would defeat the point of pinning.
2376
+
2377
+ #### 2. Observed habits stayed as words — `/model 카드`
2378
+
2379
+ deel already watched what the model did. But watching was all it did — it **wrote advice
2380
+ into the prompt**: "you keep truncating arguments, use Append." Small models don't follow
2381
+ that advice. That is what makes them small models.
2382
+
2383
+ Now what it observes becomes **harness settings**. Instead of asking the model, deel changes
2384
+ its own behaviour.
2385
+
2386
+ | Observed | What changes |
2387
+ |---|---|
2388
+ | Arguments truncate often (over 15%) | The cap is raised up front — no wasted first call |
2389
+ | It repeats itself | Three identical calls tolerated becomes two |
2390
+ | Edits miss often | More surrounding lines are shown on a near-miss |
2391
+
2392
+ **Nothing changes before 12 steps.** Pinning down a healthy model because of one unlucky
2393
+ truncation is worse than not learning at all.
2394
+
2395
+ #### 3. Evidence instead of "all done" — `/evidence`
2396
+
2397
+ A 2026 survey found **96% of developers don't fully trust AI-written code, while 48% verify
2398
+ it every time.** 38% said it is harder to review than human code.
2399
+
2400
+ Why harder? Ask a person why they wrote it that way and you get an answer. Agent-written
2401
+ code arrives with **one line: "done."** That line cannot be reviewed.
2402
+
2403
+ ```
2404
+ /evidence
2405
+
2406
+ Changed 3 files · +142 −38
2407
+ Ran 5 commands (1 failed)
2408
+ Unproven 1
2409
+
2410
+ ✗ src/worker.js — the last `npm test` failed — an earlier pass
2411
+ does not prove the current state.
2412
+ ```
2413
+
2414
+ Listing what changed is something `/diff` already does. What only this does is **say that
2415
+ the unproven is unproven.** Three things get caught —
2416
+
2417
+ - Changed something and ran nothing? Nothing was proven
2418
+ - Counting a red test as green means **offering a failing test as evidence**
2419
+ - A check run *before* the edit proves nothing about it — "I ran it earlier" is the most
2420
+ common form of self-deception
2421
+
2422
+ If the build passed and the tests broke *after* it, the earlier green is not evidence. The
2423
+ last thing you ran is red; it cannot have been verified.
2424
+
2425
+ `/evidence filename` writes it as markdown under `.deel/증거/`. The screen scrolls away, and
2426
+ the review happens later, by someone else.
2427
+
2428
+ #### 4. It made you open one more terminal — `deel acp`
2429
+
2430
+ Developers live inside the IDE. A tool that makes you switch windows stops being used after
2431
+ about two weeks. A build that cleared corporate review and then nobody uses is the saddest
2432
+ possible outcome.
2433
+
2434
+ deel now speaks **ACP** (Agent Client Protocol). One line — `deel acp` — in your editor's
2435
+ settings and Zed, JetBrains, Neovim and Emacs attach **without changing a line on their side**.
2436
+
2437
+ The work isn't connecting the pipe; it is **making the editor able to show something**.
2438
+ Kind, location and status are all optional in the spec, so a quick implementation omits all
2439
+ three — and then every tool is the same grey dot and no changed file is clickable.
2440
+
2441
+ Approval flows through too. deel's safety rails render as the editor's own dialog, and
2442
+ "always allow" is remembered for that session. Against a client that cannot ask, it
2443
+ **does not run** — "if I can't ask, I do as I please" is not an option.
2444
+
2445
+ **Still zero dependencies.** Newline-delimited JSON-RPC 2.0 is the whole transport, so no SDK.
2446
+
2447
+ #### 5. Review paperwork only a human could read — `deel sbom`
2448
+
2449
+ A Korean financial-sector rule change on 2026-04-20 opened an exemption to the network-
2450
+ separation mandate. The paperwork demanded at that door is not prose — security feeds an
2451
+ **SBOM to a scanner** for a vulnerability list, and operations reads the audit-log spec to
2452
+ write SIEM ingestion rules.
2453
+
2454
+ `deel pack` now emits three documents.
2455
+
2456
+ | | |
2457
+ |---|---|
2458
+ | `반입심사서.txt` | The human-readable sheet, as before |
2459
+ | `sbom.cdx.json` | CycloneDX 1.5. One component per file with SHA-256. Dependencies as an **explicit empty array** — "not declared" and "none" are different claims |
2460
+ | `심사명세.json` | Egress list (per lane: when, where, what, how it's stopped, source location) · audit-log spec · file hashes |
2461
+
2462
+ All three are generated by scanning the source. The audit-log spec is the one hand-written
2463
+ part, so **a test compares it against real log records on every run** — a review document
2464
+ that drifts is worse than none, and one wrong line costs you the reviewer's trust in the rest.
2465
+
2466
+ #### 6. One `env` put your keys in the conversation and on disk
2467
+
2468
+ People rarely paste a key. The leak is almost always command output — `env`, `git remote -v`,
2469
+ `curl -v`, a failing test log. That text goes to the model **and** is written to
2470
+ `.deel/sessions/*.jsonl`. Leak once, and you have several copies.
2471
+
2472
+ Masking now happens at the single point where tool output enters the conversation:
2473
+ private-key blocks, OpenAI, Anthropic, GitHub, Slack, AWS, Google, JWTs, credentials in URLs,
2474
+ `Authorization`-family headers, and env vars named `…KEY` / `…TOKEN` / `…SECRET` /
2475
+ `…PASSWORD`. The configured gateway key is removed regardless of shape — that one is a known
2476
+ value, not a guess.
2477
+
2478
+ **File contents are deliberately not masked.** Mask `.env` and the model edits the masked text
2479
+ and writes it back, landing a placeholder where the real key was — protecting the secret would
2480
+ destroy it. So on the file side it reports instead of rewriting.
2481
+
2482
+ <br>
2483
+
2484
+ Tests 2,578 → **2,860** · 54/54 files. Earlier releases are on the [tags](https://github.com/jysvai/deel-local-cli/tags) page.
2485
+
2486
+ </details>
2487
+
2488
+ <details>
2489
+ <summary><b>▸ 1.2.0 — so the conversation doesn't break</b> · what changed in six places</summary>
2490
+
2491
+ <br>
2492
+
2493
+ | | Before | After |
2494
+ |---|---|---|
2495
+ | Resuming | a conversation cut mid-tool-call **would not reopen** | unmatched calls are cleared, then it opens |
2496
+ | Counting tokens | it guessed, and stayed wrong | it corrects itself against the server |
2497
+ | Making room | summarising arrived at turn 49 | it holds out to turn **102** |
2498
+ | Side questions | piled up in the main context | live in their own thread |
2499
+ | Yesterday's lesson | vanished when you quit | carries over to the next session |
2500
+ | The answer on screen | `**bold**` showed up as characters | it is drawn |
2501
+
2502
+ <br>
2503
+
2504
+ #### 1. A conversation cut mid-tool-call would not reopen
2505
+
2506
+ Close the window or hit <kbd>Ctrl</kbd>+<kbd>C</kbd> while a tool is running and the saved conversation keeps **a call with no result under it**. Reopen it with `--resume` and the server rejects the mismatch with a 400 — the conversation was written down perfectly well, and you still could not carry on.
2507
+
2508
+ Now the pairs are checked before it opens. Calls with no result go, results with no parent go, and **whatever the model said stays.**
2509
+
2510
+ ```
2511
+ $ deel --resume 20260826-140217
2512
+
2513
+ ✓ 20260826-140217 — 메시지 48개를 이어 받았습니다.
2514
+ 중단된 도구 호출 2개를 걷어냈습니다 — 그때 하던 일은 다시 시켜 주세요.
2515
+ ```
2516
+
2517
+ #### 2. It misjudged the room left
2518
+
2519
+ Token counts are estimated from character counts. Mix Korean, code and JSON and that estimate drifts from the truth — so deel folded early with room to spare, or did not fold when there was none and the server refused.
2520
+
2521
+ It now **corrects the multiplier against what the server actually reports** with every answer. One line at the foot of `/context`:
2522
+
2523
+ ```
2524
+ 서버가 알려 준 실제값에 맞춰 +12% 보정했습니다 (7번 재봄).
2525
+ ```
2526
+
2527
+ The multiplier is kept per model, so **the next session starts from it** instead of measuring again from scratch.
2528
+
2529
+ #### 3. Summarising arrived too early
2530
+
2531
+ At 80% of the window, earlier turns get folded into a summary. That **cannot be undone**, and the file contents the reasoning rested on are gone with it.
2532
+
2533
+ There is now a step before it. At 55%, **only older tool results** are folded — the four most recent are left alone, and nothing the model or you said is touched at all.
2534
+
2535
+ ```
2536
+ ◲ 오래된 도구 결과 6개를 접었습니다 (2,148 토큰을 비움)
2537
+ ```
2538
+
2539
+ What was there is left in its place:
2540
+
2541
+ ```
2542
+ (접힘) Read(src/runner.js) — 61줄. 자리를 비우려고 내용을 접었습니다. 필요하면 다시 읽으세요.
2543
+ ```
2544
+
2545
+ Measured by streaming the same conversation through: summarising is pushed from **turn 49 to turn 102 — 2.1×**.
2546
+
2547
+ #### 4. Side questions polluted the main line
2548
+
2549
+ "Just check this one thing" piles into the main context and stays there long after the checking is done.
2550
+
2551
+ ```
2552
+ /thread new 로그확인
2553
+ ⑂ 로그확인 갈래로 왔습니다. 빈 대화입니다
2554
+ 본줄기로 돌아가려면 /thread 1
2555
+ ```
2556
+
2557
+ `fork` carries the conversation so far with you. What threads **keep apart is the messages, the token count and the checklist**; what they **share is the connection, undo and the audit log** — a file changed inside a thread still comes back with `/undo`. The `⑂` marker appears in the status line only once there is more than one thread.
2558
+
2559
+ #### 5. Yesterday's lesson vanished when you quit
2560
+
2561
+ deel could work out that `pnpm` is not on this machine, and lose it the moment you quit. Tomorrow it calls it again, fails again, works around it again.
2562
+
2563
+ ```
2564
+ /learned
2565
+ ── 겪어 본 것 ──────────────────────────────────────
2566
+
2567
+ 이 폴더에서 돌려 본 명령
2568
+ ✓ npm test 됨 12 · 안 됨 0
2569
+ ✗ pnpm 됨 0 · 안 됨 3
2570
+
2571
+ 이 모델에 대해 qwen2.5-coder:7b
2572
+ 같이 걸어 본 걸음 86
2573
+ 인자가 잘림 14 (16%)
2574
+ 토큰 추정 보정 ×1.12
2575
+
2576
+ 이 중 프롬프트에 실리는 것
2577
+ - 여기서 되는 명령: `npm test`
2578
+ - 이 PC 에서 안 되는 명령(다시 부르지 마라): `pnpm`
2579
+ ```
2580
+
2581
+ **This is not training.** It does not touch the model and it does not hoard conversations — that would only eat context. It counts, and it carries over **only what it has seen twice**, all of it **within 220 tokens**. Something seen once may be a coincidence, and writing a coincidence down as fact sends the model around a road that actually works.
2582
+
2583
+ Commands that work live with the folder (`.deel/배운것.json`); the model's habits live in the config folder. So **move the folder and what it learned about the model comes along.** `/learned 지우기` empties it whenever you want.
2584
+
2585
+ #### 6. The answer showed up as raw characters
2586
+
2587
+ The model speaks Markdown and the screen did not know it, so asterisks, backticks and hashes came through mixed into the prose. You had to re-read it in your head to see what was a heading and what was code.
2588
+
2589
+ ```
2590
+ before after
2591
+ ▌ ## 고친 것 ▌ ▍ 고친 것
2592
+ ▌ **src/runner.js** 의 `console.log` ▌ src/runner.js 의 console.log
2593
+ ▌ - [ ] 남은 것: `src/worker.js` ▌ ☐ 남은 것: src/worker.js
2594
+ ▌ ```js ▌ ┌──────────────── js
2595
+ ▌ log.info('시작', { id }) ▌ │ log.info('시작', { id })
2596
+ ▌ |---|---| ▌ ┼────────┼─────────┼
2597
+ ```
2598
+
2599
+ Answers arrive in fragments, so a line can only be drawn **once it ends**. But a whole paragraph on one line would leave the screen still for seconds — so once a line grows past the screen width, everything up to there is **streamed raw**. Looking alive comes before looking neat.
2600
+
2601
+ <br>
2602
+
2603
+ Tests 2,532 → **2,578** · 48/48 files. Earlier releases are in the [tags](https://github.com/jysvai/deel-local-cli/tags).
2604
+
2605
+ </details>
2606
+
1800
2607
  ---
1801
2608
 
1802
2609
  ## Licence