deel-local-cli 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -20,10 +20,12 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
20
20
  │ Sends to this machine 127.0.0.1:11434 ← nowhere else │
21
21
  │ Link streaming · tools · reasoning control │
22
22
  │ Folder C:\work\myproject │
23
+ │ Approval ⏵⏵ 자동 승인 — nothing is asked; /undo is the net │
24
+ │ Shift+Tab to change · Tab completes a / command │
23
25
  │ This PC 337 skills · 127 commands · 42 plugins │
24
26
  ╰──────────────────────────────────────────────────────────────╯
25
27
 
26
- ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱▱▱ 22% 28k/128k ▏ ◎ 종합 · ◇ medium·절약 · auto
28
+ ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱▱▱ 22% 28k/128k ▏ ◎ 종합 · ◇ medium·절약 · ⏵⏵ 자동
27
29
  ❯ unify the logging style
28
30
 
29
31
  ❊ Grep(console.log)
@@ -201,6 +203,136 @@ $ deel scan
201
203
 
202
204
  Switch with `/model` mid-conversation — **the conversation carries over.**
203
205
 
206
+ ### It adapts to whatever model is attached
207
+
208
+ If you move between models, any number tuned for one of them is wrong for all the others.
209
+ So **no number is hardcoded.**
210
+
211
+ There are two axes. They are easy to confuse, so they have separate commands.
212
+
213
+ | | What it measures | Command |
214
+ |---|---|---|
215
+ | **Window size** | how much it can hold | `/ctx` |
216
+ | **Model grade** | how much it can do on its own | `/grade` |
217
+
218
+ They do not move together. A 3B model with a 128k window exists; so does a very good model
219
+ with 32k. Treat them as one axis and you hold one back while overrunning the other.
220
+
221
+ **Derived from window size** (`src/agent/budget.js`):
222
+
223
+ | | 8k | 32k | 131k | 655k |
224
+ |---|---|---|---|---|
225
+ | Steps per turn (code) | 16 | 48 | 192 | 200 |
226
+ | `Read` lines | 200 | 384 | 1,536 | 4,000 |
227
+ | `Glob` results | 50 | 192 | 768 | 1,000 |
228
+ | `Outline` lines | 120 | 480 | 1,920 | 2,500 |
229
+ | `WebFetch` chars | 4,000 | 12,800 | 51,200 | 120,000 |
230
+ | Subtask summary | 400 | 1,600 | 4,000 | 4,000 |
231
+
232
+ **Derived from model grade** (`src/agent/grade.js`):
233
+
234
+ | | small | medium | large |
235
+ |---|---|---|---|
236
+ | Files per `Write` | 3 | 6 | 12 |
237
+ | Split-writing threshold | 200 lines | 400 lines | 800 lines |
238
+ | Spell out the procedure | yes | yes | **no — give the goal** |
239
+ | Require verification | yes | yes | **yes** (grade-independent) |
240
+
241
+ The grade is decided like this:
242
+
243
+ 1. **First guess from the name.** `qwen2.5-coder-7b` -> small, `llama-3.3-70b` -> large.
244
+ Version numbers (`2.5`) and quantisation tags (`q4_k_m`) are not read as sizes.
245
+ A name that says nothing means **medium**, not small — a corporate gateway is exactly
246
+ that case, and the models behind one are usually big. Guessing small holds them back.
247
+ 2. **Corrected by what actually happened.** Truncated tool arguments, empty answers,
248
+ failed edits and repeats are counted. A model labelled 70B that truncates every step is
249
+ dropped to **small**; a 7B that runs ten clean steps is raised one level. The name is a
250
+ guess; what happened is a fact.
251
+ 3. **You win if you say so.** `/grade large`, and `/grade auto` hands it back.
252
+
253
+ The status line shows `◈ small?`. The question mark means **still a guess** — a guess is
254
+ not presented with the same face as something measured.
255
+
256
+ The grade only changes **how much hand-holding you get**. Working scope, approval mode,
257
+ undo and the audit log are identical at every grade. There is no "it is a good model, so
258
+ skip verification" — that is exactly how a good model's mistake goes unnoticed.
259
+
260
+ ### Small windows get a smaller fixed share
261
+
262
+ The system prompt and the tool definitions go out **in full on every request**. Compaction
263
+ (`/compact`) cannot shrink them. Once that share passes half the window there is no room
264
+ left however well you fold, and it looks like "the model suddenly got stupid".
265
+
266
+ Adding three tools (`Outline`, `Verify`, `Task`) pushed it to **49%** at 8k. Dropping a
267
+ tool would have fixed it — and would have made small models capable of different things,
268
+ which is the thing to avoid. The descriptions were trimmed to the window instead.
269
+
270
+ | | 8k | 16k | 32k+ |
271
+ |---|---|---|---|
272
+ | Base rules | short form | short form | full |
273
+ | Mode description | short form | short form | full |
274
+ | Tool descriptions | 90 chars | 140 chars | 220 / full |
275
+ | Obvious param descriptions | dropped | kept | kept |
276
+ | Descriptions inside array items | dropped | dropped | kept |
277
+ | **Fixed share** | **2,712 tokens (33%)** | 3,290 (21%) | 4,745 (4% at 131k) |
278
+
279
+ The **folder brief** adds roughly 80 more tokens at 8k (below). That value varies by
280
+ folder, so it is not in the table.
281
+
282
+ **Tool names and arguments are untouched.** What is possible is identical in every window;
283
+ what disappears is only the argument for *why* to use a tool. Large windows keep it,
284
+ because that argument earns its keep — those two sentences are what make a model call
285
+ `Outline` before `Read`.
286
+
287
+ A test pins these numbers (`test/compact.test.js`).
288
+
289
+ ### On startup it reads what kind of project this folder is
290
+
291
+ Started in a folder of someone else's code, the model began knowing nothing. So it
292
+ retraced the same three steps every time — scan the top level, read `package.json`, find
293
+ out how tests are run. **On a local model each step is 20-40 seconds, so two minutes go
294
+ by before the work even starts.**
295
+
296
+ The worse case is the model *skipping* those three steps. Then it creates files by its
297
+ own conventions without knowing what the project already uses — a `requirements.txt`
298
+ dropped into an npm project.
299
+
300
+ The answers are all knowable at startup, so they are read once and put in the prompt.
301
+
302
+ The brief itself is written in Korean, like everything else deel puts on screen — this is
303
+ deel's own folder, wrapped here for width:
304
+
305
+ ```
306
+ --- 이 폴더 --- (this folder)
307
+ node 프로젝트 (deel-local-cli) · git main
308
+ 돌릴 수 있는 것: npm start · npm test · npm run bench · npm run chat · npm run check ·
309
+ npm run coverage · npm run demo · npm run diagnose (runnable)
310
+ 위쪽: bin/ src/ test/ LICENSE README.en.md README.md package.json report.txt (top level)
311
+ 위쪽 한 겹만 본 것이다. 안을 알아야 하면 Outline 을 불러라.
312
+ (top level only — call Outline to see inside)
313
+ ```
314
+
315
+ **Nothing is invented.** Runnable commands are copied verbatim from `scripts` in
316
+ `package.json`. Advertising a command that does not exist means the model calls it,
317
+ fails, and spends the steps you just saved looking for the real one.
318
+
319
+ **The last line matters.** What is listed is the top level and `package.json`, nothing
320
+ inside subfolders. Without saying so, the model treats this as a map of the whole project
321
+ and stops calling `Outline` — at which point the brief costs more than it saves.
322
+
323
+ | Not done | Why |
324
+ |---|---|
325
+ | No `git` subprocess | `.git/HEAD` is read directly. Spawning one freezes startup for seconds on a large repo |
326
+ | No directory walk | Top level **only**. Descending is slow on large repos, and only one line goes in the prompt anyway |
327
+ | Not re-read each turn | Once, at startup. A prompt that changes mid-conversation makes it impossible to tell why an answer changed |
328
+
329
+ It scales with the window — 10 entries and 4 commands at 8k, 24 and 8 on a large one.
330
+ If only one thing survives a narrow window it is the **commands**. The top-level listing
331
+ can be recovered with one `Glob`; "tests run with `npm test`" requires opening
332
+ `package.json`.
333
+
334
+ 7ms on a folder with 600 files (`test/project.test.js`).
335
+
204
336
  ---
