deel-local-cli 0.9.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +470 -27
- package/README.md +481 -31
- package/bin/deel.js +234 -186
- package/package.json +3 -2
- package/src/agent/compact.js +131 -138
- package/src/agent/effort.js +44 -11
- package/src/agent/loop.js +574 -251
- package/src/agent/memory.js +152 -0
- package/src/agent/mention.js +164 -0
- package/src/agent/modes.js +11 -3
- package/src/agent/recall.js +209 -0
- package/src/agent/salvage.js +182 -0
- package/src/agent/session.js +140 -10
- package/src/agent/store.js +36 -11
- package/src/backend/adapter.js +266 -183
- package/src/backend/ctxsize.js +133 -61
- package/src/backend/detect.js +10 -1
- package/src/backend/learn.js +102 -0
- package/src/backend/mcp.js +304 -0
- package/src/commands.js +493 -53
- package/src/oneshot.js +327 -0
- package/src/repl.js +787 -479
- package/src/report.js +19 -5
- package/src/safety/guard.js +123 -7
- package/src/safety/undo.js +96 -11
- package/src/tools/encoding.js +24 -2
- package/src/tools/fsutil.js +70 -0
- package/src/tools/index.js +471 -27
- package/src/tools/webfetch.js +37 -1
- package/src/ui/ansi.js +13 -1
- package/src/ui/diff.js +255 -0
- package/src/ui/inputbox.js +184 -0
- package/src/ui/level.js +3 -1
- package/src/ui/prompt.js +13 -1
- package/src/ui/screen.js +214 -0
- package/src/ui/status.js +81 -17
- package/src/ui/wrap.js +68 -0
- package/src/version.js +28 -0
package/README.en.md
CHANGED
|
@@ -23,7 +23,7 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
|
|
|
23
23
|
│ This PC 337 skills · 127 commands · 42 plugins │
|
|
24
24
|
╰──────────────────────────────────────────────────────────────╯
|
|
25
25
|
|
|
26
|
-
▏myproject
|
|
26
|
+
▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱▱▱ 22% 28k/128k ▏ ◎ 종합 · ◇ medium·절약 · auto
|
|
27
27
|
❯ unify the logging style
|
|
28
28
|
|
|
29
29
|
❊ Grep(console.log)
|
|
@@ -33,7 +33,7 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
|
|
|
33
33
|
◈ Edit(src/runner.js)
|
|
34
34
|
└ 1 spot
|
|
35
35
|
|
|
36
|
-
Unified log calls to the logger format. One change in runner.js.
|
|
36
|
+
▌ Unified log calls to the logger format. One change in runner.js.
|
|
37
37
|
|
|
38
38
|
── 4.2s · 3 tools · ↑3,900 ↓180
|
|
39
39
|
```
|
|
@@ -55,6 +55,7 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
|
|
|
55
55
|
- [Reasoning effort](#reasoning-effort)
|
|
56
56
|
- [Auto-compaction](#auto-compaction)
|
|
57
57
|
- [Resuming a conversation](#resuming-a-conversation)
|
|
58
|
+
- [Attaching tools from outside (MCP)](#attaching-tools-from-outside-mcp)
|
|
58
59
|
- [Safety](#safety)
|
|
59
60
|
- [Corporate review package](#corporate-review-package)
|
|
60
61
|
- [Configuration](#configuration)
|
|
@@ -211,16 +212,20 @@ Names follow Claude Code / Codex conventions.
|
|
|
211
212
|
| `/help` | Command list |
|
|
212
213
|
| `/context` | What is consuming the context window |
|
|
213
214
|
| `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
|
|
215
|
+
| `/out [number\|auto]` | Cap on a **single reply** — raise it when large files get cut |
|
|
214
216
|
| `/compact` | Summarise and fold older turns |
|
|
215
217
|
| `/clear` | Clear the conversation (keeps link and rules) |
|
|
216
218
|
| `/model` | Switch connection / model |
|
|
217
|
-
| `/think <level
|
|
219
|
+
| `/think <level>` | Reasoning level (`off·low·medium·high·max`) |
|
|
220
|
+
| `/think 배분 <profile>` | Per-stage profile (`even·save·deep`) |
|
|
221
|
+
| `/think 자세히` | Stage table — which stage runs at which level and cap |
|
|
218
222
|
| `/mode <mode>` | Approval policy — how much it asks (`auto` · `confirm` · `strict`) |
|
|
219
223
|
| `/work [mode]` | Work mode — what kind of work you are doing |
|
|
220
224
|
| `/auto` | Hand the wheel back — it picks the mode from what you type |
|
|
221
225
|
| `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
|
|
222
226
|
| `/level [level]` | How much to show (`쉬움` simple · `개발자` developer) |
|
|
223
227
|
| `/undo [turns]` | Revert file changes |
|
|
228
|
+
| `/diff [file]` | Files changed this session, and the changed lines |
|
|
224
229
|
| `/tools` | Available tools |
|
|
225
230
|
| `/skills [query\|all\|off]` | Browse, search, load skills |
|
|
226
231
|
| `/plugin [install\|remove\|pack]` | Manage plugins |
|
|
@@ -228,6 +233,9 @@ Names follow Claude Code / Codex conventions.
|
|
|
228
233
|
| `/status` | Connection status |
|
|
229
234
|
| `/scan [save]` | Sweep this machine for local model servers (`save` registers them) |
|
|
230
235
|
| `/sessions` | Past conversations in this folder |
|
|
236
|
+
| `/recall <text>` | Search past conversations **by content** |
|
|
237
|
+
| `/memory` | What persists across sessions — view, add, delete |
|
|
238
|
+
| `/mcp` | Externally attached tools (MCP servers) |
|
|
231
239
|
| `/init` | Create a `DEEL.md` rules file |
|
|
232
240
|
| `/exit` | Quit |
|
|
233
241
|
|
|
@@ -237,6 +245,43 @@ Discovered plugin commands are invoked as `/<plugin>:<name>`, with `$ARGUMENTS`
|
|
|
237
245
|
server or loaded a different model, `/scan save` then `/model` switches over without losing
|
|
238
246
|
the conversation.
|
|
239
247
|
|
|
248
|
+
### Attaching a file with `@`
|
|
249
|
+
|
|
250
|
+
Write `@` followed by a path and that file is sent along with your message.
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
❯ @src/a.js why is this slow?
|
|
254
|
+
◧ attached src/a.js
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
That removes **one round-trip** — the one where the model has to call `Read` itself. Local
|
|
258
|
+
models are weak at tool calling and that round-trip often misfires: it calls the wrong path,
|
|
259
|
+
or skips the call and invents the contents. You already know which file it is; there is no
|
|
260
|
+
reason to make the model go looking.
|
|
261
|
+
|
|
262
|
+
The hard part is not attaching. It is **not mistaking something else for a file.**
|
|
263
|
+
Things starting with `@` are everywhere.
|
|
264
|
+
|
|
265
|
+
| What you type | What happens |
|
|
266
|
+
|---|---|
|
|
267
|
+
| `@src/a.js` | Attached — only when the path actually exists |
|
|
268
|
+
| `hong@example.com` | Not a mention at all. A letter before the `@` means it is an address |
|
|
269
|
+
| `@media` · `@dataclass` · `@scope/pkg` | Left alone — no such path exists |
|
|
270
|
+
| `@src/` (a directory) | A listing of what is inside is attached |
|
|
271
|
+
| `@"draft report.txt"` | Quote names containing spaces |
|
|
272
|
+
| Outside the working scope | Refused, and the refusal is printed |
|
|
273
|
+
| A CP949 corporate document | Decoded by content and attached correctly |
|
|
274
|
+
|
|
275
|
+
There is one rule: **attach only when the path really exists.** Otherwise the text is left
|
|
276
|
+
exactly as typed, silently — it probably wasn't a mention.
|
|
277
|
+
|
|
278
|
+
Attachments are capped at **25% of the context length** (20,000 tokens at most). Anything larger is attached from the
|
|
279
|
+
top only, and a truncated file is **not marked as already read.** Marking it read would let the
|
|
280
|
+
model edit a part it never saw. Only a fully attached file lets it skip `Read`.
|
|
281
|
+
|
|
282
|
+
Every attachment is announced on screen. Text the user did not type is now in the conversation;
|
|
283
|
+
not showing it would also leave them wondering where the context went.
|
|
284
|
+
|
|
240
285
|
### Interrupting
|
|
241
286
|
|
|
242
287
|
Press **Ctrl+C** to stop the model mid-answer when it is heading the wrong way.
|
|
@@ -349,6 +394,52 @@ Two things matter here:
|
|
|
349
394
|
- **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
|
|
350
395
|
blocking are identical. A beginner needs the undo more, not less.
|
|
351
396
|
|
|
397
|
+
### The input box
|
|
398
|
+
|
|
399
|
+
Launched in a terminal, **the conversation scrolls normally and an input box is pinned at
|
|
400
|
+
the bottom.** Only the box is erased and redrawn — nothing above it is touched.
|
|
401
|
+
|
|
402
|
+
```
|
|
403
|
+
❊ Grep(console.log)
|
|
404
|
+
└ 3 files · 11 hits
|
|
405
|
+
◈ Edit(src/runner.js)
|
|
406
|
+
└ 1 spot +3-1
|
|
407
|
+
- 12 console.log('시작', 이름)
|
|
408
|
+
+ 12 logger.info({ 단계: '시작', 이름 })
|
|
409
|
+
|
|
410
|
+
▌ Unified log calls to the logger format. One change in runner.js.
|
|
411
|
+
|
|
412
|
+
── 4.2s · 3 tools · ↑3,900 ↓180
|
|
413
|
+
|
|
414
|
+
▏myproject · qwen2.5-coder:7b ▏ ▰▰▱▱▱▱▱▱ 22% ▏ ◎ 종합 · ◇ medium · auto
|
|
415
|
+
╭─────────────────────────────────────────────────────────────────────────────╮
|
|
416
|
+
│ ❯ also shrink the aggregate helpers │
|
|
417
|
+
╰─────────────────────────────────────────────────────────────────────────────╯
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
Terminal scrollback, selection and `Ctrl+F` all keep working, because the conversation is
|
|
421
|
+
never trapped inside a pane of ours. Long input grows the box to as many lines as it needs.
|
|
422
|
+
|
|
423
|
+
**It switches itself off where it would do harm.** Piped or redirected output, `CI` set,
|
|
424
|
+
`TERM=dumb`, or a window under 40 columns: no box, no asking. `deel … | tee log.txt` must
|
|
425
|
+
not become a pile of escape codes. Passing `--tui` does not override a pipe, and `--no-tui`
|
|
426
|
+
turns it off at any time.
|
|
427
|
+
|
|
428
|
+
Line editing stays entirely with Node's readline — Korean IME composition, paste, history,
|
|
429
|
+
Ctrl+A/E, backspace. We only *draw* the string readline is holding. Hand-rolling a line
|
|
430
|
+
editor is how you break IME input first.
|
|
431
|
+
|
|
432
|
+
> **A path taken and abandoned** — the first version borrowed the whole terminal (an
|
|
433
|
+
> alternate screen, like vim) and split it into conversation, changed-files and todo panes.
|
|
434
|
+
> It looked the part, and **every slash command went dead.** Six modules including
|
|
435
|
+
> `commands.js` write straight to the terminal rather than through the screen object, and a
|
|
436
|
+
> full repaint erased their output the instant it appeared. Not "the command didn't run" —
|
|
437
|
+
> "you can't see that it ran", which is worse. Fixing it would mean threading every one of
|
|
438
|
+
> those six through the screen object, plus every one added later, with a silent return of
|
|
439
|
+
> the same symptom if one is missed. So the design went the other way: let the conversation
|
|
440
|
+
> flow, manage only the box. ([`test/box.test.js`](test/box.test.js) spawns a child that
|
|
441
|
+
> pretends to be a terminal, so this one cannot ship again.)
|
|
442
|
+
|
|
352
443
|
---
|
|
353
444
|
|
|
354
445
|
## Tools
|
|
@@ -359,14 +450,95 @@ Names and arguments match Claude Code, so skills written for that convention wor
|
|
|
359
450
|
|---|---|
|
|
360
451
|
| `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV**) |
|
|
361
452
|
| `Write` | Write / overwrite a file |
|
|
453
|
+
| `Append` | Append to the end of a file — **how large files get written in pieces** |
|
|
362
454
|
| `Edit` | Replace an exact string (`replace_all` supported) |
|
|
363
455
|
| `Glob` | Find files by name pattern |
|
|
364
456
|
| `Grep` | Regex search file contents |
|
|
365
457
|
| `Bash` | Run a command |
|
|
366
458
|
| `Skill` | Expand a skill body (shown to the model only when skills exist) |
|
|
367
459
|
| `WebFetch` | Read a web page (read-only; hidden under `--offline`) |
|
|
460
|
+
| `Recall` | Search **past conversations** — the model digs up "that thing last time" itself |
|
|
461
|
+
| `Remember` | One line that outlives the session — known from the start next time |
|
|
368
462
|
| `TodoWrite` | Checklist — breaks long work into steps and shows progress |
|
|
369
463
|
|
|
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`).
|
|
467
|
+
|
|
468
|
+
### Finding past conversations, and remembering decisions
|
|
469
|
+
|
|
470
|
+
deel writes every conversation to `.deel/sessions/*.jsonl`. Until now all you could do was
|
|
471
|
+
list them — **a record you cannot search is the same as no record.**
|
|
472
|
+
|
|
473
|
+
```
|
|
474
|
+
$ /recall 인코딩을 어떻게
|
|
475
|
+
|
|
476
|
+
2026-08-01 10:15 모델 20260801-101500
|
|
477
|
+
CP949 인코딩 문제입니다. 읽을 때 인코딩을 재서 그대로 되돌려 쓰도록…
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Korean particles are handled: `인코딩을` also matches `인코딩`. A morphological analyser is
|
|
481
|
+
out of the question (zero dependencies), so particle-looking tails are stripped and **both**
|
|
482
|
+
forms are searched. No index is built — an index inevitably goes stale, and **a stale index is
|
|
483
|
+
worse than none** ("not found" reads as "never happened"). Instead every search reports how
|
|
484
|
+
much it read and what it could not.
|
|
485
|
+
|
|
486
|
+
`Recall` is also a **tool**. Left as a human-only command, "do it the way we decided last
|
|
487
|
+
time" leaves the model nothing to do but ask again.
|
|
488
|
+
|
|
489
|
+
**Memory (`/memory`) is a different thing.** Recall has to be *searched*; memory is *already
|
|
490
|
+
there*. Things you cannot re-explain every session go here.
|
|
491
|
+
|
|
492
|
+
```
|
|
493
|
+
$ /memory
|
|
494
|
+
|
|
495
|
+
1 사내 문서는 CP949 로 읽고 CP949 로 되돌려 쓴다
|
|
496
|
+
2 검증할 때 7080 포트는 쓰지 않는다
|
|
497
|
+
|
|
498
|
+
2줄 · 약 30토큰이 매 요청마다 함께 나갑니다
|
|
499
|
+
파일 .deel/memory.md — 직접 고치셔도 됩니다
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
`.deel/memory.md` is **prose a human edits**, not a database. That matters: a line the model
|
|
503
|
+
got wrong ships on every request and keeps being wrong. **A wrong memory is worse than none.**
|
|
504
|
+
So `/memory 지우기 2` deletes one.
|
|
505
|
+
|
|
506
|
+
Because it ships on every request it is bounded: 400 chars per line, 60 lines, 6,000 chars
|
|
507
|
+
total. Overflow drops the oldest and says so. `/context` shows its line count and tokens.
|
|
508
|
+
|
|
509
|
+
### Repeatable procedures become skills
|
|
510
|
+
|
|
511
|
+
When a multi-step job finishes and it is something that will come up again, the model writes
|
|
512
|
+
the procedure to `.deel/skills/<name>/SKILL.md`. Next session it appears in the skill list;
|
|
513
|
+
when it turns out to be wrong somewhere, the model edits that file.
|
|
514
|
+
|
|
515
|
+
No new tool needed — the existing `Write` writes it and the existing skill sweep reads it.
|
|
516
|
+
|
|
517
|
+
### Large files are written in pieces — `Append`
|
|
518
|
+
|
|
519
|
+
A model with a 4k output cap still has to be able to write a 2,000-line file, eight pieces at
|
|
520
|
+
a time. Stitching with `Edit` does not work in practice — HTML repeats anchors like `</div>`,
|
|
521
|
+
so the match comes back as "found in several places", and a longer anchor eats the tokens that
|
|
522
|
+
should have gone into the body.
|
|
523
|
+
|
|
524
|
+
`Write` to create, `Append` to continue. Encoding follows `Write` (CP949 for corporate
|
|
525
|
+
documents, the BOM on a `.csv` is preserved). The undo snapshot is taken **only on the first
|
|
526
|
+
`Append`** — eight appends must not leave eight copies in the history, or there is no single
|
|
527
|
+
point to revert to.
|
|
528
|
+
|
|
529
|
+
```
|
|
530
|
+
⏺ Write(dashboard.html)
|
|
531
|
+
└ ⚠ wrote only as far as it arrived — 632 lines
|
|
532
|
+
↻ the reply hit the cap — retrying with 9,984 → 16,384
|
|
533
|
+
⏺ Append(dashboard.html)
|
|
534
|
+
└ +567 lines · 1,199 total
|
|
535
|
+
|
|
536
|
+
✓ dashboard.html · 1,199 lines · 97.7KB
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
That last line matters. If the file does not exist and the model says "created it", you would
|
|
540
|
+
believe it. **The real file is measured at the end of the turn.**
|
|
541
|
+
|
|
370
542
|
### Checklists
|
|
371
543
|
|
|
372
544
|
Keeps the model from losing its place on multi-step work. The list is redrawn whenever the
|
|
@@ -417,6 +589,65 @@ Measured with `npm run bench`:
|
|
|
417
589
|
|
|
418
590
|
On failure it points at the closest line in the file.
|
|
419
591
|
|
|
592
|
+
### It shows you what it changed
|
|
593
|
+
|
|
594
|
+
`auto` mode edits without asking. That is the speed of this tool — but if all that reaches the
|
|
595
|
+
screen is `1 spot`, you move on without knowing what happened. Undo is the safety net, and
|
|
596
|
+
**you cannot decide whether to undo something you never saw.** So every `Edit` and `Write` is
|
|
597
|
+
followed by the changed lines themselves.
|
|
598
|
+
|
|
599
|
+
```
|
|
600
|
+
◈ Edit(src/runner.js)
|
|
601
|
+
└ 1 spot +1 −2
|
|
602
|
+
|
|
603
|
+
11 const id = job.id;
|
|
604
|
+
- console.log("start: " + id);
|
|
605
|
+
- console.log(" opts " + JSON.stringify(opts));
|
|
606
|
+
+ 12 logger.info('start', { id, opts });
|
|
607
|
+
13 return run(job);
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
The `+1 −2` next to the summary is how many lines were added and removed.
|
|
611
|
+
|
|
612
|
+
- **Removed lines carry no line number.** They no longer exist in the file. Printing the old
|
|
613
|
+
number put it directly under a context line's new number — two different files' numbering in
|
|
614
|
+
one column. Line 8 really did appear twice on screen.
|
|
615
|
+
- If only the line endings changed (CRLF/LF), it says so. Otherwise every visually identical
|
|
616
|
+
line shows as changed and the real edit is impossible to find.
|
|
617
|
+
- Large files are compared after trimming the identical head and tail. If it is still too big,
|
|
618
|
+
exact matching is abandoned for "this whole block changed" — a rough answer now beats an
|
|
619
|
+
exact one later.
|
|
620
|
+
|
|
621
|
+
How many lines are shown depends on the level. Forty lines at someone's first launch means none get read.
|
|
622
|
+
|
|
623
|
+
| | Simple | Developer |
|
|
624
|
+
|---|---|---|
|
|
625
|
+
| After a tool call | 14 lines | 40 lines |
|
|
626
|
+
| `/diff <file>` | 60 lines | 200 lines |
|
|
627
|
+
|
|
628
|
+
### `/diff` — everything changed this session
|
|
629
|
+
|
|
630
|
+
Those lines scroll away. `/diff` collects every file touched this session onto one page.
|
|
631
|
+
|
|
632
|
+
```
|
|
633
|
+
$ /diff
|
|
634
|
+
|
|
635
|
+
── files changed this session ─────────────────────────────
|
|
636
|
+
src/runner.js +12 −7 3×
|
|
637
|
+
src/logger.js +40 −0
|
|
638
|
+
──────────────────────────────────────────────────────────
|
|
639
|
+
2 files +52 −7
|
|
640
|
+
|
|
641
|
+
/diff <file> for detail, /undo to revert
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
`/diff <file>` compares **the state at the start of the session against now.** Even after
|
|
645
|
+
three edits, what you want to know is "what is different from before I asked", not what the
|
|
646
|
+
last edit did. That original state comes from the earliest undo snapshot.
|
|
647
|
+
|
|
648
|
+
`/diff` is **in the simple level's command list.** As long as `auto` edits without asking,
|
|
649
|
+
a beginner needs a way to see what changed more than anyone.
|
|
650
|
+
|
|
420
651
|
---
|
|
421
652
|
|
|
422
653
|
## Korean text and Excel
|
|
@@ -453,6 +684,12 @@ Newly created files are UTF-8.
|
|
|
453
684
|
Command output is handled the same way. A Windows console is not UTF-8, so taking `Bash`
|
|
454
685
|
output as utf8 garbles non-ASCII text. It is collected as bytes and decoded afterwards.
|
|
455
686
|
|
|
687
|
+
**Undo snapshots are stored as bytes too.** They used to be stored as UTF-8 text, so undoing
|
|
688
|
+
a CP949 file brought back `가나다` (bytes `b0a1 b3aa b4d9`) as six U+FFFD characters — **the
|
|
689
|
+
safety net itself destroyed the original bytes.** Now every snapshot is round-tripped through
|
|
690
|
+
UTF-8 first; anything that does not come back identical is stored as base64 and restored
|
|
691
|
+
byte-exact.
|
|
692
|
+
|
|
456
693
|
### Excel — read as CSV
|
|
457
694
|
|
|
458
695
|
An Excel file is a compressed archive, not text, so normally you get "this is a binary file"
|
|
@@ -539,15 +776,14 @@ with a licence table — ready to hand to a security reviewer.
|
|
|
539
776
|
One answer means several model calls, and **each needs a different amount of thinking.**
|
|
540
777
|
All-high is slow; all-low wanders off.
|
|
541
778
|
|
|
779
|
+
The default is **one line**. What you want to know is how hard it is thinking right now,
|
|
780
|
+
not a stage table.
|
|
781
|
+
|
|
542
782
|
```
|
|
543
783
|
$ /think
|
|
544
784
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
Stage Effort Cap When
|
|
548
|
-
first call · medium 4,096 deciding what to do
|
|
549
|
-
continuing ↓ low 2,048 reading a tool result, picking the next step
|
|
550
|
-
stuck ↑ high 4,096 the previous tool errored
|
|
785
|
+
추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
|
|
786
|
+
더 세게 /think high 더 빠르게 /think low
|
|
551
787
|
```
|
|
552
788
|
|
|
553
789
|
| Profile | Character |
|
|
@@ -556,6 +792,31 @@ $ /think
|
|
|
556
792
|
| `save` (default) | Hard on the first decision only |
|
|
557
793
|
| `deep` | Everything one notch up — for hard work |
|
|
558
794
|
|
|
795
|
+
Set the profile with `/think 배분 절약`. **Level and profile are different axes, so the
|
|
796
|
+
commands were split** — `/think high` and `/think save` used to set different things under
|
|
797
|
+
one name, which made the screen unreadable.
|
|
798
|
+
|
|
799
|
+
The stage table moved to `/think 자세히` (the default at developer level).
|
|
800
|
+
|
|
801
|
+
```
|
|
802
|
+
$ /think 자세히
|
|
803
|
+
|
|
804
|
+
추론 강도 medium (첫 판단 medium · 이어가기 low · 막혔을 때 high)
|
|
805
|
+
배분 절약 첫 판단만 세게, 이어가기는 얕게 — 대개 이게 낫습니다
|
|
806
|
+
|
|
807
|
+
단계 강도 출력상한 언제
|
|
808
|
+
첫 판단 · medium 15,549 무엇을 할지 정하는 자리
|
|
809
|
+
이어가기 ↓ low 13,605 도구 결과를 읽고 다음 한 수
|
|
810
|
+
막혔을 때 ↑ high 16,384 직전 도구가 오류를 냄
|
|
811
|
+
|
|
812
|
+
출력 상한은 16,384 (모르는 값이라 기본값) 안에서 나눕니다 — /out
|
|
813
|
+
컨텍스트 40,960 · 지금 찬 양 2,087
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
That second-to-last line exists for a reason: **when all three caps are equal, it is the
|
|
817
|
+
only thing that says whether that is correct.** A low known cap makes them equal, and that
|
|
818
|
+
is fine. For a while all three read `16,384` always — which meant the table said nothing.
|
|
819
|
+
|
|
559
820
|
### Context length is read off the model
|
|
560
821
|
|
|
561
822
|
This one number sizes the whole program: how many files fit in one read, when the
|
|
@@ -595,29 +856,99 @@ the **loaded length is what deel uses**, and the maximum is reported separately.
|
|
|
595
856
|
| `/ctx` | Current length and remaining room |
|
|
596
857
|
| `/ctx auto` | Ask the server again and match the model |
|
|
597
858
|
| `/ctx 655360` | Set it yourself (`640k`, `128k`, `1m` also work) |
|
|
598
|
-
| `/ctx
|
|
859
|
+
| `/ctx 자세히` | Which endpoints were probed and what each returned — how to see why a lookup failed |
|
|
599
860
|
| `deel --ctx 655360` | Start at this value (skips the lookup) |
|
|
600
861
|
|
|
601
862
|
**`k` means 1024 here.** Context lengths are all powers of two, so that is the only base
|
|
602
863
|
that lines up: 655,360 is `640k`, not `655k`; 131,072 is `128k`, not `131k`. The display and
|
|
603
864
|
`/ctx` use the same unit, so typing back what you see gives you the same number.
|
|
604
865
|
|
|
866
|
+
### Reply length cap — `/out`
|
|
867
|
+
|
|
868
|
+
Context (how much can be held) and the **output cap** (how much can come back at once) are
|
|
869
|
+
different numbers. Treating them as one makes it impossible to understand why a large file
|
|
870
|
+
never gets written — the context is roomy while the reply is being cut.
|
|
871
|
+
|
|
872
|
+
| Command | What it does |
|
|
873
|
+
|---|---|
|
|
874
|
+
| `/out` | Current cap and **where it came from** (set by you / discovered / default) |
|
|
875
|
+
| `/out 32k` | Set it yourself (`k` is 1024). Saved to the profile |
|
|
876
|
+
| `/out auto` | Drop your value and go back to the discovered one, or the default |
|
|
877
|
+
| `deel --max-tokens 65536` | Start at this value |
|
|
878
|
+
|
|
879
|
+
The old name `/ctx out 32k` still works.
|
|
880
|
+
|
|
605
881
|
**Caps are not fixed numbers.** They are computed from the model's context window and how
|
|
606
882
|
much of it is currently used — the profile decides what share of the remaining room a stage gets.
|
|
607
883
|
|
|
608
|
-
| Model | First call | Continuing | Stuck |
|
|
609
|
-
|
|
610
|
-
| 2k local |
|
|
611
|
-
| 8k local | 2,
|
|
612
|
-
| 40k (qwen3) |
|
|
613
|
-
| 128k gateway | 16,384 | 16,384 | 16,384 |
|
|
614
|
-
| 128k, 80% full |
|
|
884
|
+
| Model | First call | Continuing | Stuck | Retry after truncation |
|
|
885
|
+
|---|---|---|---|---|
|
|
886
|
+
| 2k local | 819 | 716 | 921 | 1,638 |
|
|
887
|
+
| 8k local | 3,276 | 2,867 | 3,686 | 6,553 |
|
|
888
|
+
| 40k (qwen3) | 16,384 | 14,336 | 16,384 | 16,384 |
|
|
889
|
+
| 128k gateway | 16,384 | 16,384 | 16,384 | 16,384 |
|
|
890
|
+
| 128k, 80% full | 10,485 | 9,174 | 11,796 | 16,384 |
|
|
891
|
+
| 640k with `/out 65536` | 65,536 | 65,536 | 65,536 | 65,536 |
|
|
615
892
|
|
|
616
893
|
Caps shrink as the context fills. Handing a 4k model a 4,096-token cap would leave no room for input.
|
|
617
|
-
Raise the ceiling with `maxTokens` in the profile if you need more.
|
|
618
894
|
|
|
619
|
-
|
|
620
|
-
|
|
895
|
+
The last row is the point: **a known cap overrides the 16,384 default.** For a while it did
|
|
896
|
+
not — the third argument of `Math.min(cap, max ?? 16384, 16384)` clamped it right back, so a
|
|
897
|
+
configured cap could only be lowered, never raised. Meanwhile the comment, the README, and the
|
|
898
|
+
on-screen help all said it could be raised. A documented escape hatch that is welded shut is
|
|
899
|
+
the worst kind.
|
|
900
|
+
|
|
901
|
+
If a cap truncates a reply, **the call is retried with the cap lifted** — and the thinking
|
|
902
|
+
level drops one notch, because reasoning tokens eat the same budget first. Without that, more
|
|
903
|
+
headroom just buys more thinking. A truncated reply means a half-written tool call, which fails silently.
|
|
904
|
+
|
|
905
|
+
**When the server refuses, it is read for the answer.**
|
|
906
|
+
|
|
907
|
+
```
|
|
908
|
+
This model's maximum context length is 8192 tokens, however you requested 41003
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
The number is extracted, applied, and the call is retried. You never see the failure.
|
|
912
|
+
No spec knowledge is needed, so **this works against servers we have never seen.**
|
|
913
|
+
|
|
914
|
+
### Truncated tool calls
|
|
915
|
+
|
|
916
|
+
This actually happened. A user asked for a dashboard; the model tried to put an entire HTML
|
|
917
|
+
document into `Write`'s arguments, hit the output token limit, and the arguments JSON arrived
|
|
918
|
+
cut off mid-string.
|
|
919
|
+
|
|
920
|
+
The old code quietly turned that unparseable JSON into `{_raw: "..."}` and handed it to the
|
|
921
|
+
tool. The tool answered `path is empty` — **a message with nothing to do with the real cause.**
|
|
922
|
+
The model had not omitted the path, so there was nothing to fix; it retried identically, and
|
|
923
|
+
was truncated again.
|
|
924
|
+
|
|
925
|
+
```
|
|
926
|
+
◆ Write(dashboard.html)
|
|
927
|
+
└ path is empty ← nine identical times
|
|
928
|
+
|
|
929
|
+
── 71s · 13 tool calls · context filled and auto-compacted · no file produced
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
One silently swallowed value produced all of that. What happens now:
|
|
933
|
+
|
|
934
|
+
| | Now |
|
|
935
|
+
|---|---|
|
|
936
|
+
| Unparseable arguments | **Marked as truncated**, not swallowed. Never passed to the tool |
|
|
937
|
+
| The model is told | Exactly what happened, and to stop resending the whole thing — write a short skeleton first, then **build it up with `Edit`** |
|
|
938
|
+
| The truncated payload | Never re-injected into the conversation — it is half a payload and it costs context |
|
|
939
|
+
| Detecting truncation | Even when the gateway reports `finish_reason: "stop"`, **broken arguments are themselves proof it was cut.** No model writes half a JSON object on purpose |
|
|
940
|
+
| Three identical failures | The turn stops, with a suggestion to split the request |
|
|
941
|
+
|
|
942
|
+
```
|
|
943
|
+
⊘ Stopped — it is spinning in the same place.
|
|
944
|
+
the same tool call keeps getting truncated
|
|
945
|
+
What you asked for in one go is larger than the model's output cap. Try splitting it —
|
|
946
|
+
e.g. "just the skeleton first" → "now add the table" → "now add the chart"
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
**A step limit (`maxSteps`) cannot catch this.** It cannot tell a long healthy task from a
|
|
950
|
+
spinning one. What is counted here is not steps but **how many times the same tool failed for
|
|
951
|
+
the same reason.**
|
|
621
952
|
|
|
622
953
|
---
|
|
623
954
|
|
|
@@ -670,6 +1001,43 @@ covers `.deel/` so it never reaches a repository.
|
|
|
670
1001
|
|
|
671
1002
|
---
|
|
672
1003
|
|
|
1004
|
+
## Attaching tools from outside (MCP)
|
|
1005
|
+
|
|
1006
|
+
A corporate wiki search, an issue tracker, a DB query tool — if a team publishes one as an MCP
|
|
1007
|
+
server, deel uses it as a tool **without a code change**.
|
|
1008
|
+
|
|
1009
|
+
Configure in `.deel/mcp.json`. A Claude Code config can be copied over verbatim:
|
|
1010
|
+
|
|
1011
|
+
```json
|
|
1012
|
+
{ "mcpServers": { "wiki": { "command": "node", "args": ["wiki-mcp.js"] } } }
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
The model sees it as `mcp__wiki__search`. `/mcp` shows what is attached.
|
|
1016
|
+
|
|
1017
|
+
**Dependencies stay at zero.** The stdio transport is nothing but newline-delimited JSON-RPC
|
|
1018
|
+
2.0 over a child process's stdin/stdout, so `child_process` and `JSON` cover it. No SDK.
|
|
1019
|
+
|
|
1020
|
+
### But this is somebody else's program
|
|
1021
|
+
|
|
1022
|
+
This project exists because unapproved software is blocked. Turning on MCP carelessly would
|
|
1023
|
+
tear down that line with our own hands. So:
|
|
1024
|
+
|
|
1025
|
+
| | |
|
|
1026
|
+
|---|---|
|
|
1027
|
+
| **Off by default** | Nothing runs unless it is in `.deel/mcp.json` |
|
|
1028
|
+
| **Never under `--offline`** | We cannot police where a child process connects. **We do not claim to block what we cannot block** |
|
|
1029
|
+
| **Outside the working scope** | MCP servers do not honour our fence. The `/mcp` screen says so |
|
|
1030
|
+
| **Audited** | What was launched and what was called, in `.deel/audit.jsonl` |
|
|
1031
|
+
| **No key passthrough** | Our environment is not forwarded wholesale — a `DEEL_*` gateway key in someone else's process goes somewhere we cannot see |
|
|
1032
|
+
| **Not in read-only modes** | A tool named "search" can still write files. Handing an unknown to plan/architect mode would make that promise meaningless |
|
|
1033
|
+
| **24 tools per server** | Schemas ship on every request. Past that they are dropped, and **the drop is reported** |
|
|
1034
|
+
|
|
1035
|
+
One server crashing, hanging, or talking nonsense does not affect the others. Failures are not
|
|
1036
|
+
swallowed — the reason appears in the header, because a silent drop leaves "why is that tool
|
|
1037
|
+
missing?" unanswerable.
|
|
1038
|
+
|
|
1039
|
+
---
|
|
1040
|
+
|
|
673
1041
|
## Safety
|
|
674
1042
|
|
|
675
1043
|
Instead of approval prompts, the design makes things **reversible**. The default `auto` mode
|
|
@@ -678,10 +1046,13 @@ does not ask.
|
|
|
678
1046
|
| Mechanism | Detail |
|
|
679
1047
|
|---|---|
|
|
680
1048
|
| **Undo** | Snapshot before every write. `/undo` restores per turn |
|
|
1049
|
+
| **Change display** | The changed lines are shown on every edit; `/diff` for the whole session |
|
|
681
1050
|
| **Scope** | Outside the starting folder is refused, even if the model insists |
|
|
682
1051
|
| **Blocked commands** | Only irreversible ones (disk format, recursive delete, `--force` push) |
|
|
683
1052
|
| **No re-run** | A mutating command is never retried after failure |
|
|
684
1053
|
| **Interrupt** | Ctrl+C stops mid-answer and leaves the conversation valid |
|
|
1054
|
+
| **Spin guard** | Three identical failures stop the turn, with the reason |
|
|
1055
|
+
| **Not read** | Other tools' private stores, and deel's own logs and config (the key), are refused |
|
|
685
1056
|
| **Audit log** | Everything recorded in `.deel/audit.jsonl` |
|
|
686
1057
|
|
|
687
1058
|
| Mode | Asks when |
|
|
@@ -694,6 +1065,43 @@ Undo history stores whole file contents, so repeated edits to large files add up
|
|
|
694
1065
|
it keeps the **most recent 50 turns** and drops the rest. What you just did is always
|
|
695
1066
|
undoable; `/status` shows how large the history currently is.
|
|
696
1067
|
|
|
1068
|
+
### What it will not read
|
|
1069
|
+
|
|
1070
|
+
Walking a folder turns up things that are not project files: the private stores other coding
|
|
1071
|
+
tools keep — past conversations, command history, caches, and keys. They have nothing to do
|
|
1072
|
+
with the task, but once they appear in a listing the model reads them first.
|
|
1073
|
+
|
|
1074
|
+
```
|
|
1075
|
+
◧ Read(~/.deel/audit.jsonl) 77 lines
|
|
1076
|
+
◧ Read(~/.claude/history.jsonl) 35 lines
|
|
1077
|
+
```
|
|
1078
|
+
|
|
1079
|
+
The audit log is **this program's own record of what it just did.** Reading it back into the
|
|
1080
|
+
conversation makes the model chase its own shadow. It has nothing to do with what was asked,
|
|
1081
|
+
and it fills the context.
|
|
1082
|
+
|
|
1083
|
+
The config file is worse. `.deel/config.json` holds the gateway **API key.** Reading it puts
|
|
1084
|
+
that key into the conversation, sends it to the model, and writes it into the on-disk session
|
|
1085
|
+
log. It hands the key to the very service the key is for.
|
|
1086
|
+
|
|
1087
|
+
| Refused | Why |
|
|
1088
|
+
|---|---|
|
|
1089
|
+
| `.deel/config.json` | Contains the gateway key |
|
|
1090
|
+
| `.deel/audit.jsonl` · `.deel/sessions` · `.deel/history` | deel's own records — chasing its own shadow |
|
|
1091
|
+
| `.claude` `.codex` `.cursor` `.gemini` `.aider` `.continue` `.cline` `.roo` `.kilocode` `.windsurf` `.opencode` `.zed` `.trae` `.augment` `.qodo` `.tabnine` `.cody` `.sourcegraph` `.copilot` `.amazonq` `.junie` `.codeium` `.goose` `.crush` `.gptme` `.openhands` `.devin` | Other tools' private stores |
|
|
1092
|
+
| File-style leftovers like `.aider.chat.history.md` | Same reason |
|
|
1093
|
+
|
|
1094
|
+
Writing is blocked too, not just reading. Blocking only reads would still let the agent
|
|
1095
|
+
overwrite another tool's settings, and overwriting `.deel/config.json` destroys the connection.
|
|
1096
|
+
|
|
1097
|
+
**It blocks, it does not hide** — the refusal says exactly why. New tools keep appearing;
|
|
1098
|
+
when a name is missing from the list, **adding it is one line.**
|
|
1099
|
+
|
|
1100
|
+
That list lives in **exactly one place in the source.** The directory walker (`SKIP_DIRS`) and
|
|
1101
|
+
the read guard look at the same set. They used to be two copies, and two copies means the day
|
|
1102
|
+
comes when only one of them learns a new name — a folder that is skipped while walking but
|
|
1103
|
+
readable if you name it directly, which is very hard to explain.
|
|
1104
|
+
|
|
697
1105
|
---
|
|
698
1106
|
|
|
699
1107
|
## Corporate review package
|
|
@@ -780,11 +1188,14 @@ deel --root <folder> Working scope. Defaults to the current folder
|
|
|
780
1188
|
deel --mode <mode> auto (default) / confirm / strict
|
|
781
1189
|
deel --work <mode> auto (default) / code / plan / architect / debug / ask / orchestrator
|
|
782
1190
|
deel --level <level> 쉬움 (simple) / 개발자 (developer)
|
|
1191
|
+
deel --ctx <length> Set the context length yourself (655360 · 640k · 128k)
|
|
1192
|
+
deel --max-tokens <len> Cap on a single reply (32k) — same value as /out
|
|
783
1193
|
deel --think <level> off / low / medium (default) / high / max
|
|
784
1194
|
deel --effort <profile> even / save (default) / deep
|
|
785
1195
|
deel --offline Nothing leaves this machine
|
|
786
1196
|
deel --continue Resume the most recent conversation
|
|
787
1197
|
deel --resume <id> Resume a specific one
|
|
1198
|
+
deel --no-tui Turn the input box off; plain scrolling view (see below)
|
|
788
1199
|
```
|
|
789
1200
|
|
|
790
1201
|
### Project rules
|
|
@@ -805,7 +1216,9 @@ If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as
|
|
|
805
1216
|
| 401 / 403 | Wrong key or auth header style (four are tried automatically) |
|
|
806
1217
|
| `address not permitted` | The lock did its job — pick a connection with `/model` |
|
|
807
1218
|
| Tool calls don't work | Run `deel diagnose`. Small models (1B–3B) often can't |
|
|
808
|
-
| Empty replies |
|
|
1219
|
+
| Empty replies | The server ignores streaming. deel retries once, then turns streaming off for the session |
|
|
1220
|
+
| Large files cut off mid-write | Check `/out` and raise it — the cap may be sitting at the 16,384 default because it could not be discovered |
|
|
1221
|
+
| Only `HTTP 400` shows | The server's own message is shown verbatim. If it is a length problem the number is read and applied automatically |
|
|
809
1222
|
| `deel scan` finds nothing | Server is off or on another port — use `--ports` |
|
|
810
1223
|
|
|
811
1224
|
---
|
|
@@ -813,11 +1226,12 @@ If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as
|
|
|
813
1226
|
## Development
|
|
814
1227
|
|
|
815
1228
|
```bash
|
|
816
|
-
npm test
|
|
817
|
-
npm run
|
|
818
|
-
npm run
|
|
819
|
-
npm run
|
|
820
|
-
npm run
|
|
1229
|
+
npm test Full suite (1,787 checks)
|
|
1230
|
+
npm run coverage Which lines the tests actually execute
|
|
1231
|
+
npm run verify Import + network checks only
|
|
1232
|
+
npm run bench Edit success rate
|
|
1233
|
+
npm run demo See what the UI actually looks like
|
|
1234
|
+
npm run check Syntax check every file
|
|
821
1235
|
```
|
|
822
1236
|
|
|
823
1237
|
Tests run against a **fake gateway**, so the loop, streaming, tool execution, undo and compaction
|
|
@@ -834,17 +1248,46 @@ so one run tells you everything.
|
|
|
834
1248
|
|---|---|---|
|
|
835
1249
|
| `smoke` | 20 | Tools, scope, undo, audit log |
|
|
836
1250
|
| `loop` | 16 | Agent loop, streaming, tool calls |
|
|
1251
|
+
| `guard` | 24 | **What it refuses to do** — denied edits, unknown tools, repeated mutations, out-of-scope writes |
|
|
837
1252
|
| `network` | 30 | Nothing escapes the configured address |
|
|
838
1253
|
| `web` | 25 | Web reads stay read-only |
|
|
839
1254
|
| `abort` | 16 | Ctrl+C leaves the conversation valid |
|
|
840
1255
|
| `parallel` | 23 | Read-only tools run together; checklists |
|
|
1256
|
+
| `cli` | 75 | **Spawns the real `deel`** and drives it to completion |
|
|
1257
|
+
| `setup` | 42 | First-run wizard, driven through a fake TTY |
|
|
1258
|
+
| `detect` | 66 | Identifying shape and auth from one address |
|
|
1259
|
+
| `modes` · `route` | 89 · 33 | Work modes; auto-switching from Auto |
|
|
1260
|
+
| `ctxsize` | 43 | Reading context length off the model |
|
|
1261
|
+
| `commands` · `commands-more` | 128 · 62 | Every slash command |
|
|
1262
|
+
| `ui` · `ui2` | 60 · 40 | Password masking, CJK width, status line, session list, Excel→text |
|
|
1263
|
+
| `encoding` · `xlsx` | 68 · 72 | Legacy-encoding detection; Excel reading |
|
|
841
1264
|
| `compact` | 21 | Summary folding, pairing intact, graceful fallback |
|
|
842
1265
|
| `store` | 34 | Session persistence, resume, crash recovery |
|
|
843
|
-
| `scan` |
|
|
1266
|
+
| `scan` | 29 | Distinguishing multiple runtimes |
|
|
844
1267
|
| `plugins` | 38 | Plugin fetch/pack, ZIP/TAR |
|
|
845
1268
|
| `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
|
|
846
1269
|
| `edit-bench` | 20 cases | Edit success rate |
|
|
847
1270
|
|
|
1271
|
+
### Coverage
|
|
1272
|
+
|
|
1273
|
+
```bash
|
|
1274
|
+
npm run coverage Summary
|
|
1275
|
+
node test/coverage.mjs --file src/repl.js One file in detail
|
|
1276
|
+
node test/coverage.mjs --json Machine-readable
|
|
1277
|
+
```
|
|
1278
|
+
|
|
1279
|
+
Zero dependencies rules out c8 and nyc, so this reads Node's own
|
|
1280
|
+
`NODE_V8_COVERAGE` instead — nothing new to get through an import review. It picks up
|
|
1281
|
+
child processes too, so the `cli` suite that spawns `deel` counts like everything else.
|
|
1282
|
+
|
|
1283
|
+
Currently **92% overall** (6,911 of 7,496 lines). Three files are deliberately left short.
|
|
1284
|
+
|
|
1285
|
+
| File | Now | Why it stops there |
|
|
1286
|
+
|---|---|---|
|
|
1287
|
+
| `tools/excel.js` | 67% | The password path needs Excel installed and a genuinely encrypted file. Faking it would produce a test that only *looks* like it passes |
|
|
1288
|
+
| `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) |
|
|
1289
|
+
| `plugins/manage.js` | 79% | The GitHub download path. **Tests not reaching the network** matters more. Folder installs are covered |
|
|
1290
|
+
|
|
848
1291
|
---
|
|
849
1292
|
|
|
850
1293
|
## Licence
|