205
337
 
206
338
  ## Slash commands
@@ -212,6 +344,7 @@ Names follow Claude Code / Codex conventions.
212
344
  | `/help` | Command list |
213
345
  | `/context` | What is consuming the context window |
214
346
  | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
347
+ | `/grade [small\|medium\|large\|auto]` | Model **grade** — how much it does on its own. A different axis from `/ctx` |
215
348
  | `/out [number\|auto]` | Cap on a **single reply** — raise it when large files get cut |
216
349
  | `/compact` | Summarise and fold older turns |
217
350
  | `/clear` | Clear the conversation (keeps link and rules) |
@@ -245,6 +378,18 @@ Discovered plugin commands are invoked as `/<plugin>:<name>`, with `$ARGUMENTS`
245
378
  server or loaded a different model, `/scan save` then `/model` switches over without losing
246
379
  the conversation.
247
380
 
381
+ **Without typing**
382
+
383
+ | Key | What it does |
384
+ |---|---|
385
+ | `Tab` | Completes the `/` command you are typing. Candidates appear under the box as you type |
386
+ | `Shift+Tab` | Approval policy (`⏵⏵ auto` → `⏵ risky only` → `⏸ everything`) |
387
+ | `Ctrl+O` | Work mode (`종합` → `코드` → `계획` → …) |
388
+ | `↑` `↓` | Input history |
389
+ | `Ctrl+C` | Stops the answer in progress; twice on an empty line quits |
390
+
391
+ Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
392
+
248
393
  ### Attaching a file with `@`
249
394
 
250
395
  Write `@` followed by a path and that file is sent along with your message.
@@ -411,7 +556,7 @@ the bottom.** Only the box is erased and redrawn — nothing above it is touched
411
556
 
412
557
  ── 4.2s · 3 tools · ↑3,900 ↓180
413
558
 
414
- ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱ 22% ▏ ◎ 종합 · ◇ medium · auto
559
+ ▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱ 22% ▏ ◎ 종합 · ◇ medium · ⏵⏵ 자동
415
560
  ╭─────────────────────────────────────────────────────────────────────────────╮
416
561
  │ ❯ also shrink the aggregate helpers │
417
562
  ╰─────────────────────────────────────────────────────────────────────────────╯
@@ -442,6 +587,127 @@ editor is how you break IME input first.
442
587
 
443
588
  ---
444
589
 
590
+ ### You don't have to type the whole command
591
+
592
+ There are over thirty commands. The only person who has them memorised is the one who
593
+ wrote them, and even he gets as far as `/mem…` and stops to wonder whether it was `memory`
594
+ or `memo`. If the only recourse at that point is typing `/help` and scanning thirty lines,
595
+ that isn't a command, it's a quiz.
596
+
597
+ So they show up **while you type.**
598
+
599
+ ```
600
+ ╭─────────────────────────────────────────────────────────────────────────────╮
601
+ │ ❯ /mo │
602
+ ╰─────────────────────────────────────────────────────────────────────────────╯
603
+ › /model [이름|list|models] 연결·모델 바꾸기 (이름 일부 · list · models)
604
+ /mode <모드> 승인 정책 — 얼마나 물어보나
605
+ /memory [지우기 <번호>|…] 대화가 끝나도 남는 기억 — 보기·지우기
606
+ ```
607
+
608
+ `Tab` fills it in: all the way if only one matches, otherwise **only as far as they all
609
+ agree** (`/mo` + Tab → `/mode`). It doesn't pick one for you, because deleting the wrong
610
+ guess costs more than typing the rest. Commands that take an argument get a trailing space
611
+ so you can keep going.
612
+
613
+ Prefix matches come first and **substring matches follow** — typos cluster in the first
614
+ character, so `/emo` still surfaces `/memory`. The moment you type a space (`/mode auto`)
615
+ the list folds away: the command is already decided.
616
+
617
+ The list sits **below** the box. Putting it inside would push what you are typing upward,
618
+ so you could no longer see it.
619
+
620
+ > No up/down selection. That would have to take over **input history** (up arrow), which
621
+ > is used far more often. What is needed here isn't picking, it's recognising — and once
622
+ > you recognise it, one `Tab` is enough.
623
+ >
624
+ > Commands hidden at the `쉬움` level still surface when you type their prefix. Hidden is
625
+ > not the same as unavailable — someone who knows `/recall` should not be told it doesn't
626
+ > exist because of a display setting.
627
+
628
+ ---
629
+
630
+ ### The box stays while it works
631
+
632
+ Local models are slow. A single step can take tens of seconds, and if the bottom of the
633
+ screen goes blank for that long, **people assume it hung and hit Ctrl+C** — losing work that
634
+ was nearly done. So the border stays and only the contents change.
635
+
636
+ ```
637
+ ◧ Read(집계.py)
638
+ └ 6 lines
639
+
640
+ ▏myproject · qwen2.5-coder:7b ▏ ▰▱▱▱▱▱▱▱▱▱ 2% ▏ ◎ 종합 · ◇ medium · ⏵⏵ 자동 ▏ ↑3.8k ↓180
641
+ ╭─────────────────────────────────────────────────────────────────────────────╮
642
+ │ ⠹ 파일 들여다보는 중… 12초 · 생각 1,240자 · Ctrl+C 중단 │
643
+ ╰─────────────────────────────────────────────────────────────────────────────╯
644
+ ```
645
+
646
+ The phrase tracks **what is actually happening**. This is not decoration: a message that
647
+ cycles at random stops being read after the second time, and from then on it is worth no
648
+ more than a blank screen. One turn reads like this:
649
+
650
+ ```
651
+ 머리 굴리는 중 → 파일 들여다보는 중 → 코드 짜는 중 → 답 쓰는 중
652
+ (turning it over) (looking at files) (writing code) (writing the answer)
653
+ ```
654
+
655
+ | Activity | Phrases |
656
+ |---|---|
657
+ | Thinking | 머리 굴리는 중 · 어떻게 할지 궁리하는 중 · 수 읽는 중 · 따져 보는 중 |
658
+ | `Read` `Grep` `Glob` | 파일 들여다보는 중 · 코드 훑는 중 · 어디 있나 뒤지는 중 · 단서 찾는 중 |
659
+ | `Write` `Edit` `Append` | 코드 짜는 중 · 고쳐 넣는 중 · 손보는 중 · 한 줄씩 옮기는 중 |
660
+ | `Bash` | 명령 돌리는 중 · 터미널 두드리는 중 · 결과 기다리는 중 |
661
+ | `WebFetch` | 문서 찾아보는 중 · 읽어 오는 중 |
662
+ | Answering | 답 쓰는 중 · 정리해서 말하는 중 |
663
+ | **Past 45 seconds** | 아직 하는 중 · 조금만 더 · 생각보다 오래 걸리는 중 |
664
+
665
+ Within a category the phrase advances every 4 seconds — text frozen for 30 seconds reads as
666
+ hung too. On the right: **elapsed time**, and while the model is reasoning, **how many
667
+ characters of thinking have arrived**. One number that genuinely increases is what turns
668
+ "still alive" from a claim into a fact.
669
+
670
+ ### What gets asked, and what just happens
671
+
672
+ Whether your files change **with or without being asked** is the one thing that has to be
673
+ readable at a glance. It sits on the right of the status line at all times.
674
+
675
+ | Indicator | Command | What it asks about |
676
+ |---|---|---|
677
+ | `⏵⏵ 자동 승인` (auto) | `/mode auto` | Nothing is asked. `/undo` is the safety net |
678
+ | `⏵ 위험만 확인` (risky only) | `/mode confirm` | Only irreversible commands. Files change unasked |
679
+ | `⏸ 모두 확인` (everything) | `/mode strict` | Every file change and every command is confirmed first |
680
+
681
+ `/mode` on its own lists all three and marks the current one with ●. The startup header
682
+ spells it out in a sentence, so the glyph is enough from then on.
683
+
684
+ **`Shift+Tab` cycles it without typing.** Each press moves one step and leaves a line
685
+ saying where it went. Whatever you were typing stays put.
686
+
687
+ ```
688
+ ⏵ 위험만 확인 되돌릴 수 없는 명령만 물어봅니다. 파일은 안 묻고 고칩니다
689
+ 자동 승인 → 위험만 확인 · Shift+Tab 으로 계속 바꿉니다
690
+ ```
691
+
692
+ The cycle runs **loose → strict** (auto → risky only → everything → auto). A mistaken
693
+ press only makes it ask more; it never drops you into "changes files unasked" in one hit.
694
+
695
+ > That key used to cycle the work mode (`종합`, `코드`, …). The swap is about **who
696
+ > reaches for it more often.** Work mode follows your request on its own, while approval
697
+ > policy is what you want to change mid-task when a particular job deserves a look.
698
+ > Work mode moved to `Ctrl+O`; `/work` still does the same thing.
699
+
700
+ > It used to be the bare word `auto`. Next to `종합` and `medium·절약` it looked like just
701
+ > another mode, and nothing on screen said that one of them meant **files change without
702
+ > asking.**
703
+ >
704
+ > That also changed what gets truncated when space runs out. A corporate gateway model name
705
+ > like `databricks-gpt-5-6-luna` eats twenty-three columns, and that alone was pushing the
706
+ > approval indicator off the line entirely. Now **the model name shortens first** — you
707
+ > already know what you are running; whether your files change unasked is what you need now.
708
+
709
+ ---
710
+
445
711
  ## Tools
446
712
 
447
713
  Names and arguments match Claude Code, so skills written for that convention work unchanged.
@@ -449,21 +715,233 @@ Names and arguments match Claude Code, so skills written for that convention wor
449
715
  | Tool | What it does |
450
716
  |---|---|
451
717
  | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV**) |
452
- | `Write` | Write / overwrite a file |
718
+ | `Write` | Write / overwrite a file (**several at once via the `files` array**) |
453
719
  | `Append` | Append to the end of a file — **how large files get written in pieces** |
454
- | `Edit` | Replace an exact string (`replace_all` supported) |
720
+ | `Edit` | Replace an exact string (`replace_all`; **several sites at once via the `edits` array**) |
455
721
  | `Glob` | Find files by name pattern |
456
722
  | `Grep` | Regex search file contents |
457
- | `Bash` | Run a command |
723
+ | `Bash` | Run a command (**`background: true` for anything that does not finish**) |
458
724
  | `Skill` | Expand a skill body (shown to the model only when skills exist) |
459
725
  | `WebFetch` | Read a web page (read-only; hidden under `--offline`) |
460
726
  | `Recall` | Search **past conversations** — the model digs up "that thing last time" itself |
461
727
  | `Remember` | One line that outlives the session — known from the start next time |
462
728
  | `TodoWrite` | Checklist — breaks long work into steps and shows progress |
729
+ | `Outline` | See a folder's **skeleton only** — tens of times cheaper than reading it whole |
730
+ | `Verify` | Check that what was built **actually works** |
731
+ | `Task` | Run one chunk of a big job in a **separate context** |
732
+ | `Jobs` | Inspect, read and stop **background commands** — the other half of `Bash`'s `background` |
733
+
734
+ Seven tools here are not in Claude Code — `Append`, `Recall`, `Remember`, `Outline`,
735
+ `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
736
+ so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
737
+ their cost; here is why.
738
+
739
+ ### Seeing a project's shape cheaply — `Outline`
740
+
741
+ There used to be only two ways to understand someone else's code. `Glob` gives you paths;
742
+ `Read` pulls a whole file into the window. **The middle was missing.**
743
+
744
+ So the model started editing without knowing what lived where, and re-created functions
745
+ that already existed somewhere else. It had not seen them — which is different from not
746
+ knowing.
747
+
748
+ ```
749
+ ❉ Outline(src/ui) 12 files · 122 places
750
+
751
+ src/ui/screen.js (304 lines)
752
+ 46 fn 상자쓸까
753
+ 65 class LineScreen
754
+ 92 method 줄
755
+ 198 class BoxScreen
756
+ ```
757
+
758
+ Reading that folder whole costs **25,612 tokens**; `Outline` costs **857** — 30x cheaper.
759
+ An 8k model can see the shape of a whole project.
760
+
761
+ It reads js/ts, py, java/kotlin, go, rust, c#, md, html, css, sh and json. Regex, not a
762
+ parser (zero dependencies). So it **says what it could not read** — dropping those
763
+ silently makes the model believe the file does not exist, and rebuild config that is
764
+ already there.
765
+
766
+ ### Checking what was built — `Verify`
767
+
768
+ The end of a turn used to say:
769
+
770
+ ```
771
+ ✓ index.html · 410 lines · 18.2KB
772
+ ```
773
+
774
+ That proves the file **exists**, not that it **works**. An unclosed `<div>`, a
775
+ `src="app.js"` pointing at nothing, a JS file one bracket short — all green.
776
+
777
+ What can be run gets run (`node --check`, `py_compile`); what cannot gets read (HTML tag
778
+ pairing, missing references, CSS braces, JSON).
779
+
780
+ ```
781
+ ⏺ Verify 1 broken · 3 checked
782
+ ```
783
+
784
+ And the part that matters most — **what could not be checked is reported as such.**
785
+
786
+ Arbitrary commands are **not** run here. That path has to be `Bash` alone: the approval
787
+ gate and the safety checks live only there, so running commands from here would break the
788
+ strict-mode promise in exactly this one spot. It tells you `npm test` exists instead.
789
+
790
+ ### Splitting big work off — `Task`
791
+
792
+ Building eight files in one window means all eight files pile up in that window. On a 32k
793
+ model it fills around the third or fourth, and once it fills, earlier turns get folded
794
+ away. From then on the model has forgotten what it was building — **no error appears, the
795
+ result just gets worse.** That was the root of "build me a dashboard" ending as a plan.
796
+
797
+ `Task` runs that chunk in a **fresh conversation** and returns only a summary.
798
+
799
+ ```
800
+ ⌥ subtask build the page skeleton separate conversation · max 8 steps
801
+ │ ◆ Write(index.html +1) 2 files · 24 lines
802
+ ✓ subtask build the page skeleton done · 2 files · 2 steps
803
+ ```
804
+
805
+ Peak conversation size while building the same four files (system prompt excluded):
806
+
807
+ | | Peak |
808
+ |---|---|
809
+ | All in one window | 4,181 chars |
810
+ | Split with `Task` | **2,113 chars** |
811
+
812
+ The left column keeps growing with each file; the right one does not.
813
+
814
+ **Every guard still applies.** A subtask runs inside the same working folder, follows the
815
+ same approval mode, is undone **together with** its parent by one `/undo`, and lands in
816
+ the audit log. There is no path for a subtask to edit files under a read-only mode
817
+ (architect, plan, ask) — that is blocked both at the mode level and in the tool list.
818
+ Nesting stops at two levels.
819
+
820
+ ### Commands that never finish — `Bash`'s `background` and `Jobs`
821
+
822
+ `Bash` only returns once the command **ends**. So anything that does not end could not be
823
+ run — `npm run dev`, `python -m http.server`, `vite`, `npm run watch`. Asking for one used
824
+ to mean waiting 120 seconds and then a kill, leaving one line on screen.
825
+
826
+ ```
827
+ ▶ Bash(npm run dev)
828
+ └ 시간 초과로 중단됨 (120000ms) 2분 0.0초
829
+ (timed out)
830
+ ```
831
+
832
+ The model concludes the server would not start and gives up, or worse, raises `timeout`
833
+ and calls again — which stalls the whole turn. **There was no way at all to start what
834
+ you built and check it.** `Verify` gets you as far as "the syntax is valid"; whether it
835
+ actually comes up requires bringing it up.
463
836
 
464
- Only **three** tools here are not in Claude Code — `Append`, `Recall`, `Remember`. Each
465
- tool costs roughly 150 tokens of schema on **every request**, so a test stops you every
466
- time the list grows (`test/loop.test.js`).
837
+ ```
838
+ Bash(npm run dev)
839
+ 1번으로 띄움 (started as job 1)
840
+
841
+ ◈ Edit(src/App.jsx)
842
+ └ 1군데 (1 site)
843
+
844
+ ◐ Jobs(1번)
845
+ └ 도는중 · 24초 (running · 24s)
846
+
847
+ ◐ Jobs(1번 · 끝내기)
848
+ └ 끝냄 · 41초 (stopped · 41s)
849
+ ```
850
+
851
+ It starts and **returns immediately**. Output accumulates and `Jobs` reads it.
852
+
853
+ **Something that did not start is never reported as started.** The job is watched briefly
854
+ after launch, and if it dies in that window it comes back as a failure. The most common
855
+ failure is a port already in use; reporting that as "started" sends the model on to the
856
+ next step while you refresh a server that was never there.
857
+
858
+ ```
859
+ ▶ Bash(npm run dev)
860
+ └ 띄우자마자 끝났습니다 (종료코드 1).
861
+ (exited immediately, exit code 1)
862
+ ```
863
+
864
+ | Guarantee | Detail |
865
+ |---|---|
866
+ | Safety checks | **Identical** to `Bash`. This must not become a back door |
867
+ | Cleanup | Everything is killed when deel exits — **down to grandchildren**, and it says how many |
868
+ | Retained | 256KB. Past that the front is dropped and **the drop is stated** |
869
+ | Handed to the model | 4,000 chars. This **must** be a different number from the one above |
870
+ | On stop | Waits for the dying output, and returns only once the process is **actually dead** |
871
+ | Argument names | Korean and English both accepted (`번호`/`job`, `끝내기`/`stop`). Unrecognized ones **are reported** |
872
+ | Count | Eight running. Finished jobs keep the most recent eight, and evictions **are stated** |
873
+
874
+ Why two different caps: make them equal and every overflow of a `watch` job means
875
+ one `Jobs` read dumps 256KB into the window. On an 8k model that single read ends
876
+ the window.
877
+
878
+ **Stopping a job does not close its pipes immediately.** At the moment the kill is
879
+ issued there is still unread data in the pipe, and the last few lines before a
880
+ death are the ones that matter — the stack trace a server leaves on the way down.
881
+ Printing `last output:` and then withholding the last output is worse than not
882
+ printing it. It also waits until the process is **confirmed dead** before dropping
883
+ it from the list: dropping a live one means it can never be named again, which is
884
+ the exact state this feature exists to prevent.
885
+
886
+ Finished jobs are not dropped right away — they are kept so their final output can
887
+ be read. Only the most recent eight survive; otherwise thirty short commands leave
888
+ thirty entries, each holding up to 256KB.
889
+
890
+ **Argument names are accepted in both Korean and English.** Models frequently
891
+ translate Korean parameter names into English — not a guess, something this repo
892
+ already hit (`Task` accepts both `목적` and `purpose`). `Jobs` did not, which meant:
893
+
894
+ ```
895
+ Jobs({job: 1, stop: true}) -> a listing comes back. The server keeps running.
896
+ ```
897
+
898
+ The model asked for a stop and **got what looks like a success** while the port
899
+ stays held. So both spellings are accepted, and when nothing is recognized it says
900
+ so rather than falling back to a listing. The name mapping lives in exactly **one**
901
+ place — the on-screen label reads it too. Two copies means the tool works while the
902
+ label shows empty parentheses.
903
+
904
+ **Killing grandchildren is where this quietly goes wrong.** `npm run dev` descends
905
+ npm → node → vite, and the thing holding the port is at the bottom. Windows has
906
+ `taskkill /t` to walk the tree; Unix has nothing equivalent, so the job is
907
+ **started in its own process group** — after the fact there is no way to name a
908
+ grandchild at all. Skip that and deel says "killed 3" while the server keeps running.
909
+
910
+ Cleanup is where this quietly goes wrong. Skip it and a process nobody started keeps
911
+ running. Next time you start a dev server you get "port already in use" with **no way to
912
+ find what is holding it**. So `test/jobs.test.js` verifies the process actually died, via
913
+ a file the child keeps appending to.
914
+
915
+ `deel run` (one-shot mode) does the same. A batch job is hurt worst by missing this — the
916
+ job reports done, the server keeps running, and the next job fails to bind the same port
917
+ with nothing in the log to explain it.
918
+
919
+ ### Several at once — `Write`'s `files`, `Edit`'s `edits`
920
+
921
+ One round trip is 20-40 seconds on a local model. Creating five files with five `Write`
922
+ calls is two to three minutes of nothing but round trips. So they go in one array.
923
+
924
+ ```
925
+ ◈ Edit(src/app.js 외 2군데)
926
+ └ 2개 파일 · 3군데 (2 files · 3 sites)
927
+ ✓ src/app.js · 2군데
928
+ ✓ src/style.css · 1군데
929
+ ```
930
+
931
+ **Editing is worth more than writing here.** Creating files happens once; editing happens
932
+ continuously. Six edit sites at six round trips is minutes gone.
933
+
934
+ | | Guaranteed |
935
+ |---|---|
936
+ | Applied in order | Editing one file twice is common. Each edit re-reads from disk, so later ones see earlier results |
937
+ | One failure | The rest still run. Stopping at the first failure re-adds the round trips this was meant to remove |
938
+ | On failure | "Resend only what failed — **and `Read` the file again first**" |
939
+ | Undo | Still **one turn**. Six sites in one file is one `/undo` |
940
+ | Single-site form | The result shape is byte-for-byte unchanged |
941
+
942
+ It reports `2 files · 3 sites` rather than a single number. Editing one file at six sites
943
+ is normal, so "3 files" would be false — and once the screen stops matching what you can
944
+ count yourself, you stop trusting the screen.
467
945
 
468
946
  ### Finding past conversations, and remembering decisions
469
947
 
@@ -1045,7 +1523,7 @@ does not ask.
1045
1523
 
1046
1524
  | Mechanism | Detail |
1047
1525
  |---|---|
1048
- | **Undo** | Snapshot before every write. `/undo` restores per turn |
1526
+ | **Undo** | Snapshot before every write. `/undo` restores per turn. **Includes moves and deletes done through `Bash`** |
1049
1527
  | **Change display** | The changed lines are shown on every edit; `/diff` for the whole session |
1050
1528
  | **Scope** | Outside the starting folder is refused, even if the model insists |
1051
1529
  | **Blocked commands** | Only irreversible ones (disk format, recursive delete, `--force` push) |
@@ -1065,6 +1543,37 @@ Undo history stores whole file contents, so repeated edits to large files add up
1065
1543
  it keeps the **most recent 50 turns** and drops the rest. What you just did is always
1066
1544
  undoable; `/status` shows how large the history currently is.
1067
1545
 
1546
+ ### Files removed through `Bash` come back too
1547
+
1548
+ The safety net covered `Write` and `Edit` only. But a model moving a file reaches for
1549
+ `Bash` — `mv old.js new.js`, `rm temp.txt`. The file was gone and `/undo` could do
1550
+ nothing about it. It was half a safety net.
1551
+
1552
+ Now a mutating command snapshots the files it names beforehand, and **says what it saved,
1553
+ right there**.
1554
+
1555
+ ```
1556
+ ▶ Bash(mv src/old.js src/new.js)
1557
+ └ 성공
1558
+ ↩ src/old.js 는 떠 뒀습니다 — /undo 로 되돌아갑니다
1559
+ (saved src/old.js — /undo restores it)
1560
+ ```
1561
+
1562
+ `mv` and `rm` leave one "success" line on screen. From that line alone there is no way to
1563
+ tell whether it is reversible, so people either assume it is and move on, or assume it is
1564
+ not and get scared. So the fact is stated.
1565
+
1566
+ **What cannot be saved is not hidden.** Shell-expanded wildcards (`rm *.tmp`), deletions
1567
+ inside a script, and whole directories are invisible here. In those cases the `↩` line
1568
+ simply does not appear — **it never claims "everything is reversible"**. False reassurance
1569
+ means people stop checking.
1570
+
1571
+ Snapshotting casts a **wider** net than blocking does. The scope guard (`checkPaths`) only
1572
+ treats words containing a slash as paths — anything else would block legitimate commands —
1573
+ but `del target.txt`, with no slash, is the most common form there is. This is a reading
1574
+ site rather than a blocking one, so it scans broadly and saves a file only when one is
1575
+ actually there. A wrong guess costs nothing. Up to 24 per command.
1576
+
1068
1577
  ### What it will not read
1069
1578
 
1070
1579
  Walking a folder turns up things that are not project files: the private stores other coding
@@ -1226,7 +1735,7 @@ If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as
1226
1735
  ## Development
1227
1736
 
1228
1737
  ```bash
1229
- npm test Full suite (1,787 checks)
1738
+ npm test Full suite (1,832 checks)
1230
1739
  npm run coverage Which lines the tests actually execute
1231
1740
  npm run verify Import + network checks only
1232
1741
  npm run bench Edit success rate
@@ -1280,7 +1789,7 @@ Zero dependencies rules out c8 and nyc, so this reads Node's own
1280
1789
  `NODE_V8_COVERAGE` instead — nothing new to get through an import review. It picks up
1281
1790
  child processes too, so the `cli` suite that spawns `deel` counts like everything else.
1282
1791
 
1283
- Currently **92% overall** (6,911 of 7,496 lines). Three files are deliberately left short.
1792
+ Currently **92% overall** (7,056 of 7,646 lines). Three files are deliberately left short.
1284
1793
 
1285
1794
  | File | Now | Why it stops there |
1286
1795
  |---|---|---|