deel-local-cli 1.10.0 → 1.12.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.md CHANGED
@@ -1,1223 +1,1224 @@
1
- <div align="center">
2
-
3
- <picture>
4
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/hero-en-dark.svg">
5
- <img alt="deel — stays on this machine" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/hero-en-light.svg" width="620">
6
- </picture>
7
-
8
- ### A coding-agent CLI that runs on local models and private gateways
9
-
10
- Zero dependencies · Node 20+ · Exactly one place your source can go
11
-
12
- Vendor APIs connect too — **only when you say so**
13
-
14
- <br>
15
-
16
- [![npm](https://img.shields.io/npm/v/deel-local-cli?logo=npm&logoColor=white&label=npm&color=cb3837)](https://www.npmjs.com/package/deel-local-cli)
17
- [![downloads](https://img.shields.io/npm/dt/deel-local-cli?label=downloads&color=1a7f37)](https://www.npmjs.com/package/deel-local-cli)
18
- [![node](https://img.shields.io/node/v/deel-local-cli?logo=nodedotjs&logoColor=white&label=node&color=5FA04E)](https://nodejs.org)
19
- [![license](https://img.shields.io/npm/l/deel-local-cli?label=license&color=0969da)](LICENSE)
20
-
21
- [![Node.js CI](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/test.yml?branch=main&logo=github&logoColor=white&label=Node.js%20CI)](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
22
- [![CodeQL](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/codeql.yml?branch=main&logo=github&logoColor=white&label=CodeQL)](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
23
- [![tests](https://img.shields.io/badge/tests-5%2C899%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
24
-
25
- [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
26
- [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
27
- [![network](https://img.shields.io/badge/network-127.0.0.1%20only-1a7f37?logo=wireguard&logoColor=white)](#where-your-data-can-go)
28
- [![telemetry](https://img.shields.io/badge/telemetry-none-1a7f37?logo=ghostery&logoColor=white)](#where-your-data-can-go)
29
-
30
- **[한국어](README.ko.md)** · [Corporate review](#corporate-review-package) · [Troubleshooting](#troubleshooting) · [Full docs](docs/en/)
31
-
32
- </div>
33
-
34
- ---
35
-
36
- ### The first thing you see answers the only question that matters
37
-
38
- <picture>
39
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-head-en-dark.svg">
40
- <img alt="deel startup panel: model, where it sends, link capabilities, folder, approval mode" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-head-en-light.svg" width="900">
41
- </picture>
42
-
43
- **`sends to this machine 127.0.0.1 ← and nowhere else`.** Not in a policy document —
44
- on screen, every time you start. The `⌂` in front stays in the status bar for the whole
45
- session and turns into `↗` the moment traffic would leave. A real capture, not a mockup:
46
- `node tools/shot.mjs` regenerates every image on this page from a live run.
47
-
48
- <br>
49
-
50
- Then you give it work, and it goes and does it:
51
-
52
- <picture>
53
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-chat-en-dark.svg">
54
- <img alt="deel doing a task end to end: Grep, Read, Edit with an inline diff, and the file it wrote" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-chat-en-light.svg" width="900">
55
- </picture>
56
-
57
- Every number in that image is real — the tools ran against real files. Only the model is a
58
- stub on `127.0.0.1`, so the capture is reproducible; the tool calls, the diff and the file
59
- summary are what deel actually printed.
60
-
61
- ---
62
-
63
- ## 60 seconds
64
-
65
- ```bash
66
- npm i -g deel-local-cli # 0 dependencies, no install scripts
67
- deel setup # point it at Ollama, LM Studio, or your gateway
68
- deel # start working in the current folder
69
- ```
70
-
71
- No account, no sign-up, no telemetry. If you already run Ollama or LM Studio,
72
- `deel setup` finds it — `deel scan` lists every local runtime and model on the machine.
73
-
74
- ---
75
-
76
- <details>
77
- <summary><b>Contents</b> — every section on this page</summary>
78
-
79
- <br>
80
-
81
- - [Why this exists](#why-this-exists)
82
- - [What's different](#whats-different)
83
- - [Quick start](#quick-start)
84
- - [Where your data can go](#where-your-data-can-go)
85
- - [Connecting a vendor API](#connecting-a-vendor-api)
86
- - [Keys that expire (corporate gateways)](#keys-that-expire-corporate-gateways)
87
- - [Multiple local runtimes](#multiple-local-runtimes)
88
- - [Slash commands](#slash-commands)
89
- - [Work modes](#work-modes)
90
- - [Simple vs developer](#simple-vs-developer)
91
- - [Tools](#tools)
92
- - [Korean text and Excel](#korean-text-and-excel)
93
- - [Serving what you built](#serving-what-you-built)
94
- - [Skills and plugins](#skills-and-plugins)
95
- - [Reasoning effort](#reasoning-effort)
96
- - [Auto-compaction](#auto-compaction)
97
- - [Resuming a conversation](#resuming-a-conversation)
98
- - [Attaching tools from outside (MCP)](#attaching-tools-from-outside-mcp)
99
- - [Inside your editor (ACP)](#inside-your-editor-acp)
100
- - [Keeping secrets out of the conversation](#keeping-secrets-out-of-the-conversation)
101
- - [Safety](#safety)
102
- - [Corporate review package](#corporate-review-package)
103
- - [Configuration](#configuration)
104
- - [Troubleshooting](#troubleshooting)
105
- - [Development](#development)
106
- - [Release notes](#release-notes)
107
-
108
- </details>
109
-
110
- This page is the **summary**. Each section links to the detail behind it.
111
-
112
- | Full docs | What is in there |
113
- |---|---|
114
- | [Models](docs/en/models.md) | Grade and window size · Korean-model presets · project detection |
115
- | [The screen](docs/en/interface.md) | The input box · work modes · simple vs developer · what it asks about |
116
- | [Tools in depth](docs/en/tools.md) | `Outline` · `Verify` · `Task` · `Jobs` · `Append` · `Def`/`Refs` · edit matching |
117
- | [Korean documents and Excel](docs/en/documents.md) | hwpx/docx/pptx/**PDF** · encoding · Excel → CSV |
118
- | [Extending](docs/en/extend.md) | Skills · plugins · MCP · ACP |
119
- | [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
120
- | [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
121
- | [Configuration](docs/en/config.md) · [Development](docs/en/develop.md) | Env vars · run flags · running the tests · folder layout |
122
- | [Release notes](docs/en/releases.md) | [1.10.x](docs/en/releases/1.10.md) · [1.9.x](docs/en/releases/1.9.md) · [1.8.x](docs/en/releases/1.8.md) · [1.7.x](docs/en/releases/1.7.md) · [older](docs/en/releases.md) |
123
-
124
- ---
125
-
126
- ## Why this exists
127
-
128
- When a corporate security policy blocks **unapproved software**, most coding-agent tools are
129
- unusable: hundreds of transitive dependencies, scripts that run at install time, and no
130
- one-line answer to "where does it send my code?"
131
-
132
- deel is built to pass that review.
133
-
134
- | | deel |
135
- |---|---|
136
- | External dependencies | **0** — Node built-ins only |
137
- | Install scripts | **none** — unzip and run |
138
- | Where source can go | **one address** — the one you configured |
139
- | Requirement | Node 20+ |
140
-
141
- Verify it yourself:
142
-
143
- ```bash
144
- npm view deel-local-cli dependencies # {}
145
- npm view deel-local-cli scripts # no install/postinstall
146
- deel audit # full review sheet
147
- ```
148
-
149
- ---
150
-
151
- ## What's different
152
-
153
- A handful of coding agents can talk to a local model. Far fewer were
154
- **redesigned inside for running locally.**
155
-
156
- | | Other tools | deel |
157
- |---|---|---|
158
- | `/undo` | rolls back files only — the conversation still believes it happened | rewinds **the conversation too** |
159
- | Long conversations | pay a cost cloud tools never feel, unchanged, locally | ordering designed so the prefix cache **survives** |
160
- | Edits on small models | fail on a single whitespace mismatch | 20%→**100%** success, 0 wrong-location edits |
161
- | Korean models | unknown until you've run them | known **in advance** from public docs |
162
- | "Done" | says so even for what wasn't checked | `/evidence` / `/export` — **unproven items included** |
163
- | MCP · ACP | need an SDK | `child_process` + `JSON`, nothing else |
164
- | Compliance paperwork | hand-written, drifts from reality | **generated by scanning the source** |
165
-
166
- <br>
167
-
168
- #### `/undo` rewinds the conversation along with the files
169
-
170
- Roll back only the files and the model still believes it just made that edit
171
- — it builds the next step on a premise that no longer holds, and nothing on
172
- screen says otherwise. deel folds the messages back in lockstep with the
173
- files. Folding can orphan a tool call, which the server answers with a 400,
174
- so the same pass repairs the pairing (`repairToolPairs`).
175
-
176
- #### Fixed the hidden reason local models get slower as a conversation grows
177
-
178
- Ollama and llama.cpp only reuse computation when a request's prefix exactly
179
- matches the last one — change one character near the front and everything
180
- after it, the whole conversation, gets recomputed. A cloud API never pays
181
- this cost, so cloud-first tools have no reason to care; someone running
182
- locally feels it compound every turn. deel pushes what can change per turn
183
- (mode, pins) to the **end** of the prompt and sends Ollama `keep_alive: 60m`
184
- so the front stays cached. The ordering is enforced by a test
185
- (`test/cache.test.js`).
186
-
187
- #### Edits actually succeed on small models
188
-
189
- Small local models often can't reproduce the exact whitespace of the string
190
- they're trying to edit. The internal benchmark (`npm run bench`) measured
191
- 20% success for the old exact-match-only approach. The current approach
192
- (stepped whitespace/indent tolerance) measures **100%** — and both approaches
193
- land at **0** wrong-location edits. When it's ambiguous, it says so instead
194
- of guessing.
195
-
196
- #### Korean models are known before you've ever run them
197
-
198
- EXAONE, HyperCLOVA X, Kanana, Midm, and Solar get whatever's verifiable from
199
- public documentation (e.g., whether a model is a reasoning model) applied
200
- before the first prompt. Other tools meet these models cold, and it takes a
201
- dozen-plus turns of trial and error before anyone learns their quirks.
202
-
203
- #### "Done" comes with a receipt, not just a claim
204
-
205
- `/evidence` and `/export` record what wasn't verified alongside what was —
206
- because the moment an AI coding tool is most likely to mislead someone is
207
- exactly the moment it confidently says "done." `/export` is a self-contained
208
- HTML file with zero outbound links, so it opens anywhere, including an
209
- air-gapped network.
210
-
211
- #### MCP and ACP, with no SDK
212
-
213
- Both the Model Context Protocol and the Agent Client Protocol are just
214
- newline-delimited JSON-RPC 2.0 over stdio. deel implements both with nothing
215
- but `child_process` and `JSON` — proof that zero dependencies isn't a
216
- capability given up, it's a capability that was never needed.
217
-
218
- #### Compliance paperwork it doesn't hand-write
219
-
220
- The import-review report, SBOM, and audit spec that `deel pack` produces are
221
- generated **by scanning the actual source**, not typed by a person.
222
- Hand-written paperwork eventually drifts from reality, and the moment a
223
- reviewer catches one drifted claim, they stop trusting the rest of it.
224
-
225
- ---
226
-
227
- ## Quick start
228
-
229
- ### The screen speaks your language
230
-
231
- deel is written in Korean — the code, the function names, the comments. That part stays.
232
- What you see on screen does not have to. Four screen languages ship: **한국어 · English ·
233
- 日本語 · 中文**.
234
-
235
- > **How this is kept honest:** `/lang` counting the table is not enough — it can only count
236
- > strings that made it *into* the table, and for a long time the tool result summaries and the
237
- > thinking indicator never did. So a test starts deel in each language and reads the screen:
238
- > `test/langleak.test.js` walks seven screens and fails if a Korean character shows up on an
239
- > English one. It also fails on `1 files`, and on a stray `·` left dangling at the end of a line.
240
-
241
- ```bash
242
- DEEL_LANG=en deel # this run only (also ja, zh)
243
- /lang en # and remember it
244
- /lang ja # 日本語
245
- /lang zh # 中文
246
- /lang # how much is translated so far
247
- ```
248
-
249
- Nothing ever comes through blank. A line that has not been translated falls back to English
250
- first and Korean second — so a Japanese or Chinese screen degrades to English, not to Korean —
251
- and `/lang` prints exactly how many strings are covered.
252
-
253
- What the model reads follows the same switch. Set it to English and the rules, the mode
254
- instructions, and the tool descriptions all go out in English — so the model answers you in
255
- English instead of Korean. That side is cheaper, too: the part of the window that ships on every
256
- single request drops from about 4,900 tokens to about 3,450 — on a 32k model, from 15% of the
257
- window to 10.5%.
258
-
259
- Tool and argument names are ASCII in every language — `Read(file_path)`, `Task(purpose)`.
260
- Those are identifiers, not prose, and several vendors reject a non-ASCII name outright.
261
- The Korean names some models learned earlier are still accepted on the way *in*, so a model
262
- that sends `목적` instead of `purpose` still gets through.
263
-
264
- ### Install
265
-
266
- ```bash
267
- npm install -g deel-local-cli
268
- ```
269
-
270
- Or skip installing entirely — there is no `npm install` step:
271
-
272
- ```bash
273
- git clone https://github.com/jysvai/deel-local-cli
274
- node deel-local-cli/bin/deel.js
275
- ```
276
-
277
- > **Note** — do not run `npm install` in your home directory. A `node_modules` there makes every
278
- > later npm command scan it and report warnings about unrelated packages. Use `-g` or `npx`.
279
-
280
- ### Point it at a model
281
-
282
- Scan this machine and pick one:
283
-
284
- ```bash
285
- deel scan --pick
286
- ```
287
-
288
- Or enter an address directly (use this for a corporate gateway):
289
-
290
- ```bash
291
- deel setup
292
- ```
293
-
294
- ### Start
295
-
296
- Run `deel` in the folder you want to work in. **That folder becomes the scope — files outside it
297
- cannot be read or written.**
298
-
299
- ```bash
300
- cd C:\work\myproject
301
- deel
302
- ```
303
-
304
- ---
305
-
306
- ## Where your data can go
307
-
308
- A coding agent ships your whole source to a model. **The address is everything.**
309
- Rather than promising in prose, the code enforces it: `src/safety/network.js` checks every request
310
- and never builds one for an address that is not on the allow-list.
311
-
312
- ```
313
- [A] Model gateway ────── the only path your source travels
314
- One address, set in `setup`. Switching models closes the previous one.
315
-
316
- [B] Web read (WebFetch) ─ receive-only
317
- GET only, zero-byte body. Private/loopback addresses refused. Every visit logged.
318
-
319
- [C] Plugin fetch ─────── open only while /plugin install runs
320
-
321
- [D] MCP servers ──────── a separate child process, someone else's program
322
- Only starts if a human writes it into .deel/mcp.json. Off by default.
323
- ```
324
-
325
- A, B, and C are requests deel makes itself, so each one can be filtered.
326
- **D is different** — an MCP server is its own process; there is no way to see
327
- what sockets it opens from the outside. So under `--offline`, instead of
328
- filtering its requests, deel **never starts the server at all** — it doesn't
329
- claim to have blocked what it can't actually see.
330
-
331
- Pass `--offline` and **B, C, and D are all closed** — traffic stays on this machine.
332
-
333
- ```bash
334
- deel --offline
335
- ```
336
-
337
- The destination is printed at the top of every session:
338
-
339
- ```
340
- deel 1.10.0 ⌂ inside
341
- Sends to this machine 127.0.0.1:11434 ← nowhere else
342
- ```
343
-
344
- ### Three run modes
345
-
346
- Through 1.6 the only lock was `--offline`, and **the default was open**. One
347
- line in `.deel/config.json` pointing outside was enough. The screen did show
348
- `↗`, but that is a *notice*, not a lock.
349
-
350
- | Mode | How | On an external address |
351
- |---|---|---|
352
- | `⌂ inside` | `deel` (default) | **Asks.** Say yes once and that connection stops asking |
353
- | `↗ outside` | `deel online` · `--online` | Does not ask |
354
- | `⛊ sealed` | `deel offline` · `--offline` | Ignores even remembered permission (strongest) |
355
-
356
- **Both the address and the permission are required.** Changing the address
357
- alone does not get you out.
358
-
359
- Local and intranet ranges (`127.x` · `10.x` · `192.168.x` · `172.16-31.x`) pass
360
- in all three modes — `offline` does not mean "no internet," it means "nothing
361
- leaves the company."
362
-
363
- Nothing is collected or transmitted. No telemetry, no usage stats, no crash reporting.
364
- Conversation history, undo snapshots and config live only in `.deel/` inside your working folder.
365
-
366
- > Verified by 159 checks in `npm test` (network + web + mcp), including bringing up a real
367
- > server and confirming that **not a single request reaches it** when it is not allow-listed,
368
- > and that an MCP server **never starts** under `--offline`.
369
-
370
- ---
371
-
372
- ## Connecting a vendor API
373
-
374
- Local models are the default and that does not change. But "we have no GPU
375
- in-house" and "just this one task on a bigger model" are real situations, so
376
- vendor APIs can connect. **The modes above guard that door.**
377
-
378
- ```bash
379
- deel setup
380
- ```
381
-
382
- Instead of asking for a URL, it asks **where you're connecting**.
383
-
384
- ```
385
- 1. I only have a key — I'll figure out where it goes 1 blank
386
- 2. Enter an address (corporate gateway · local) 2 blanks
387
- 3. OpenAI (GPT) 1 blank
388
- 4. Anthropic (Claude) 1 blank
389
- 5. Google (Gemini) 1 blank
390
- 6. AWS Bedrock 2 blanks
391
- ```
392
-
393
- Option 1 is the point — the key prefix decides which single vendor is asked.
394
-
395
- ```
396
- ❯ sk-ant-api03-••••
397
- ✓ Looks like an Anthropic (Claude) key. (the key starts with sk-ant-)
398
- It is not thrown at every vendor in turn.
399
- ```
400
-
401
- Probing vendors one by one would send an Anthropic key to OpenAI's server and
402
- then to Google's. You get a 401 and stop — but **the key has already left.**
403
- So an unrecognized key is never guessed at; you are asked.
404
-
405
- Bedrock asks for a region — five including Seoul (`ap-northeast-2`), plus
406
- "enter it yourself." Claude has a different wire shape, absorbed in six places
407
- ([1.7.0 release notes](docs/en/releases/1.7.md#170)).
408
-
409
- ### Going outside masks secrets in file contents too
410
-
411
- While everything stayed local, text read from files was deliberately **not**
412
- masked: mask it and the model writes the mask back into the file, destroying
413
- your real key.
414
-
415
- Going outside flips that trade. One `Read` puts your whole `.env` into someone
416
- else's server log, and that cannot be undone. The other side is now handled
417
- elsewhere — `Write`, `Append` and `Edit` refuse to write a mask back into a file.
418
-
419
- ### You can see what it costs
420
-
421
- ```
422
- ❯ ─ 12.4s · 3 tools · ↑8.2k ↓1.1k · $0.0271
423
- ```
424
-
425
- **There is no built-in price table.** Prices change whenever a vendor decides,
426
- and a table baked into source would have the tool confidently printing wrong
427
- amounts six months later. Write them in `.deel/config.json` — dollars per
428
- million tokens:
429
-
430
- ```json
431
- "pricing": { "claude-opus-4-6": { "input": 0, "output": 0, "asOf": "2026-09-01" } }
432
- ```
433
-
434
- The amount is shown with **where it came from and as of when**, and after six
435
- months it is marked stale. Unknown means nothing is printed — a local-only
436
- session never sees money at all.
437
-
438
- ---
439
-
440
- ## Keys that expire (corporate gateways)
441
-
442
- Corporate gateways do not hand out a fixed key. They hand out a one-hour token, behind a
443
- corporate login. Pasting one in works until lunch, and then you get `HTTP 401` — a message
444
- that **does not distinguish "wrong key" from "old key."** People go re-issue a key that was
445
- never the problem.
446
-
447
- So write down *how to get a key* instead of the key:
448
-
449
- ```json
450
- "authCommand": {
451
- "command": "az account get-access-token --resource api://ai-gw --query accessToken -o tsv",
452
- "ttl": 3600
453
- }
454
- ```
455
-
456
- It runs right before a request, keeps the result in memory only, and fetches a fresh one a
457
- minute before expiry — a token alive when the request left and dead when it arrived is
458
- exactly that 401. On a 401 it fetches once more and retries **once**; a second 401 means you
459
- genuinely lack access.
460
-
461
- | | |
462
- |---|---|
463
- | You write the path | Nothing is auto-detected. Guessing at `az` on your PATH would mean you no longer know when this program runs what |
464
- | Separate process | Never `import`ed — code inside our process would see other keys and the conversation |
465
- | Asked once per session | Not once per fetch. Three prompts and people just press the key |
466
- | Not while sealed | An `--offline` session does not go out to a login portal |
467
- | Never written to disk | Memory only, for the life of the session |
468
- | Banner output rejected | `Logged in as …` followed by a token gets a 400 from the gateway, indistinguishable on screen from a wrong key. The first line is shown back so you know to add `--query` |
469
-
470
- `/status` says `Key store fetched · 52 min left` — not "stored", because we are not holding
471
- it. An organisation can set the same block in the [managed policy](docs/en/config.md) file,
472
- where it overrides the user's config and is never asked about.
473
-
474
- ---
475
-
476
- ## Multiple local runtimes
477
-
478
- People rarely run just one. `deel scan` knocks on 13 known ports concurrently and identifies
479
- each runtime from its **response**, not its port number — Ollama by `/api/version`,
480
- LM Studio by `/api/v0/models`, llama.cpp by `/props`. Unrecognised ones are marked as a guess.
481
-
482
- ```
483
- $ deel scan
484
-
485
- ✓ found 3
486
-
487
- ◆ Ollama 127.0.0.1:11434 Ollama API 36ms
488
- · qwen2.5-coder:7b 7B · 4.4GB
489
- · llama3.2:1b 1B · 1.2GB
490
- ◆ LM Studio 127.0.0.1:1234 OpenAI-compat 7ms
491
- · devstral-small-2507
492
- ◆ llama.cpp 127.0.0.1:8080 OpenAI-compat 7ms
493
- · gemma-3-4b-it
494
-
495
- Recommended Ollama · qwen2.5-coder:7b
496
- ```
497
-
498
- | Command | What it does |
499
- |---|---|
500
- | `deel scan` | Show what is running |
501
- | `deel scan --pick` | Choose one from the list |
502
- | `deel scan --save` | Register everything found |
503
- | `deel scan --ports 9000,9100` | Extra ports to probe |
504
- | `deel scan --host <addr>` | Defaults to `127.0.0.1` |
505
-
506
- Switch with `/model` mid-conversation — **the conversation carries over.**
507
-
508
- > **More** — It adapts to whatever model is attached · Korean models are known before they are experienced · Small windows get a smaller fixed share · On startup it reads what kind of project this folder is
509
- >
510
- > **[Models read →](docs/en/models.md#multiple-local-runtimes)**
511
-
512
- ---
513
-
514
- ## Slash commands
515
-
516
- Names follow Claude Code / Codex conventions.
517
-
518
- | Command | What it does |
519
- |---|---|
520
- | `/help` | Command list |
521
- | `/lang [ko\|en] [prompt lang]` | Screen language. **The prompt language is a separate axis** — `/lang en ko` instructs the model in Korean while answering you in English. Korean screen + English prompt cuts the 8k fixed share by 23% |
522
- | `/keys` | Press keys to see what your terminal actually sends — for when new lines will not work |
523
- | `/bell [on\|off]` | Ring and set the window title when a turn ends, or when deel needs an answer |
524
- | `/consult <profile> <question>` | Ask a second model one question. Your current model stays put |
525
- | `/export` | This conversation as a **one-page HTML report** — asked, changed, verified. Self-contained, opens on any network |
526
- | `/lsp [on\|off]` | Language servers — what is installed, and whether `Def`/`Refs` are available. `off` turns post-edit diagnostics off only |
527
- | `/context` | What is consuming the context window |
528
- | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
529
- | `/grade [small\|medium\|large\|auto]` | Model **grade** — how much it does on its own. A different axis from `/ctx` |
530
- | `/out [number\|auto]` | Cap on a **single reply** — raise it when large files get cut |
531
- | `/compact` | Summarise and fold older turns |
532
- | `/clear` | Clear the conversation (keeps link and rules) |
533
- | `/thread [new\|fork\|close\|n]` | Conversation threads — side work in its own context. Link and undo stay shared |
534
- | `/learned [clear]` | What deel has picked up on its own — commands that work here, this model's habits |
535
- | `/pin <text>` | Pin a line — folding and compaction **cannot reach it** |
536
- | `/evidence [file]` | Evidence — what changed, and what proves it. **What is unproven is listed too** |
537
- | `/commit [all\|preview\|title]` | Commits only what this session changed; message from the diff and the evidence. **Never pushes** |
538
- | `/model` | Switch connection / model |
539
- | `/model card` | Model card — what this model has actually done here, and what deel changed because of it |
540
- | `/think <level>` | Reasoning level (`off·low·medium·high·max`) |
541
- | `/think profile <name>` | Per-stage profile (`even·save·deep`) |
542
- | `/think detail` | Stage table — which stage runs at which level and cap |
543
- | `/mode <mode>` | Approval policyhow much it asks (`auto` · `confirm` · `strict`) |
544
- | `/work [mode]` | Work modewhat kind of work you are doing |
545
- | `/auto` | Hand the wheel back it picks the mode from what you type |
546
- | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
547
- | `/level [level]` | How much to show (`simple` · `developer`) |
548
- | `/motion [plain\|knight\|animal\|office\|off]` | What animates while it works takes effect at once, and is saved |
549
- | `/undo [turns]` | Revert file changes |
550
- | `/diff [file]` | Files changed this session, and the changed lines |
551
- | `/preview [folder\|file\|off]` | Serve what you built, right here a browser opens with it |
552
- | `/tools` | Available tools |
553
- | `/skills [query\|all\|off]` | Browse, search, load skills |
554
- | `/plugin [install\|remove\|pack]` | Manage plugins |
555
- | `/cost` | Session usage |
556
- | `/status` | Connection status |
557
- | `/scan [save]` | Sweep this machine for local model servers (`save` registers them) |
558
- | `/sessions` | Past conversations in this folder |
559
- | `/recall <text>` | Search past conversations **by content** |
560
- | `/memory` | What persists across sessions view, add, delete |
561
- | `/mcp` | Externally attached tools (MCP servers) |
562
- | `/init` | Create a `DEEL.md` rules file |
563
- | `/exit` | Quit |
564
-
565
- Discovered plugin commands are invoked as `/<plugin>:<name>`, with `$ARGUMENTS` substituted.
566
-
567
- `/scan` and `/sessions` work without leaving the session. If you just started another local
568
- server or loaded a different model, `/scan save` then `/model` switches over without losing
569
- the conversation.
570
-
571
- **Without typing**
572
-
573
- | Key | What it does |
574
- |---|---|
575
- | `Tab` | Completes the `/` command you are typing. Candidates appear under the box as you type |
576
- | `Shift+Tab` | Approval policy (`⏵⏵ auto` `⏵ risky only` `⏸ everything`) |
577
- | `Ctrl+O` | Work mode (`Auto``Code` `Plan`) |
578
- | `↑` `↓` | Input history |
579
- | `Ctrl+C` | Stops the answer in progress; twice on an empty line quits |
580
- | Typing while it works, then `Enter` | Steers without throwing anything away takes effect from the next step |
581
-
582
- Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
583
-
584
- > **More** — Attaching a file with @ · Interrupting · Steering without stopping
585
- >
586
- > **[The screen read →](docs/en/interface.md#slash-commands)**
587
-
588
- ---
589
-
590
- ## Work modes
591
-
592
- What you are working on changes **which tools the model is given and how hard it thinks.**
593
- Cycle with `Shift+Tab`, or type the name.
594
-
595
- | Mode | For | Can edit files | Reasoning |
596
- |---|---|---|---|
597
- | `/auto` ◎ Auto | **Default.** Reads your message and switches for you | Yes | Normal (`save`) |
598
- | `/code` Code | Writing and fixing | Yes | Normal (`save`) |
599
- | `/plan` Plan | Planning first | **No** | Deep (`deep`·high) |
600
- | `/architect` Architect | Shaping structure | **No** | Deep (`deep`·high) |
601
- | `/debug` Debug | Finding causes | Yes | Deep, more steps (32) |
602
- | `/ask` Ask | Explaining only | **No** | Shallow (`low`) |
603
- | `/orchestrator` Orchestrator | Breaking up large work | Yes | Many steps (40) |
604
-
605
- In read-only modes, `Write`, `Edit` and `Bash` are **never sent to the model at all.**
606
- It is not asked politely not to edit models forget requests. A tool that isn't there can't be used.
607
-
608
- Don't confuse this with `/mode`. They are separate axes:
609
-
610
- - `/mode` — **how much it asks you** (auto · confirm · strict)
611
- - `/work` — **what kind of work you are doing** (the seven above)
612
-
613
- If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
614
- overrides something a person chose.
615
-
616
- > **More** — Switching by itself (Auto mode)
617
- >
618
- > **[The screen read →](docs/en/interface.md#work-modes)**
619
-
620
- ---
621
-
622
- ## Simple vs developer
623
-
624
- Twenty commands on first launch means nothing gets chosen. Locking features away means
625
- hitting a wall later. So only **what is shown** differs.
626
-
627
- | | Simple (default) | Developer |
628
- |---|---|---|
629
- | `/help` listing | Common commands only | Everything |
630
- | Error messages | What to do about it | The original text |
631
- | Safety | **Identical** | **Identical** |
632
-
633
- `/level developer` is saved to config and persists across sessions.
634
-
635
- Two things matter here:
636
-
637
- - **Hidden commands still work.** `/think high` works in simple mode. It just isn't listed.
638
- - **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
639
- blocking are identical. A beginner needs the undo more, not less.
640
-
641
- > **More** — The input box · You don't have to type the whole command · The box stays while it works · The picture on the left moves too and 1 more
642
- >
643
- > **[The screen read →](docs/en/interface.md#simple-vs-developer)**
644
-
645
- ---
646
-
647
- ## Tools
648
-
649
- Names and arguments match Claude Code, so skills written for that convention work unchanged.
650
-
651
- | Tool | What it does |
652
- |---|---|
653
- | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV, hwpx/docx/pptx and PDF as text**) |
654
- | `Write` | Write / overwrite a file (**several at once via the `files` array**) |
655
- | `Append` | Append to the end of a file **how large files get written in pieces** |
656
- | `Edit` | Replace an exact string (`replace_all`; **several sites at once via the `edits` array**) |
657
- | `Move` | Move / rename files and folders **how you restructure** (`moves` array; covered by undo) |
658
- | `Glob` | Find files by name pattern |
659
- | `Grep` | Regex search file contents |
660
- | `Bash` | Run a command (**`background: true` for anything that does not finish**) |
661
- | `Skill` | Expand a skill body (shown to the model only when skills exist) |
662
- | `WebFetch` | Read a web page (read-only; hidden under `--offline`) |
663
- | `Ask` | **Ask you back** at a fork offers the choices, takes a single number |
664
- | `Recall` | Search **past conversations** the model digs up "that thing last time" itself |
665
- | `Remember` | One line that outlives the session known from the start next time |
666
- | `TodoWrite` | Checklist breaks long work into steps and shows progress |
667
- | `Outline` | See a folder's **skeleton only** tens of times cheaper than reading it whole |
668
- | `Verify` | Check that what was built **actually works** |
669
- | `Task` | Run one chunk of a big job in a **separate context** |
670
- | `Jobs` | Inspect, read and stop **background commands** the other half of `Bash`'s `background` |
671
- | `Def` | **Where a name is defined** — only shown when a language server is installed |
672
- | `Refs` | **Every place a name is used** — only shown when a language server is installed |
673
-
674
- Nine tools here are not in Claude Code — `Append`, `Move`, `Ask`, `Recall`, `Remember`,
675
- `Outline`, `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
676
- so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
677
- their cost; here is why.
678
-
679
- > **More** — Outline · Verify · Task · Def · Refs · Commands that never finish and 9 more
680
- >
681
- > **[Tools in depth read →](docs/en/tools.md#tools)**
682
-
683
- ---
684
-
685
- ## Korean text and Excel
686
-
687
- **A file saved as CP949 is written back as CP949.** The encoding is never changed.
688
- Excel (`.xlsx`) is read as CSV read-only.
689
-
690
- Old formats (`.ppt`, `.doc`, `.xls`, `.rtf`) are read by **borrowing the LibreOffice already
691
- on this machine** the same terms on which deel borrows `rg`, and nothing is ever installed.
692
- With no converter it **says so definitively and stops**: what is missing, what you can do about
693
- it, and not to open the file again. Turn it off with `DEEL_CONVERT=off`.
694
-
695
- > **More** — Encoding · Excel · borrowing a converter
696
- >
697
- > **[Korean documents and Excel read →](docs/en/documents.md#korean-text-and-excel)**
698
-
699
- ---
700
-
701
- ## Serving what you built
702
-
703
- ```
704
- ❯ /preview
705
-
706
- ▶ Serving http://127.0.0.1:56801/
707
- showing .
708
- Edit a file and the page reloads by itself.
709
- Only this machine can open it (127.0.0.1). No other PC can see it.
710
- Stop with /preview off · it shuts down when deel exits.
711
- ```
712
-
713
- A browser opens with it. `/preview <folder>` picks what to serve, `/preview off` stops it.
714
-
715
- **This is not the same as double-clicking the file (`file://`).** Under `file://` everything
716
- below is blocked — and the error only shows up in the console while the page stays blank, so
717
- you end up suspecting your own code. This is a real HTTP server, so it all works:
718
-
719
- | | `file://` | `/preview` |
720
- |---|---|---|
721
- | `<script type="module">` · `import` | blocked (CORS) | **works** |
722
- | `fetch('./data.json')` | blocked | **works** |
723
- | `new Worker(...)` | blocked | **works** |
724
- | `WebAssembly.compileStreaming` | blocked (MIME) | **works** |
725
- | textures · `getImageData` | tainted canvas | **works** |
726
- | `.glb` / `.gltf` (Three.js) | no MIME type → silently not drawn | **works** |
727
-
728
- All seven were run in a real Chrome and confirmed **7/7**.
729
-
730
- Apps with a router (React Router and friends) get the first page back when you reload on a
731
- deep link. Never for requests with an extension (`app.js`) though returning HTML for a
732
- missing script dies with `Unexpected token '<'`, which hides the real cause (a typo in a filename).
733
-
734
- ### It opens exactly as much as it says
735
-
736
- Starting a server means opening your disk to somebody else.
737
-
738
- - Bound to **`127.0.0.1` only**. `0.0.0.0` is not available at all — on an office network
739
- that would let anyone read your source.
740
- - Port **0** (the kernel hands out a free one). A fixed port steals someone else's.
741
- - Paths cannot leave the working scope. `../` · `%2e%2e` · double encoding · absolute paths ·
742
- null bytes · symlinks eight of these are held shut by tests.
743
- - **It only serves.** `POST` · `PUT` · `DELETE` are refused with 405.
744
- - It shuts down when `deel` exits.
745
-
746
- ---
747
-
748
- ## Skills and plugins
749
-
750
- **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
751
- whatever is there. On a clean PC: zero. On a PC with skills installed: those skills.
752
-
753
- ```
754
- project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
755
- user ~/.deel/skills ~/.claude/skills ~/.claude/commands
756
- plugins ~/.claude/plugins/** ~/.deel/plugins/**
757
- ```
758
-
759
- Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
760
-
761
- > **More** — Loaded in three stages · Fetching plugins · Deliberately not included
762
- >
763
- > **[Extending read →](docs/en/extend.md#skills-and-plugins)**
764
-
765
- ---
766
-
767
- ### The hidden latency of local models — keeping the prefix cache alive
768
-
769
- Ollama and llama.cpp reuse computation **only while the request starts the same way as the
770
- last one.** Change one early character and everything after it the entire conversation
771
- is recomputed. This is the usual hidden reason long local sessions feel slower and slower,
772
- and it never shows up anywhere, because it is not an error.
773
-
774
- deel routes every message to the right mode automatically, and that mode instruction used to
775
- sit **early** in the prompt every mode switch broke the whole cache. So the stable parts
776
- (rules, folder, project fingerprint, user rules, memory, skills) are frozen at the front and
777
- the per-turn parts (mode, pins) go last. A test pins this order down (`test/cache.test.js`).
778
-
779
- Ollama also gets `keep_alive: 60m` — with the 5-minute default, the model unloads while you
780
- glance at another window, and the first message after you come back recomputes everything.
781
- Override with `DEEL_KEEP_ALIVE`. If you run llama.cpp directly, `--cache-reuse 256` on the
782
- server side does the same job.
783
-
784
- ## Reasoning effort
785
-
786
- One answer means several model calls, and **each needs a different amount of thinking.**
787
- All-high is slow; all-low wanders off.
788
-
789
- The default is **one line**. What you want to know is how hard it is thinking right now,
790
- not a stage table.
791
-
792
- ```
793
- $ /think
794
-
795
- Effort medium (First call medium · Continue low · Stuck high)
796
- harder /think high faster /think low
797
- ```
798
-
799
- | Profile | Character |
800
- |---|---|
801
- | `even` | Same effort everywhere — predictable, slower |
802
- | `save` (default) | Hard on the first decision only |
803
- | `deep` | Everything one notch up for hard work |
804
-
805
- Set the profile with `/think profile save`. **Level and profile are different axes, so the
806
- commands were split** `/think high` and `/think save` used to set different things under
807
- one name, which made the screen unreadable.
808
-
809
- The stage table moved to `/think detail` (the default at developer level).
810
-
811
- ```
812
- $ /think detail
813
-
814
- Effort medium (First call medium · Continue low · Stuck high)
815
- Profile save Hard on the first call, shallow while continuing - usually the better trade
816
-
817
- Stage Effort Out cap When
818
- First call · medium 14,069 Deciding what to do
819
- Continue ↓ low 12,310 Reading a tool result, picking the next move
820
- Stuck ↑ high 15,827 The last tool returned an error
821
-
822
- Caps are shared inside 16,384 (unknown, so a default) — /out
823
- Context 40,960 · used 5,787
824
- ```
825
-
826
- That second-to-last line exists for a reason: **when all three caps are equal, it is the
827
- only thing that says whether that is correct.** A low known cap makes them equal, and that
828
- is fine. For a while all three read `16,384` always which meant the table said nothing.
829
-
830
- > **More** — Context length is read off the model · /out · Truncated tool calls
831
- >
832
- > **[Speed and spend read →](docs/en/tuning.md#reasoning-effort)**
833
-
834
- ---
835
-
836
- ## Auto-compaction
837
-
838
- At 80% context, older turns are **summarised and folded** so work continues.
839
- Plain truncation makes the model forget: it re-reads files and re-fixes what it already fixed.
840
-
841
- ```
842
- ◱ Folded 44 turns into a summary — 10,399 → 3,170 tokens (70% smaller)
843
- ```
844
-
845
- The summary keeps goal / done / learned / decided / remaining. The cut point is chosen so a
846
- **tool call is never separated from its result** splitting them makes the server return 400.
847
- If the summary request fails, it falls back to plain trimming rather than stopping.
848
-
849
- `/compact` folds on demand.
850
-
851
- ---
852
-
853
- ## Resuming a conversation
854
-
855
- Close the terminal by accident, or reboot, and the conversation is still there.
856
- Messages are written to `.deel/sessions/` **as each one completes**, so a crash
857
- loses at most the message in flight.
858
-
859
- ```
860
- $ deel sessions
861
-
862
- ── conversations in this folder ────────────────────────────────
863
- 20260824-090200 just now 1 turn devstral-small-2507
864
- fix the failing test
865
- · 20260824-084500 2h ago 2 turns qwen2.5-coder:7b
866
- switch src/a.js logging to the logger
867
- ```
868
-
869
- | Command | What it does |
870
- |---|---|
871
- | `deel --continue` | Resume the most recent conversation in this folder |
872
- | `deel --resume <id>` | Resume a specific one |
873
- | `deel sessions` | List what is stored |
874
- | `deel sessions --rm <id>` | Delete one |
875
-
876
- The format is `jsonl` — one message per line — so a power cut costs only the last line.
877
- Resumed history keeps tool calls paired with their results, so work continues immediately.
878
- Conversations older than 30 days and outside the most recent 30 are pruned automatically.
879
-
880
- Everything lives in `.deel/sessions/` inside the working folder, and `.gitignore`
881
- covers `.deel/` so it never reaches a repository.
882
-
883
- ---
884
-
885
- ## Starting over (`deel reset`)
886
-
887
- Switching gateways, learned facts that went stale, handing the machine to someone else —
888
- sometimes you want to go back to the beginning. Reinstalling does not do it: `~/.deel`
889
- survives an install. Hence a command of its own.
890
-
891
- ```
892
- $ deel reset
893
-
894
- ── what can be wiped ──────────────────────────────────────────
895
- home C:\Users\me\.deel
896
- working folder C:\work\myproject
897
-
898
- connections 2
899
- memory 8 lines
900
- conversations 14
901
- learned 2 places
902
- evidence, exports, temp 6
903
- plugins 3
904
- sealed key DPAPI — this PC, this account only
905
-
906
- ── what is kept ───────────────────────────────────────────────
907
- undo snapshots 41 needs all --hard
908
- audit log 1,203 lines needs all --hard
909
- written by you .deel/mcp.json · .deelignore
910
- never touched, by any route.
911
- ```
912
-
913
- **Bare `deel reset` wipes nothing.** It shows what exists and asks.
914
-
915
- | Command | What goes |
916
- |---|---|
917
- | `deel reset model` | connections and profiles, plus the key in the OS keystore |
918
- | `deel reset memory` | memory (`.deel/memory.md`) |
919
- | `deel reset sessions` | conversation history |
920
- | `deel reset learned` | learned facts (this PC + this folder) |
921
- | `deel reset plugins` | installed plugins |
922
- | `deel reset all` | everything above **except plugins** |
923
- | `deel reset all --hard` | plus undo snapshots and the audit log |
924
- | `--yes` | skip the question (scripts, first-time provisioning) |
925
-
926
- What it does **not** touch matters more.
927
-
928
- | Kept | Why |
929
- |---|---|
930
- | `.deel/history/` (undo snapshots) | This is the safety net offered in place of an approval prompt. `--hard` only |
931
- | `.deel/audit.jsonl` | The evidence for an internal review. `--hard` only |
932
- | `.deel/mcp.json` · `.deelignore` · `DEEL.md` | You wrote these by hand. **Never touched, by any route** |
933
- | Everything else in the working folder | Nothing outside `.deel` and the home folder is ever reached |
934
-
935
- It runs on a broken config. That is usually *why* someone reaches for a reset, so
936
- `deel reset` starts without reading the connection the same reason `deel --version` answers
937
- without one. Plugins take time to fetch again, so they stay out of `all`; ask for them by name.
938
-
939
- ---
940
-
941
- ## Attaching tools from outside (MCP)
942
-
943
- A corporate wiki search, an issue tracker, a DB query tool — if a team publishes one as an MCP
944
- server, deel uses it as a tool **without a code change**.
945
-
946
- Configure in `.deel/mcp.json`. A Claude Code config can be copied over verbatim:
947
-
948
- ```json
949
- { "mcpServers": { "wiki": { "command": "node", "args": ["wiki-mcp.js"] } } }
950
- ```
951
-
952
- The model sees it as `mcp__wiki__search`. `/mcp` shows what is attached.
953
-
954
- **Dependencies stay at zero.** The stdio transport is nothing but newline-delimited JSON-RPC
955
- 2.0 over a child process's stdin/stdout, so `child_process` and `JSON` cover it. No SDK.
956
-
957
- > **More** — But this is somebody else's program
958
- >
959
- > **[Extending read →](docs/en/extend.md#attaching-tools-from-outside-mcp)**
960
-
961
- ---
962
-
963
- ## Inside your editor (ACP)
964
-
965
- A tool that makes you open one more terminal window stops being used after about two weeks.
966
- Developers live inside the IDE. So deel speaks **ACP** (Agent Client Protocol) Zed,
967
- JetBrains, Neovim and Emacs attach to it **without changing a line on their side**.
968
-
969
- One command in your editor's settings:
970
-
971
- ```
972
- deel acp
973
- ```
974
-
975
- The editor spawns that as a child process and exchanges newline-delimited JSON-RPC 2.0 over
976
- stdio. It is not a command you type yourself.
977
-
978
- **What you get once it is attached:**
979
-
980
- | In the editor | From deel |
981
- |---|---|
982
- | Streaming reply pane | The model's text and its reasoning |
983
- | 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 |
984
- | Clickable file links | The **absolute path** of every file touched |
985
- | Approval dialog | deel's safety rails, rendered as the editor's own prompt (`allow once` · `always allow` · `reject`) |
986
- | Mode picker | deel's seven work modes (auto · code · plan · architect · debug · ask · orchestrator) |
987
- | Stop button | Reaches the turn mid-flight, even while waiting on the model |
988
- | Past conversations | Still there after a restart. They live in the **same place** as the terminal's, so a session started in the editor can be picked up with `deel --resume` |
989
-
990
- **Still zero dependencies.** Same reason as MCP — newline-delimited JSON-RPC 2.0 is the whole
991
- transport, so no SDK is needed.
992
-
993
- > **More** — Details — the places this breaks silently
994
- >
995
- > **[Extending read →](docs/en/extend.md#inside-your-editor-acp)**
996
-
997
- ---
998
-
999
- ## Keeping secrets out of the conversation
1000
-
1001
- People rarely paste a key. The leak is almost always **command output**.
1002
-
1003
- ```
1004
- env OPENAI_API_KEY=sk-proj-…
1005
- git remote -v https://user:token@github.com/…
1006
- curl -v > Authorization: Bearer eyJ…
1007
- a failing test log the whole connection string
1008
- ```
1009
-
1010
- That text goes to the model **and** gets written to `.deel/sessions/*.jsonl` on disk. That
1011
- file is later re-read by `/recall` and can end up inside a `deel pack` bundle. Leak once and
1012
- you have several copies.
1013
-
1014
- So it is masked at the single point where tool output enters the conversation.
1015
-
1016
- ```
1017
- ⏺ Bash(env | grep API) done
1018
- 2 secret-looking values entered the conversation (openai · env var) — masked before the model
1019
- ```
1020
-
1021
- What it looks for: private-key blocks · OpenAI/Anthropic keys · GitHub tokens · Slack tokens ·
1022
- AWS keys · Google keys · JWTs · credentials embedded in URLs · `Authorization`-family headers ·
1023
- env vars named `…KEY` / `…TOKEN` / `…SECRET` / `…PASSWORD`. Plus **the configured gateway key
1024
- regardless of its shape** that one is not a guess, it is a known value.
1025
-
1026
- ### File contents are deliberately not masked
1027
-
1028
- `.env` is exactly where masking feels most tempting, and exactly where it backfires: the model
1029
- sees the masked text, edits it, writes it back — and `«가림»` lands where the real key was.
1030
- **Protecting the secret would destroy it.**
1031
-
1032
- So on the file side it reports instead of rewriting.
1033
-
1034
- ```
1035
- ⏺ Read(.env) 12 lines
1036
- ! 3 secret-looking values entered the conversation (env var)
1037
- file contents are not masked (masking them would erase the key on write-back)
1038
- ```
1039
-
1040
- Saying plainly what cannot be stopped beats claiming it was stopped while corrupting the file.
1041
- Either way it lands in the audit log.
1042
-
1043
- ---
1044
-
1045
- ## Safety
1046
-
1047
- Instead of approval prompts, the design makes things **reversible**. The default `auto` mode
1048
- does not ask.
1049
-
1050
- | Mechanism | Detail |
1051
- |---|---|
1052
- | **Undo** | Snapshot before every write. `/undo` restores per turn. **Includes moves and deletes done through `Bash`** |
1053
- | **Change display** | The changed lines are shown on every edit; `/diff` for the whole session |
1054
- | **Scope** | Outside the starting folder is refused, even if the model insists |
1055
- | **Blocked commands** | Only irreversible ones (disk format, recursive delete, `--force` push) |
1056
- | **No re-run** | A mutating command is never retried after failure |
1057
- | **Interrupt** | Ctrl+C stops mid-answer and leaves the conversation valid |
1058
- | **Spin guard** | Three identical failures stop the turn, with the reason |
1059
- | **Not read** | Other tools' private stores, and deel's own logs and config (the key), are refused |
1060
- | **Audit log** | Everything recorded in `.deel/audit.jsonl` |
1061
-
1062
- | Mode | Asks when |
1063
- |---|---|
1064
- | `auto` (default) | Never — undo is the safety net |
1065
- | `confirm` | Irreversible commands only |
1066
- | `strict` | All file changes and commands |
1067
-
1068
- Undo history stores whole file contents, so repeated edits to large files add up. Past 32MB
1069
- it keeps the **most recent 50 turns** and drops the rest. What you just did is always
1070
- undoable; `/status` shows how large the history currently is.
1071
-
1072
- > **More** — Files removed through Bash come back too · What it will not read
1073
- >
1074
- > **[Safety and corporate review read →](docs/en/safety.md#safety)**
1075
-
1076
- ---
1077
-
1078
- ## Corporate review package
1079
-
1080
- ```bash
1081
- deel pack --out deel-import.zip
1082
- ```
1083
-
1084
- ```
1085
- ✓ deel-import.zip
1086
- 94 files · 509.6KB
1087
-
1088
- Dependencies 0
1089
- Install scripts none
1090
- External imports 0
1091
- Network calls 3 sites (configured address only)
1092
- Ports opened 1 site (/preview only)
1093
- ```
1094
-
1095
- The zip carries **one document for people and two for machines.** A corporate review is
1096
- not a human-only process security feeds an SBOM to a scanner, and operations reads the
1097
- audit-log spec to write SIEM ingestion rules.
1098
-
1099
- | File | What |
1100
- |---|---|
1101
- | `import-review.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 |
1102
- | `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 |
1103
- | `audit-spec.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 |
1104
-
1105
- ```bash
1106
- deel audit # the human-readable sheet only
1107
- deel sbom # the two machine-readable ones, on stdout (deel sbom | jq)
1108
- deel sbom --out review.json # to a file
1109
- deel sbom --only sbom # just the SBOM
1110
- ```
1111
-
1112
- With the screen language set to English these three, and the `deel pack` archive itself,
1113
- come out in English file names included (`deel pack` writes `deel-import.zip`).
1114
-
1115
- All three are generated by scanning the source, never written by hand — hand-written sheets
1116
- drift, and **a review document that drifts is worse than none.** Find one wrong line and the
1117
- reviewer stops trusting the rest. The audit-log spec is the one hand-written part, so a test
1118
- checks it against real log records on every run.
1119
-
1120
- > **More** — Diagnosing a corporate gateway
1121
- >
1122
- > **[Safety and corporate review read →](docs/en/safety.md#corporate-review-package)**
1123
-
1124
- ---
1125
-
1126
- ## Configuration
1127
-
1128
- Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder takes precedence.
1129
-
1130
- > **More** — Supported servers · Environment variables · Flags · Project rules
1131
- >
1132
- > **[Configuration read →](docs/en/config.md#configuration)**
1133
-
1134
- ---
1135
-
1136
- ## Troubleshooting
1137
-
1138
- | Symptom | Check |
1139
- |---|---|
1140
- | `address not found` | Typo, DNS, VPN / intranet connectivity |
1141
- | `connection refused` | Server is down or the port differs |
1142
- | certificate error | `set NODE_EXTRA_CA_CERTS=C:\path\corp-ca.pem` |
1143
- | behind a proxy | `set HTTPS_PROXY=http://proxy:port` (with auth: `http://user:pw@proxy:port`). If the first screen and `/status` show `proxy …`, it is in use. Exclude hosts with `NO_PROXY=.corp.com,10.1.2.3`; turn it off entirely with `"proxy": "none"` in the config |
1144
- | the proxy answers 407 | Put `user:pw@` into the proxy address. Proxies that only accept NTLM · Negotiate are not supported ask the admin for Basic or an unauthenticated address |
1145
- | 401 / 403 | Wrong key or auth header style (four are tried automatically) |
1146
- | `address not permitted` | The lock did its job pick a connection with `/model` |
1147
- | Tool calls don't work | Run `deel diagnose`. Small models (1B–3B) often can't |
1148
- | Empty replies | The server ignores streaming. deel retries once, then turns streaming off for the session |
1149
- | 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 |
1150
- | 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 |
1151
- | `429` · `503` shows | The gateway pushed back for a moment. deel waits and calls again, up to three times (honouring `Retry-After`). If it keeps happening, check your quota |
1152
- | `deel scan` finds nothing | Server is off or on another port use `--ports` |
1153
-
1154
- ---
1155
-
1156
- ## Development
1157
-
1158
- ```bash
1159
- npm test Full suite (~5,890 checks; a few are TTY-dependent)
1160
- npm run coverage Which lines the tests actually execute
1161
- npm run verify Import + network checks only
1162
- npm run bench Edit success rate
1163
- npm run demo See what the UI actually looks like
1164
- npm run check Syntax check every file
1165
- ```
1166
-
1167
- Tests run against a **fake gateway**, so the loop, streaming, tool execution, undo and compaction
1168
- are verified deterministically without any model. ZIP output is cross-checked with the real
1169
- `unzip`; the TAR reader is fed archives produced by the real `tar`.
1170
-
1171
- `npm test` runs each file separately and reports **per-file exit codes**, because the exit code
1172
- not the pass marks on screen is what CI reads, and the two can disagree: a file can pass
1173
- every check and still die on the way out, leaving the screen green and the exit code 1. That
1174
- happened once on Windows and cost a lot of time. The runner does not stop at the first failure,
1175
- so one run tells you everything.
1176
-
1177
- | Suite | Checks | Covers |
1178
- |---|---|---|
1179
- | `smoke` | 20 | Tools, scope, undo, audit log |
1180
- | `loop` | 16 | Agent loop, streaming, tool calls |
1181
- | `guard` | 24 | **What it refuses to do** — denied edits, unknown tools, repeated mutations, out-of-scope writes |
1182
- | `network` | 30 | Nothing escapes the configured address |
1183
- | `web` | 25 | Web reads stay read-only |
1184
- | `abort` · `steer` | 16 · 15 | Ctrl+C leaves the conversation valid · a line typed mid-turn rides the next call |
1185
- | `parallel` | 23 | Read-only tools run together; checklists |
1186
- | `cli` | 75 | **Spawns the real `deel`** and drives it to completion |
1187
- | `setup` | 42 | First-run wizard, driven through a fake TTY |
1188
- | `detect` | 66 | Identifying shape and auth from one address |
1189
- | `modes` · `route` | 89 · 33 | Work modes; auto-switching from Auto |
1190
- | `ctxsize` | 43 | Reading context length off the model |
1191
- | `commands` · `commands-more` | 128 · 62 | Every slash command |
1192
- | `ui` · `ui2` | 60 · 40 | Password masking, CJK width, status line, session list, Excel→text |
1193
- | `encoding` · `xlsx` | 68 · 72 | Legacy-encoding detection; Excel reading |
1194
- | `compact` | 21 | Summary folding, pairing intact, graceful fallback |
1195
- | `store` | 34 | Session persistence, resume, crash recovery |
1196
- | `scan` | 29 | Distinguishing multiple runtimes |
1197
- | `plugins` | 38 | Plugin fetch/pack, ZIP/TAR |
1198
- | `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
1199
- | `edit-bench` | 20 cases | Edit success rate |
1200
-
1201
- > **More** — Coverage · Layout
1202
- >
1203
- > **[Development read →](docs/en/develop.md#development)**
1204
-
1205
- ---
1206
-
1207
- ## Release notes
1208
-
1209
- | Version | What changed |
1210
- |---|---|
1211
- | **[1.10.0](docs/en/releases/1.10.md#1100)** | ESC actually stops · a full context carries on inside the same turn · every vendor endpoint measured |
1212
- | [1.9.2](docs/en/releases/1.9.md#192) | Guards that said they were blocking were not blocking · folding lost the request and the outstanding work |
1213
- | [1.9.1](docs/en/releases/1.9.md#191) | MCP tools with Korean names all looked the same to the model names are now distinct and stable |
1214
- | **[1.9.0](docs/en/releases/1.9.md#190)** | Tables are drawn as tables · reasoning effort reaches Claude and Bedrock · tool schemas shaped per vendor · only the changed part of a file is re-sent |
1215
- | **[1.8.0](docs/en/releases/1.8.md#180)** | A full day against a gateway that hands out one-hour tokens fetch the key instead of storing it · `deel reset` · an English screen that is actually English |
1216
-
1217
- The five most recent are listed here. Every version, and why each thing changed, is in the **[release notes](docs/en/releases.md)**.
1218
-
1219
- ---
1220
-
1221
- ## Licence
1222
-
1223
- [MIT](LICENSE)
1
+ <div align="center">
2
+
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/hero-en-dark.svg">
5
+ <img alt="deel — stays on this machine" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/hero-en-light.svg" width="620">
6
+ </picture>
7
+
8
+ ### A coding-agent CLI that runs on local models and private gateways
9
+
10
+ Zero dependencies · Node 20+ · Exactly one place your source can go
11
+
12
+ Vendor APIs connect too — **only when you say so**
13
+
14
+ <br>
15
+
16
+ [![npm](https://img.shields.io/npm/v/deel-local-cli?logo=npm&logoColor=white&label=npm&color=cb3837)](https://www.npmjs.com/package/deel-local-cli)
17
+ [![downloads](https://img.shields.io/npm/dt/deel-local-cli?label=downloads&color=1a7f37)](https://www.npmjs.com/package/deel-local-cli)
18
+ [![node](https://img.shields.io/node/v/deel-local-cli?logo=nodedotjs&logoColor=white&label=node&color=5FA04E)](https://nodejs.org)
19
+ [![license](https://img.shields.io/npm/l/deel-local-cli?label=license&color=0969da)](LICENSE)
20
+
21
+ [![Node.js CI](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/test.yml?branch=main&logo=github&logoColor=white&label=Node.js%20CI)](https://github.com/jysvai/deel-local-cli/actions/workflows/test.yml)
22
+ [![CodeQL](https://img.shields.io/github/actions/workflow/status/jysvai/deel-local-cli/codeql.yml?branch=main&logo=github&logoColor=white&label=CodeQL)](https://github.com/jysvai/deel-local-cli/actions/workflows/codeql.yml)
23
+ [![tests](https://img.shields.io/badge/tests-5%2C899%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
24
+
25
+ [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
26
+ [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
27
+ [![network](https://img.shields.io/badge/network-127.0.0.1%20only-1a7f37?logo=wireguard&logoColor=white)](#where-your-data-can-go)
28
+ [![telemetry](https://img.shields.io/badge/telemetry-none-1a7f37?logo=ghostery&logoColor=white)](#where-your-data-can-go)
29
+
30
+ **[한국어](README.ko.md)** · [Corporate review](#corporate-review-package) · [Troubleshooting](#troubleshooting) · [Full docs](docs/en/)
31
+
32
+ </div>
33
+
34
+ ---
35
+
36
+ ### The first thing you see answers the only question that matters
37
+
38
+ <picture>
39
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-head-en-dark.svg">
40
+ <img alt="deel startup panel: model, where it sends, link capabilities, folder, approval mode" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-head-en-light.svg" width="900">
41
+ </picture>
42
+
43
+ **`sends to this machine 127.0.0.1 ← and nowhere else`.** Not in a policy document —
44
+ on screen, every time you start. The `⌂` in front stays in the status bar for the whole
45
+ session and turns into `↗` the moment traffic would leave. A real capture, not a mockup:
46
+ `node tools/shot.mjs` regenerates every image on this page from a live run.
47
+
48
+ <br>
49
+
50
+ Then you give it work, and it goes and does it:
51
+
52
+ <picture>
53
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-chat-en-dark.svg">
54
+ <img alt="deel doing a task end to end: Grep, Read, Edit with an inline diff, and the file it wrote" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-chat-en-light.svg" width="900">
55
+ </picture>
56
+
57
+ Every number in that image is real — the tools ran against real files. Only the model is a
58
+ stub on `127.0.0.1`, so the capture is reproducible; the tool calls, the diff and the file
59
+ summary are what deel actually printed.
60
+
61
+ ---
62
+
63
+ ## 60 seconds
64
+
65
+ ```bash
66
+ npm i -g deel-local-cli # 0 dependencies, no install scripts
67
+ deel setup # point it at Ollama, LM Studio, or your gateway
68
+ deel # start working in the current folder
69
+ ```
70
+
71
+ No account, no sign-up, no telemetry. If you already run Ollama or LM Studio,
72
+ `deel setup` finds it — `deel scan` lists every local runtime and model on the machine.
73
+
74
+ ---
75
+
76
+ <details>
77
+ <summary><b>Contents</b> — every section on this page</summary>
78
+
79
+ <br>
80
+
81
+ - [Why this exists](#why-this-exists)
82
+ - [What's different](#whats-different)
83
+ - [Quick start](#quick-start)
84
+ - [Where your data can go](#where-your-data-can-go)
85
+ - [Connecting a vendor API](#connecting-a-vendor-api)
86
+ - [Keys that expire (corporate gateways)](#keys-that-expire-corporate-gateways)
87
+ - [Multiple local runtimes](#multiple-local-runtimes)
88
+ - [Slash commands](#slash-commands)
89
+ - [Work modes](#work-modes)
90
+ - [Simple vs developer](#simple-vs-developer)
91
+ - [Tools](#tools)
92
+ - [Korean text and Excel](#korean-text-and-excel)
93
+ - [Serving what you built](#serving-what-you-built)
94
+ - [Skills and plugins](#skills-and-plugins)
95
+ - [Reasoning effort](#reasoning-effort)
96
+ - [Auto-compaction](#auto-compaction)
97
+ - [Resuming a conversation](#resuming-a-conversation)
98
+ - [Attaching tools from outside (MCP)](#attaching-tools-from-outside-mcp)
99
+ - [Inside your editor (ACP)](#inside-your-editor-acp)
100
+ - [Keeping secrets out of the conversation](#keeping-secrets-out-of-the-conversation)
101
+ - [Safety](#safety)
102
+ - [Corporate review package](#corporate-review-package)
103
+ - [Configuration](#configuration)
104
+ - [Troubleshooting](#troubleshooting)
105
+ - [Development](#development)
106
+ - [Release notes](#release-notes)
107
+
108
+ </details>
109
+
110
+ This page is the **summary**. Each section links to the detail behind it.
111
+
112
+ | Full docs | What is in there |
113
+ |---|---|
114
+ | [Models](docs/en/models.md) | Grade and window size · Korean-model presets · project detection |
115
+ | [The screen](docs/en/interface.md) | The input box · work modes · simple vs developer · what it asks about |
116
+ | [Tools in depth](docs/en/tools.md) | `Outline` · `Verify` · `Task` · `Jobs` · `Append` · `Def`/`Refs` · edit matching |
117
+ | [Korean documents and Excel](docs/en/documents.md) | hwpx/docx/pptx/**PDF** · encoding · Excel → CSV |
118
+ | [Extending](docs/en/extend.md) | Skills · plugins · MCP · ACP |
119
+ | [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
120
+ | [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
121
+ | [Configuration](docs/en/config.md) · [Development](docs/en/develop.md) | Env vars · run flags · running the tests · folder layout |
122
+ | [Release notes](docs/en/releases.md) | [1.10.x](docs/en/releases/1.10.md) · [1.9.x](docs/en/releases/1.9.md) · [1.8.x](docs/en/releases/1.8.md) · [1.7.x](docs/en/releases/1.7.md) · [older](docs/en/releases.md) |
123
+
124
+ ---
125
+
126
+ ## Why this exists
127
+
128
+ When a corporate security policy blocks **unapproved software**, most coding-agent tools are
129
+ unusable: hundreds of transitive dependencies, scripts that run at install time, and no
130
+ one-line answer to "where does it send my code?"
131
+
132
+ deel is built to pass that review.
133
+
134
+ | | deel |
135
+ |---|---|
136
+ | External dependencies | **0** — Node built-ins only |
137
+ | Install scripts | **none** — unzip and run |
138
+ | Where source can go | **one address** — the one you configured |
139
+ | Requirement | Node 20+ |
140
+
141
+ Verify it yourself:
142
+
143
+ ```bash
144
+ npm view deel-local-cli dependencies # {}
145
+ npm view deel-local-cli scripts # no install/postinstall
146
+ deel audit # full review sheet
147
+ ```
148
+
149
+ ---
150
+
151
+ ## What's different
152
+
153
+ A handful of coding agents can talk to a local model. Far fewer were
154
+ **redesigned inside for running locally.**
155
+
156
+ | | Other tools | deel |
157
+ |---|---|---|
158
+ | `/undo` | rolls back files only — the conversation still believes it happened | rewinds **the conversation too** |
159
+ | Long conversations | pay a cost cloud tools never feel, unchanged, locally | ordering designed so the prefix cache **survives** |
160
+ | Edits on small models | fail on a single whitespace mismatch | 20%→**100%** success, 0 wrong-location edits |
161
+ | Korean models | unknown until you've run them | known **in advance** from public docs |
162
+ | "Done" | says so even for what wasn't checked | `/evidence` / `/export` — **unproven items included** |
163
+ | MCP · ACP | need an SDK | `child_process` + `JSON`, nothing else |
164
+ | Compliance paperwork | hand-written, drifts from reality | **generated by scanning the source** |
165
+
166
+ <br>
167
+
168
+ #### `/undo` rewinds the conversation along with the files
169
+
170
+ Roll back only the files and the model still believes it just made that edit
171
+ — it builds the next step on a premise that no longer holds, and nothing on
172
+ screen says otherwise. deel folds the messages back in lockstep with the
173
+ files. Folding can orphan a tool call, which the server answers with a 400,
174
+ so the same pass repairs the pairing (`repairToolPairs`).
175
+
176
+ #### Fixed the hidden reason local models get slower as a conversation grows
177
+
178
+ Ollama and llama.cpp only reuse computation when a request's prefix exactly
179
+ matches the last one — change one character near the front and everything
180
+ after it, the whole conversation, gets recomputed. A cloud API never pays
181
+ this cost, so cloud-first tools have no reason to care; someone running
182
+ locally feels it compound every turn. deel pushes what can change per turn
183
+ (mode, pins) to the **end** of the prompt and sends Ollama `keep_alive: 60m`
184
+ so the front stays cached. The ordering is enforced by a test
185
+ (`test/cache.test.js`).
186
+
187
+ #### Edits actually succeed on small models
188
+
189
+ Small local models often can't reproduce the exact whitespace of the string
190
+ they're trying to edit. The internal benchmark (`npm run bench`) measured
191
+ 20% success for the old exact-match-only approach. The current approach
192
+ (stepped whitespace/indent tolerance) measures **100%** — and both approaches
193
+ land at **0** wrong-location edits. When it's ambiguous, it says so instead
194
+ of guessing.
195
+
196
+ #### Korean models are known before you've ever run them
197
+
198
+ EXAONE, HyperCLOVA X, Kanana, Midm, and Solar get whatever's verifiable from
199
+ public documentation (e.g., whether a model is a reasoning model) applied
200
+ before the first prompt. Other tools meet these models cold, and it takes a
201
+ dozen-plus turns of trial and error before anyone learns their quirks.
202
+
203
+ #### "Done" comes with a receipt, not just a claim
204
+
205
+ `/evidence` and `/export` record what wasn't verified alongside what was —
206
+ because the moment an AI coding tool is most likely to mislead someone is
207
+ exactly the moment it confidently says "done." `/export` is a self-contained
208
+ HTML file with zero outbound links, so it opens anywhere, including an
209
+ air-gapped network.
210
+
211
+ #### MCP and ACP, with no SDK
212
+
213
+ Both the Model Context Protocol and the Agent Client Protocol are just
214
+ newline-delimited JSON-RPC 2.0 over stdio. deel implements both with nothing
215
+ but `child_process` and `JSON` — proof that zero dependencies isn't a
216
+ capability given up, it's a capability that was never needed.
217
+
218
+ #### Compliance paperwork it doesn't hand-write
219
+
220
+ The import-review report, SBOM, and audit spec that `deel pack` produces are
221
+ generated **by scanning the actual source**, not typed by a person.
222
+ Hand-written paperwork eventually drifts from reality, and the moment a
223
+ reviewer catches one drifted claim, they stop trusting the rest of it.
224
+
225
+ ---
226
+
227
+ ## Quick start
228
+
229
+ ### The screen speaks your language
230
+
231
+ deel is written in Korean — the code, the function names, the comments. That part stays.
232
+ What you see on screen does not have to. Four screen languages ship: **한국어 · English ·
233
+ 日本語 · 中文**.
234
+
235
+ > **How this is kept honest:** `/lang` counting the table is not enough — it can only count
236
+ > strings that made it *into* the table, and for a long time the tool result summaries and the
237
+ > thinking indicator never did. So a test starts deel in each language and reads the screen:
238
+ > `test/langleak.test.js` walks seven screens and fails if a Korean character shows up on an
239
+ > English one. It also fails on `1 files`, and on a stray `·` left dangling at the end of a line.
240
+
241
+ ```bash
242
+ DEEL_LANG=en deel # this run only (also ja, zh)
243
+ /lang en # and remember it
244
+ /lang ja # 日本語
245
+ /lang zh # 中文
246
+ /lang # how much is translated so far
247
+ ```
248
+
249
+ Nothing ever comes through blank. A line that has not been translated falls back to English
250
+ first and Korean second — so a Japanese or Chinese screen degrades to English, not to Korean —
251
+ and `/lang` prints exactly how many strings are covered.
252
+
253
+ What the model reads follows the same switch. Set it to English and the rules, the mode
254
+ instructions, and the tool descriptions all go out in English — so the model answers you in
255
+ English instead of Korean. That side is cheaper, too: the part of the window that ships on every
256
+ single request drops from about 4,900 tokens to about 3,450 — on a 32k model, from 15% of the
257
+ window to 10.5%.
258
+
259
+ Tool and argument names are ASCII in every language — `Read(file_path)`, `Task(purpose)`.
260
+ Those are identifiers, not prose, and several vendors reject a non-ASCII name outright.
261
+ The Korean names some models learned earlier are still accepted on the way *in*, so a model
262
+ that sends `목적` instead of `purpose` still gets through.
263
+
264
+ ### Install
265
+
266
+ ```bash
267
+ npm install -g deel-local-cli
268
+ ```
269
+
270
+ Or skip installing entirely — there is no `npm install` step:
271
+
272
+ ```bash
273
+ git clone https://github.com/jysvai/deel-local-cli
274
+ node deel-local-cli/bin/deel.js
275
+ ```
276
+
277
+ > **Note** — do not run `npm install` in your home directory. A `node_modules` there makes every
278
+ > later npm command scan it and report warnings about unrelated packages. Use `-g` or `npx`.
279
+
280
+ ### Point it at a model
281
+
282
+ Scan this machine and pick one:
283
+
284
+ ```bash
285
+ deel scan --pick
286
+ ```
287
+
288
+ Or enter an address directly (use this for a corporate gateway):
289
+
290
+ ```bash
291
+ deel setup
292
+ ```
293
+
294
+ ### Start
295
+
296
+ Run `deel` in the folder you want to work in. **That folder becomes the scope — files outside it
297
+ cannot be read or written.**
298
+
299
+ ```bash
300
+ cd C:\work\myproject
301
+ deel
302
+ ```
303
+
304
+ ---
305
+
306
+ ## Where your data can go
307
+
308
+ A coding agent ships your whole source to a model. **The address is everything.**
309
+ Rather than promising in prose, the code enforces it: `src/safety/network.js` checks every request
310
+ and never builds one for an address that is not on the allow-list.
311
+
312
+ ```
313
+ [A] Model gateway ────── the only path your source travels
314
+ One address, set in `setup`. Switching models closes the previous one.
315
+
316
+ [B] Web read (WebFetch) ─ receive-only
317
+ GET only, zero-byte body. Private/loopback addresses refused. Every visit logged.
318
+
319
+ [C] Plugin fetch ─────── open only while /plugin install runs
320
+
321
+ [D] MCP servers ──────── a separate child process, someone else's program
322
+ Only starts if a human writes it into .deel/mcp.json. Off by default.
323
+ ```
324
+
325
+ A, B, and C are requests deel makes itself, so each one can be filtered.
326
+ **D is different** — an MCP server is its own process; there is no way to see
327
+ what sockets it opens from the outside. So under `--offline`, instead of
328
+ filtering its requests, deel **never starts the server at all** — it doesn't
329
+ claim to have blocked what it can't actually see.
330
+
331
+ Pass `--offline` and **B, C, and D are all closed** — traffic stays on this machine.
332
+
333
+ ```bash
334
+ deel --offline
335
+ ```
336
+
337
+ The destination is printed at the top of every session:
338
+
339
+ ```
340
+ deel 1.10.0 ⌂ inside
341
+ Sends to this machine 127.0.0.1:11434 ← nowhere else
342
+ ```
343
+
344
+ ### Three run modes
345
+
346
+ Through 1.6 the only lock was `--offline`, and **the default was open**. One
347
+ line in `.deel/config.json` pointing outside was enough. The screen did show
348
+ `↗`, but that is a *notice*, not a lock.
349
+
350
+ | Mode | How | On an external address |
351
+ |---|---|---|
352
+ | `⌂ inside` | `deel` (default) | **Asks.** Say yes once and that connection stops asking |
353
+ | `↗ outside` | `deel online` · `--online` | Does not ask |
354
+ | `⛊ sealed` | `deel offline` · `--offline` | Ignores even remembered permission (strongest) |
355
+
356
+ **Both the address and the permission are required.** Changing the address
357
+ alone does not get you out.
358
+
359
+ Local and intranet ranges (`127.x` · `10.x` · `192.168.x` · `172.16-31.x`) pass
360
+ in all three modes — `offline` does not mean "no internet," it means "nothing
361
+ leaves the company."
362
+
363
+ Nothing is collected or transmitted. No telemetry, no usage stats, no crash reporting.
364
+ Conversation history, undo snapshots and config live only in `.deel/` inside your working folder.
365
+
366
+ > Verified by 159 checks in `npm test` (network + web + mcp), including bringing up a real
367
+ > server and confirming that **not a single request reaches it** when it is not allow-listed,
368
+ > and that an MCP server **never starts** under `--offline`.
369
+
370
+ ---
371
+
372
+ ## Connecting a vendor API
373
+
374
+ Local models are the default and that does not change. But "we have no GPU
375
+ in-house" and "just this one task on a bigger model" are real situations, so
376
+ vendor APIs can connect. **The modes above guard that door.**
377
+
378
+ ```bash
379
+ deel setup
380
+ ```
381
+
382
+ Instead of asking for a URL, it asks **where you're connecting**.
383
+
384
+ ```
385
+ 1. I only have a key — I'll figure out where it goes 1 blank
386
+ 2. Enter an address (corporate gateway · local) 2 blanks
387
+ 3. OpenAI (GPT) 1 blank
388
+ 4. Anthropic (Claude) 1 blank
389
+ 5. Google (Gemini) 1 blank
390
+ 6. AWS Bedrock 2 blanks
391
+ ```
392
+
393
+ Option 1 is the point — the key prefix decides which single vendor is asked.
394
+
395
+ ```
396
+ ❯ sk-ant-api03-••••
397
+ ✓ Looks like an Anthropic (Claude) key. (the key starts with sk-ant-)
398
+ It is not thrown at every vendor in turn.
399
+ ```
400
+
401
+ Probing vendors one by one would send an Anthropic key to OpenAI's server and
402
+ then to Google's. You get a 401 and stop — but **the key has already left.**
403
+ So an unrecognized key is never guessed at; you are asked.
404
+
405
+ Bedrock asks for a region — five including Seoul (`ap-northeast-2`), plus
406
+ "enter it yourself." Claude has a different wire shape, absorbed in six places
407
+ ([1.7.0 release notes](docs/en/releases/1.7.md#170)).
408
+
409
+ ### Going outside masks secrets in file contents too
410
+
411
+ While everything stayed local, text read from files was deliberately **not**
412
+ masked: mask it and the model writes the mask back into the file, destroying
413
+ your real key.
414
+
415
+ Going outside flips that trade. One `Read` puts your whole `.env` into someone
416
+ else's server log, and that cannot be undone. The other side is now handled
417
+ elsewhere — `Write`, `Append` and `Edit` refuse to write a mask back into a file.
418
+
419
+ ### You can see what it costs
420
+
421
+ ```
422
+ ❯ ─ 12.4s · 3 tools · ↑8.2k ↓1.1k · $0.0271
423
+ ```
424
+
425
+ **There is no built-in price table.** Prices change whenever a vendor decides,
426
+ and a table baked into source would have the tool confidently printing wrong
427
+ amounts six months later. Write them in `.deel/config.json` — dollars per
428
+ million tokens:
429
+
430
+ ```json
431
+ "pricing": { "claude-opus-4-6": { "input": 0, "output": 0, "asOf": "2026-09-01" } }
432
+ ```
433
+
434
+ The amount is shown with **where it came from and as of when**, and after six
435
+ months it is marked stale. Unknown means nothing is printed — a local-only
436
+ session never sees money at all.
437
+
438
+ ---
439
+
440
+ ## Keys that expire (corporate gateways)
441
+
442
+ Corporate gateways do not hand out a fixed key. They hand out a one-hour token, behind a
443
+ corporate login. Pasting one in works until lunch, and then you get `HTTP 401` — a message
444
+ that **does not distinguish "wrong key" from "old key."** People go re-issue a key that was
445
+ never the problem.
446
+
447
+ So write down *how to get a key* instead of the key:
448
+
449
+ ```json
450
+ "authCommand": {
451
+ "command": "az account get-access-token --resource api://ai-gw --query accessToken -o tsv",
452
+ "ttl": 3600
453
+ }
454
+ ```
455
+
456
+ It runs right before a request, keeps the result in memory only, and fetches a fresh one a
457
+ minute before expiry — a token alive when the request left and dead when it arrived is
458
+ exactly that 401. On a 401 it fetches once more and retries **once**; a second 401 means you
459
+ genuinely lack access.
460
+
461
+ | | |
462
+ |---|---|
463
+ | You write the path | Nothing is auto-detected. Guessing at `az` on your PATH would mean you no longer know when this program runs what |
464
+ | Separate process | Never `import`ed — code inside our process would see other keys and the conversation |
465
+ | Asked once per session | Not once per fetch. Three prompts and people just press the key |
466
+ | Not while sealed | An `--offline` session does not go out to a login portal |
467
+ | Never written to disk | Memory only, for the life of the session |
468
+ | Banner output rejected | `Logged in as …` followed by a token gets a 400 from the gateway, indistinguishable on screen from a wrong key. The first line is shown back so you know to add `--query` |
469
+
470
+ `/status` says `Key store fetched · 52 min left` — not "stored", because we are not holding
471
+ it. An organisation can set the same block in the [managed policy](docs/en/config.md) file,
472
+ where it overrides the user's config and is never asked about.
473
+
474
+ ---
475
+
476
+ ## Multiple local runtimes
477
+
478
+ People rarely run just one. `deel scan` knocks on 13 known ports concurrently and identifies
479
+ each runtime from its **response**, not its port number — Ollama by `/api/version`,
480
+ LM Studio by `/api/v0/models`, llama.cpp by `/props`. Unrecognised ones are marked as a guess.
481
+
482
+ ```
483
+ $ deel scan
484
+
485
+ ✓ found 3
486
+
487
+ ◆ Ollama 127.0.0.1:11434 Ollama API 36ms
488
+ · qwen2.5-coder:7b 7B · 4.4GB
489
+ · llama3.2:1b 1B · 1.2GB
490
+ ◆ LM Studio 127.0.0.1:1234 OpenAI-compat 7ms
491
+ · devstral-small-2507
492
+ ◆ llama.cpp 127.0.0.1:8080 OpenAI-compat 7ms
493
+ · gemma-3-4b-it
494
+
495
+ Recommended Ollama · qwen2.5-coder:7b
496
+ ```
497
+
498
+ | Command | What it does |
499
+ |---|---|
500
+ | `deel scan` | Show what is running |
501
+ | `deel scan --pick` | Choose one from the list |
502
+ | `deel scan --save` | Register everything found |
503
+ | `deel scan --ports 9000,9100` | Extra ports to probe |
504
+ | `deel scan --host <addr>` | Defaults to `127.0.0.1` |
505
+
506
+ Switch with `/model` mid-conversation — **the conversation carries over.**
507
+
508
+ > **More** — It adapts to whatever model is attached · Korean models are known before they are experienced · Small windows get a smaller fixed share · On startup it reads what kind of project this folder is
509
+ >
510
+ > **[Models read →](docs/en/models.md#multiple-local-runtimes)**
511
+
512
+ ---
513
+
514
+ ## Slash commands
515
+
516
+ Names follow Claude Code / Codex conventions.
517
+
518
+ | Command | What it does |
519
+ |---|---|
520
+ | `/help` | Command list |
521
+ | `/lang [ko\|en] [prompt lang]` | Screen language. **The prompt language is a separate axis** — `/lang en ko` instructs the model in Korean while answering you in English. Korean screen + English prompt cuts the 8k fixed share by 23% |
522
+ | `/keys` | Press keys to see what your terminal actually sends — for when new lines will not work |
523
+ | `/bell [on\|off]` | Ring and set the window title when a turn ends, or when deel needs an answer |
524
+ | `/consult <profile> <question>` | Ask a second model one question. Your current model stays put |
525
+ | `/export` | This conversation as a **one-page HTML report** — asked, changed, verified. Self-contained, opens on any network |
526
+ | `/lsp [on\|off]` | Language servers — what is installed, and whether `Def`/`Refs` are available. `off` turns post-edit diagnostics off only |
527
+ | `/context` | What is consuming the context window |
528
+ | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
529
+ | `/grade [small\|medium\|large\|auto]` | Model **grade** — how much it does on its own. A different axis from `/ctx` |
530
+ | `/out [number\|auto]` | Cap on a **single reply** — raise it when large files get cut |
531
+ | `/compact` | Summarise and fold older turns |
532
+ | `/clear` | Clear the conversation (keeps link and rules) |
533
+ | `/thread [new\|fork\|close\|n]` | Conversation threads — side work in its own context. Link and undo stay shared |
534
+ | `/learned [clear]` | What deel has picked up on its own — commands that work here, this model's habits |
535
+ | `/pin <text>` | Pin a line — folding and compaction **cannot reach it** |
536
+ | `/evidence [file]` | Evidence — what changed, and what proves it. **What is unproven is listed too** |
537
+ | `/commit [all\|preview\|title]` | Commits only what this session changed; message from the diff and the evidence. **Never pushes** |
538
+ | `/model` | Switch connection / model |
539
+ | `/model card` | Model card — what this model has actually done here, and what deel changed because of it |
540
+ | `/think <level>` | Reasoning level (`off·low·medium·high·xhigh·max`) — a rung this endpoint does not accept is lowered to one it does |
541
+ | `/think auto` | Effort follows the request — your setting becomes the **ceiling** |
542
+ | `/think profile <name>` | Per-stage profile (`even·save·deep`) |
543
+ | `/think detail` | Stage tablewhich stage runs at which level and cap |
544
+ | `/mode <mode>` | Approval policyhow much it asks (`auto` · `confirm` · `strict`) |
545
+ | `/work [mode]` | Work modewhat kind of work you are doing |
546
+ | `/auto` | Hand the wheel back it picks the mode from what you type |
547
+ | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
548
+ | `/level [level]` | How much to show (`simple` · `developer`) |
549
+ | `/motion [plain\|knight\|animal\|office\|off]` | What animates while it works — takes effect at once, and is saved |
550
+ | `/undo [turns]` | Revert file changes |
551
+ | `/diff [file]` | Files changed this session, and the changed lines |
552
+ | `/preview [folder\|file\|off]` | Serve what you built, right here — a browser opens with it |
553
+ | `/tools` | Available tools |
554
+ | `/skills [query\|all\|off]` | Browse, search, load skills |
555
+ | `/plugin [install\|remove\|pack]` | Manage plugins |
556
+ | `/cost` | Session usage |
557
+ | `/status` | Connection status |
558
+ | `/scan [save]` | Sweep this machine for local model servers (`save` registers them) |
559
+ | `/sessions` | Past conversations in this folder |
560
+ | `/recall <text>` | Search past conversations **by content** |
561
+ | `/memory` | What persists across sessions view, add, delete |
562
+ | `/mcp` | Externally attached tools (MCP servers) |
563
+ | `/init` | Create a `DEEL.md` rules file |
564
+ | `/exit` | Quit |
565
+
566
+ Discovered plugin commands are invoked as `/<plugin>:<name>`, with `$ARGUMENTS` substituted.
567
+
568
+ `/scan` and `/sessions` work without leaving the session. If you just started another local
569
+ server or loaded a different model, `/scan save` then `/model` switches over without losing
570
+ the conversation.
571
+
572
+ **Without typing**
573
+
574
+ | Key | What it does |
575
+ |---|---|
576
+ | `Tab` | Completes the `/` command you are typing. Candidates appear under the box as you type |
577
+ | `Shift+Tab` | Approval policy (`⏵⏵ auto` → `⏵ risky only` → `⏸ everything`) |
578
+ | `Ctrl+O` | Work mode (`Auto` → `Code` → `Plan` → …) |
579
+ | `↑` `↓` | Input history |
580
+ | `Ctrl+C` | Stops the answer in progress; twice on an empty line quits |
581
+ | Typing while it works, then `Enter` | Steers without throwing anything away — takes effect from the next step |
582
+
583
+ Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
584
+
585
+ > **More** — Attaching a file with @ · Interrupting · Steering without stopping
586
+ >
587
+ > **[The screen read →](docs/en/interface.md#slash-commands)**
588
+
589
+ ---
590
+
591
+ ## Work modes
592
+
593
+ What you are working on changes **which tools the model is given and how hard it thinks.**
594
+ Cycle with `Shift+Tab`, or type the name.
595
+
596
+ | Mode | For | Can edit files | Reasoning |
597
+ |---|---|---|---|
598
+ | `/auto` Auto | **Default.** Reads your message and switches for you | Yes | Normal (`save`) |
599
+ | `/code` Code | Writing and fixing | Yes | Normal (`save`) |
600
+ | `/plan` Plan | Planning first | **No** | Deep (`deep`·high) |
601
+ | `/architect` Architect | Shaping structure | **No** | Deep (`deep`·high) |
602
+ | `/debug` Debug | Finding causes | Yes | Deep, more steps (32) |
603
+ | `/ask` Ask | Explaining only | **No** | Shallow (`low`) |
604
+ | `/orchestrator` ❋ Orchestrator | Breaking up large work | Yes | Many steps (40) |
605
+
606
+ In read-only modes, `Write`, `Edit` and `Bash` are **never sent to the model at all.**
607
+ It is not asked politely not to edit — models forget requests. A tool that isn't there can't be used.
608
+
609
+ Don't confuse this with `/mode`. They are separate axes:
610
+
611
+ - `/mode` — **how much it asks you** (auto · confirm · strict)
612
+ - `/work` — **what kind of work you are doing** (the seven above)
613
+
614
+ If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
615
+ overrides something a person chose.
616
+
617
+ > **More** — Switching by itself (Auto mode)
618
+ >
619
+ > **[The screen read →](docs/en/interface.md#work-modes)**
620
+
621
+ ---
622
+
623
+ ## Simple vs developer
624
+
625
+ Twenty commands on first launch means nothing gets chosen. Locking features away means
626
+ hitting a wall later. So only **what is shown** differs.
627
+
628
+ | | Simple (default) | Developer |
629
+ |---|---|---|
630
+ | `/help` listing | Common commands only | Everything |
631
+ | Error messages | What to do about it | The original text |
632
+ | Safety | **Identical** | **Identical** |
633
+
634
+ `/level developer` is saved to config and persists across sessions.
635
+
636
+ Two things matter here:
637
+
638
+ - **Hidden commands still work.** `/think high` works in simple mode. It just isn't listed.
639
+ - **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
640
+ blocking are identical. A beginner needs the undo more, not less.
641
+
642
+ > **More** — The input box · You don't have to type the whole command · The box stays while it works · The picture on the left moves too and 1 more
643
+ >
644
+ > **[The screen read →](docs/en/interface.md#simple-vs-developer)**
645
+
646
+ ---
647
+
648
+ ## Tools
649
+
650
+ Names and arguments match Claude Code, so skills written for that convention work unchanged.
651
+
652
+ | Tool | What it does |
653
+ |---|---|
654
+ | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV, hwpx/docx/pptx and PDF as text**) |
655
+ | `Write` | Write / overwrite a file (**several at once via the `files` array**) |
656
+ | `Append` | Append to the end of a file — **how large files get written in pieces** |
657
+ | `Edit` | Replace an exact string (`replace_all`; **several sites at once via the `edits` array**) |
658
+ | `Move` | Move / rename files and folders — **how you restructure** (`moves` array; covered by undo) |
659
+ | `Glob` | Find files by name pattern |
660
+ | `Grep` | Regex search file contents |
661
+ | `Bash` | Run a command (**`background: true` for anything that does not finish**) |
662
+ | `Skill` | Expand a skill body (shown to the model only when skills exist) |
663
+ | `WebFetch` | Read a web page (read-only; hidden under `--offline`) |
664
+ | `Ask` | **Ask you back** at a fork offers the choices, takes a single number |
665
+ | `Recall` | Search **past conversations** the model digs up "that thing last time" itself |
666
+ | `Remember` | One line that outlives the session known from the start next time |
667
+ | `TodoWrite` | Checklistbreaks long work into steps and shows progress |
668
+ | `Outline` | See a folder's **skeleton only** — tens of times cheaper than reading it whole |
669
+ | `Verify` | Check that what was built **actually works** |
670
+ | `Task` | Run one chunk of a big job in a **separate context** |
671
+ | `Jobs` | Inspect, read and stop **background commands** the other half of `Bash`'s `background` |
672
+ | `Def` | **Where a name is defined** — only shown when a language server is installed |
673
+ | `Refs` | **Every place a name is used** — only shown when a language server is installed |
674
+
675
+ Nine tools here are not in Claude Code `Append`, `Move`, `Ask`, `Recall`, `Remember`,
676
+ `Outline`, `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
677
+ so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
678
+ their cost; here is why.
679
+
680
+ > **More** — Outline · Verify · Task · Def · Refs · Commands that never finish and 9 more
681
+ >
682
+ > **[Tools in depth read →](docs/en/tools.md#tools)**
683
+
684
+ ---
685
+
686
+ ## Korean text and Excel
687
+
688
+ **A file saved as CP949 is written back as CP949.** The encoding is never changed.
689
+ Excel (`.xlsx`) is read as CSV — read-only.
690
+
691
+ Old formats (`.ppt`, `.doc`, `.xls`, `.rtf`) are read by **borrowing the LibreOffice already
692
+ on this machine** the same terms on which deel borrows `rg`, and nothing is ever installed.
693
+ With no converter it **says so definitively and stops**: what is missing, what you can do about
694
+ it, and not to open the file again. Turn it off with `DEEL_CONVERT=off`.
695
+
696
+ > **More** — Encoding · Excel · borrowing a converter
697
+ >
698
+ > **[Korean documents and Excel read →](docs/en/documents.md#korean-text-and-excel)**
699
+
700
+ ---
701
+
702
+ ## Serving what you built
703
+
704
+ ```
705
+ ❯ /preview
706
+
707
+ Serving http://127.0.0.1:56801/
708
+ showing .
709
+ Edit a file and the page reloads by itself.
710
+ Only this machine can open it (127.0.0.1). No other PC can see it.
711
+ Stop with /preview off · it shuts down when deel exits.
712
+ ```
713
+
714
+ A browser opens with it. `/preview <folder>` picks what to serve, `/preview off` stops it.
715
+
716
+ **This is not the same as double-clicking the file (`file://`).** Under `file://` everything
717
+ below is blocked and the error only shows up in the console while the page stays blank, so
718
+ you end up suspecting your own code. This is a real HTTP server, so it all works:
719
+
720
+ | | `file://` | `/preview` |
721
+ |---|---|---|
722
+ | `<script type="module">` · `import` | blocked (CORS) | **works** |
723
+ | `fetch('./data.json')` | blocked | **works** |
724
+ | `new Worker(...)` | blocked | **works** |
725
+ | `WebAssembly.compileStreaming` | blocked (MIME) | **works** |
726
+ | textures · `getImageData` | tainted canvas | **works** |
727
+ | `.glb` / `.gltf` (Three.js) | no MIME type → silently not drawn | **works** |
728
+
729
+ All seven were run in a real Chrome and confirmed **7/7**.
730
+
731
+ Apps with a router (React Router and friends) get the first page back when you reload on a
732
+ deep link. Never for requests with an extension (`app.js`) though returning HTML for a
733
+ missing script dies with `Unexpected token '<'`, which hides the real cause (a typo in a filename).
734
+
735
+ ### It opens exactly as much as it says
736
+
737
+ Starting a server means opening your disk to somebody else.
738
+
739
+ - Bound to **`127.0.0.1` only**. `0.0.0.0` is not available at all — on an office network
740
+ that would let anyone read your source.
741
+ - Port **0** (the kernel hands out a free one). A fixed port steals someone else's.
742
+ - Paths cannot leave the working scope. `../` · `%2e%2e` · double encoding · absolute paths ·
743
+ null bytes · symlinks eight of these are held shut by tests.
744
+ - **It only serves.** `POST` · `PUT` · `DELETE` are refused with 405.
745
+ - It shuts down when `deel` exits.
746
+
747
+ ---
748
+
749
+ ## Skills and plugins
750
+
751
+ **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
752
+ whatever is there. On a clean PC: zero. On a PC with skills installed: those skills.
753
+
754
+ ```
755
+ project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
756
+ user ~/.deel/skills ~/.claude/skills ~/.claude/commands
757
+ plugins ~/.claude/plugins/** ~/.deel/plugins/**
758
+ ```
759
+
760
+ Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
761
+
762
+ > **More** — Loaded in three stages · Fetching plugins · Deliberately not included
763
+ >
764
+ > **[Extending read →](docs/en/extend.md#skills-and-plugins)**
765
+
766
+ ---
767
+
768
+ ### The hidden latency of local models — keeping the prefix cache alive
769
+
770
+ Ollama and llama.cpp reuse computation **only while the request starts the same way as the
771
+ last one.** Change one early character and everything after it the entire conversation
772
+ is recomputed. This is the usual hidden reason long local sessions feel slower and slower,
773
+ and it never shows up anywhere, because it is not an error.
774
+
775
+ deel routes every message to the right mode automatically, and that mode instruction used to
776
+ sit **early** in the prompt every mode switch broke the whole cache. So the stable parts
777
+ (rules, folder, project fingerprint, user rules, memory, skills) are frozen at the front and
778
+ the per-turn parts (mode, pins) go last. A test pins this order down (`test/cache.test.js`).
779
+
780
+ Ollama also gets `keep_alive: 60m` — with the 5-minute default, the model unloads while you
781
+ glance at another window, and the first message after you come back recomputes everything.
782
+ Override with `DEEL_KEEP_ALIVE`. If you run llama.cpp directly, `--cache-reuse 256` on the
783
+ server side does the same job.
784
+
785
+ ## Reasoning effort
786
+
787
+ One answer means several model calls, and **each needs a different amount of thinking.**
788
+ All-high is slow; all-low wanders off.
789
+
790
+ The default is **one line**. What you want to know is how hard it is thinking right now,
791
+ not a stage table.
792
+
793
+ ```
794
+ $ /think
795
+
796
+ Effort medium (First call medium · Continue low · Stuck high)
797
+ harder /think high faster /think low
798
+ ```
799
+
800
+ | Profile | Character |
801
+ |---|---|
802
+ | `even` | Same effort everywhere predictable, slower |
803
+ | `save` (default) | Hard on the first decision only |
804
+ | `deep` | Everything one notch up — for hard work |
805
+
806
+ Set the profile with `/think profile save`. **Level and profile are different axes, so the
807
+ commands were split** `/think high` and `/think save` used to set different things under
808
+ one name, which made the screen unreadable.
809
+
810
+ The stage table moved to `/think detail` (the default at developer level).
811
+
812
+ ```
813
+ $ /think detail
814
+
815
+ Effort medium (First call medium · Continue low · Stuck high)
816
+ Profile save Hard on the first call, shallow while continuing - usually the better trade
817
+
818
+ Stage Effort Out cap When
819
+ First call · medium 14,069 Deciding what to do
820
+ Continue ↓ low 12,310 Reading a tool result, picking the next move
821
+ Stuck ↑ high 15,827 The last tool returned an error
822
+
823
+ Caps are shared inside 16,384 (unknown, so a default) — /out
824
+ Context 40,960 · used 5,787
825
+ ```
826
+
827
+ That second-to-last line exists for a reason: **when all three caps are equal, it is the
828
+ only thing that says whether that is correct.** A low known cap makes them equal, and that
829
+ is fine. For a while all three read `16,384` always — which meant the table said nothing.
830
+
831
+ > **More** — Context length is read off the model · /out · Truncated tool calls
832
+ >
833
+ > **[Speed and spend read →](docs/en/tuning.md#reasoning-effort)**
834
+
835
+ ---
836
+
837
+ ## Auto-compaction
838
+
839
+ At 80% context, older turns are **summarised and folded** so work continues.
840
+ Plain truncation makes the model forget: it re-reads files and re-fixes what it already fixed.
841
+
842
+ ```
843
+ ◱ Folded 44 turns into a summary — 10,399 → 3,170 tokens (70% smaller)
844
+ ```
845
+
846
+ The summary keeps goal / done / learned / decided / remaining. The cut point is chosen so a
847
+ **tool call is never separated from its result** splitting them makes the server return 400.
848
+ If the summary request fails, it falls back to plain trimming rather than stopping.
849
+
850
+ `/compact` folds on demand.
851
+
852
+ ---
853
+
854
+ ## Resuming a conversation
855
+
856
+ Close the terminal by accident, or reboot, and the conversation is still there.
857
+ Messages are written to `.deel/sessions/` **as each one completes**, so a crash
858
+ loses at most the message in flight.
859
+
860
+ ```
861
+ $ deel sessions
862
+
863
+ ── conversations in this folder ────────────────────────────────
864
+ 20260824-090200 just now 1 turn devstral-small-2507
865
+ fix the failing test
866
+ · 20260824-084500 2h ago 2 turns qwen2.5-coder:7b
867
+ switch src/a.js logging to the logger
868
+ ```
869
+
870
+ | Command | What it does |
871
+ |---|---|
872
+ | `deel --continue` | Resume the most recent conversation in this folder |
873
+ | `deel --resume <id>` | Resume a specific one |
874
+ | `deel sessions` | List what is stored |
875
+ | `deel sessions --rm <id>` | Delete one |
876
+
877
+ The format is `jsonl` one message per line so a power cut costs only the last line.
878
+ Resumed history keeps tool calls paired with their results, so work continues immediately.
879
+ Conversations older than 30 days and outside the most recent 30 are pruned automatically.
880
+
881
+ Everything lives in `.deel/sessions/` inside the working folder, and `.gitignore`
882
+ covers `.deel/` so it never reaches a repository.
883
+
884
+ ---
885
+
886
+ ## Starting over (`deel reset`)
887
+
888
+ Switching gateways, learned facts that went stale, handing the machine to someone else
889
+ sometimes you want to go back to the beginning. Reinstalling does not do it: `~/.deel`
890
+ survives an install. Hence a command of its own.
891
+
892
+ ```
893
+ $ deel reset
894
+
895
+ ── what can be wiped ──────────────────────────────────────────
896
+ home C:\Users\me\.deel
897
+ working folder C:\work\myproject
898
+
899
+ connections 2
900
+ memory 8 lines
901
+ conversations 14
902
+ learned 2 places
903
+ evidence, exports, temp 6
904
+ plugins 3
905
+ sealed key DPAPI — this PC, this account only
906
+
907
+ ── what is kept ───────────────────────────────────────────────
908
+ undo snapshots 41 needs all --hard
909
+ audit log 1,203 lines needs all --hard
910
+ written by you .deel/mcp.json · .deelignore
911
+ never touched, by any route.
912
+ ```
913
+
914
+ **Bare `deel reset` wipes nothing.** It shows what exists and asks.
915
+
916
+ | Command | What goes |
917
+ |---|---|
918
+ | `deel reset model` | connections and profiles, plus the key in the OS keystore |
919
+ | `deel reset memory` | memory (`.deel/memory.md`) |
920
+ | `deel reset sessions` | conversation history |
921
+ | `deel reset learned` | learned facts (this PC + this folder) |
922
+ | `deel reset plugins` | installed plugins |
923
+ | `deel reset all` | everything above **except plugins** |
924
+ | `deel reset all --hard` | plus undo snapshots and the audit log |
925
+ | `--yes` | skip the question (scripts, first-time provisioning) |
926
+
927
+ What it does **not** touch matters more.
928
+
929
+ | Kept | Why |
930
+ |---|---|
931
+ | `.deel/history/` (undo snapshots) | This is the safety net offered in place of an approval prompt. `--hard` only |
932
+ | `.deel/audit.jsonl` | The evidence for an internal review. `--hard` only |
933
+ | `.deel/mcp.json` · `.deelignore` · `DEEL.md` | You wrote these by hand. **Never touched, by any route** |
934
+ | Everything else in the working folder | Nothing outside `.deel` and the home folder is ever reached |
935
+
936
+ It runs on a broken config. That is usually *why* someone reaches for a reset, so
937
+ `deel reset` starts without reading the connection the same reason `deel --version` answers
938
+ without one. Plugins take time to fetch again, so they stay out of `all`; ask for them by name.
939
+
940
+ ---
941
+
942
+ ## Attaching tools from outside (MCP)
943
+
944
+ A corporate wiki search, an issue tracker, a DB query tool if a team publishes one as an MCP
945
+ server, deel uses it as a tool **without a code change**.
946
+
947
+ Configure in `.deel/mcp.json`. A Claude Code config can be copied over verbatim:
948
+
949
+ ```json
950
+ { "mcpServers": { "wiki": { "command": "node", "args": ["wiki-mcp.js"] } } }
951
+ ```
952
+
953
+ The model sees it as `mcp__wiki__search`. `/mcp` shows what is attached.
954
+
955
+ **Dependencies stay at zero.** The stdio transport is nothing but newline-delimited JSON-RPC
956
+ 2.0 over a child process's stdin/stdout, so `child_process` and `JSON` cover it. No SDK.
957
+
958
+ > **More** — But this is somebody else's program
959
+ >
960
+ > **[Extending read →](docs/en/extend.md#attaching-tools-from-outside-mcp)**
961
+
962
+ ---
963
+
964
+ ## Inside your editor (ACP)
965
+
966
+ A tool that makes you open one more terminal window stops being used after about two weeks.
967
+ Developers live inside the IDE. So deel speaks **ACP** (Agent Client Protocol) Zed,
968
+ JetBrains, Neovim and Emacs attach to it **without changing a line on their side**.
969
+
970
+ One command in your editor's settings:
971
+
972
+ ```
973
+ deel acp
974
+ ```
975
+
976
+ The editor spawns that as a child process and exchanges newline-delimited JSON-RPC 2.0 over
977
+ stdio. It is not a command you type yourself.
978
+
979
+ **What you get once it is attached:**
980
+
981
+ | In the editor | From deel |
982
+ |---|---|
983
+ | Streaming reply pane | The model's text and its reasoning |
984
+ | 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 |
985
+ | Clickable file links | The **absolute path** of every file touched |
986
+ | Approval dialog | deel's safety rails, rendered as the editor's own prompt (`allow once` · `always allow` · `reject`) |
987
+ | Mode picker | deel's seven work modes (auto · code · plan · architect · debug · ask · orchestrator) |
988
+ | Stop button | Reaches the turn mid-flight, even while waiting on the model |
989
+ | Past conversations | Still there after a restart. They live in the **same place** as the terminal's, so a session started in the editor can be picked up with `deel --resume` |
990
+
991
+ **Still zero dependencies.** Same reason as MCP — newline-delimited JSON-RPC 2.0 is the whole
992
+ transport, so no SDK is needed.
993
+
994
+ > **More** — Details — the places this breaks silently
995
+ >
996
+ > **[Extending read →](docs/en/extend.md#inside-your-editor-acp)**
997
+
998
+ ---
999
+
1000
+ ## Keeping secrets out of the conversation
1001
+
1002
+ People rarely paste a key. The leak is almost always **command output**.
1003
+
1004
+ ```
1005
+ env OPENAI_API_KEY=sk-proj-…
1006
+ git remote -v https://user:token@github.com/…
1007
+ curl -v > Authorization: Bearer eyJ…
1008
+ a failing test log the whole connection string
1009
+ ```
1010
+
1011
+ That text goes to the model **and** gets written to `.deel/sessions/*.jsonl` on disk. That
1012
+ file is later re-read by `/recall` and can end up inside a `deel pack` bundle. Leak once and
1013
+ you have several copies.
1014
+
1015
+ So it is masked at the single point where tool output enters the conversation.
1016
+
1017
+ ```
1018
+ Bash(env | grep API) done
1019
+ ⊘ 2 secret-looking values entered the conversation (openai · env var) — masked before the model
1020
+ ```
1021
+
1022
+ What it looks for: private-key blocks · OpenAI/Anthropic keys · GitHub tokens · Slack tokens ·
1023
+ AWS keys · Google keys · JWTs · credentials embedded in URLs · `Authorization`-family headers ·
1024
+ env vars named `…KEY` / `…TOKEN` / `…SECRET` / `…PASSWORD`. Plus **the configured gateway key
1025
+ regardless of its shape** — that one is not a guess, it is a known value.
1026
+
1027
+ ### File contents are deliberately not masked
1028
+
1029
+ `.env` is exactly where masking feels most tempting, and exactly where it backfires: the model
1030
+ sees the masked text, edits it, writes it back — and `«가림»` lands where the real key was.
1031
+ **Protecting the secret would destroy it.**
1032
+
1033
+ So on the file side it reports instead of rewriting.
1034
+
1035
+ ```
1036
+ Read(.env) 12 lines
1037
+ ! 3 secret-looking values entered the conversation (env var)
1038
+ — file contents are not masked (masking them would erase the key on write-back)
1039
+ ```
1040
+
1041
+ Saying plainly what cannot be stopped beats claiming it was stopped while corrupting the file.
1042
+ Either way it lands in the audit log.
1043
+
1044
+ ---
1045
+
1046
+ ## Safety
1047
+
1048
+ Instead of approval prompts, the design makes things **reversible**. The default `auto` mode
1049
+ does not ask.
1050
+
1051
+ | Mechanism | Detail |
1052
+ |---|---|
1053
+ | **Undo** | Snapshot before every write. `/undo` restores per turn. **Includes moves and deletes done through `Bash`** |
1054
+ | **Change display** | The changed lines are shown on every edit; `/diff` for the whole session |
1055
+ | **Scope** | Outside the starting folder is refused, even if the model insists |
1056
+ | **Blocked commands** | Only irreversible ones (disk format, recursive delete, `--force` push) |
1057
+ | **No re-run** | A mutating command is never retried after failure |
1058
+ | **Interrupt** | Ctrl+C stops mid-answer and leaves the conversation valid |
1059
+ | **Spin guard** | Three identical failures stop the turn, with the reason |
1060
+ | **Not read** | Other tools' private stores, and deel's own logs and config (the key), are refused |
1061
+ | **Audit log** | Everything recorded in `.deel/audit.jsonl` |
1062
+
1063
+ | Mode | Asks when |
1064
+ |---|---|
1065
+ | `auto` (default) | Never undo is the safety net |
1066
+ | `confirm` | Irreversible commands only |
1067
+ | `strict` | All file changes and commands |
1068
+
1069
+ Undo history stores whole file contents, so repeated edits to large files add up. Past 32MB
1070
+ it keeps the **most recent 50 turns** and drops the rest. What you just did is always
1071
+ undoable; `/status` shows how large the history currently is.
1072
+
1073
+ > **More** — Files removed through Bash come back too · What it will not read
1074
+ >
1075
+ > **[Safety and corporate review read →](docs/en/safety.md#safety)**
1076
+
1077
+ ---
1078
+
1079
+ ## Corporate review package
1080
+
1081
+ ```bash
1082
+ deel pack --out deel-import.zip
1083
+ ```
1084
+
1085
+ ```
1086
+ deel-import.zip
1087
+ 94 files · 509.6KB
1088
+
1089
+ Dependencies 0
1090
+ Install scripts none
1091
+ External imports 0
1092
+ Network calls 3 sites (configured address only)
1093
+ Ports opened 1 site (/preview only)
1094
+ ```
1095
+
1096
+ The zip carries **one document for people and two for machines.** A corporate review is
1097
+ not a human-only process — security feeds an SBOM to a scanner, and operations reads the
1098
+ audit-log spec to write SIEM ingestion rules.
1099
+
1100
+ | File | What |
1101
+ |---|---|
1102
+ | `import-review.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 |
1103
+ | `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 |
1104
+ | `audit-spec.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 |
1105
+
1106
+ ```bash
1107
+ deel audit # the human-readable sheet only
1108
+ deel sbom # the two machine-readable ones, on stdout (deel sbom | jq)
1109
+ deel sbom --out review.json # to a file
1110
+ deel sbom --only sbom # just the SBOM
1111
+ ```
1112
+
1113
+ With the screen language set to English these three, and the `deel pack` archive itself,
1114
+ come out in English — file names included (`deel pack` writes `deel-import.zip`).
1115
+
1116
+ All three are generated by scanning the source, never written by hand hand-written sheets
1117
+ drift, and **a review document that drifts is worse than none.** Find one wrong line and the
1118
+ reviewer stops trusting the rest. The audit-log spec is the one hand-written part, so a test
1119
+ checks it against real log records on every run.
1120
+
1121
+ > **More** — Diagnosing a corporate gateway
1122
+ >
1123
+ > **[Safety and corporate review read →](docs/en/safety.md#corporate-review-package)**
1124
+
1125
+ ---
1126
+
1127
+ ## Configuration
1128
+
1129
+ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder takes precedence.
1130
+
1131
+ > **More** — Supported servers · Environment variables · Flags · Project rules
1132
+ >
1133
+ > **[Configuration read →](docs/en/config.md#configuration)**
1134
+
1135
+ ---
1136
+
1137
+ ## Troubleshooting
1138
+
1139
+ | Symptom | Check |
1140
+ |---|---|
1141
+ | `address not found` | Typo, DNS, VPN / intranet connectivity |
1142
+ | `connection refused` | Server is down or the port differs |
1143
+ | certificate error | `set NODE_EXTRA_CA_CERTS=C:\path\corp-ca.pem` |
1144
+ | behind a proxy | `set HTTPS_PROXY=http://proxy:port` (with auth: `http://user:pw@proxy:port`). If the first screen and `/status` show `proxy …`, it is in use. Exclude hosts with `NO_PROXY=.corp.com,10.1.2.3`; turn it off entirely with `"proxy": "none"` in the config |
1145
+ | the proxy answers 407 | Put `user:pw@` into the proxy address. Proxies that only accept NTLM · Negotiate are not supported — ask the admin for Basic or an unauthenticated address |
1146
+ | 401 / 403 | Wrong key or auth header style (four are tried automatically) |
1147
+ | `address not permitted` | The lock did its job pick a connection with `/model` |
1148
+ | Tool calls don't work | Run `deel diagnose`. Small models (1B–3B) often can't |
1149
+ | Empty replies | The server ignores streaming. deel retries once, then turns streaming off for the session |
1150
+ | 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 |
1151
+ | 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 |
1152
+ | `429` · `503` shows | The gateway pushed back for a moment. deel waits and calls again, up to three times (honouring `Retry-After`). If it keeps happening, check your quota |
1153
+ | `deel scan` finds nothing | Server is off or on another port — use `--ports` |
1154
+
1155
+ ---
1156
+
1157
+ ## Development
1158
+
1159
+ ```bash
1160
+ npm test Full suite (~5,890 checks; a few are TTY-dependent)
1161
+ npm run coverage Which lines the tests actually execute
1162
+ npm run verify Import + network checks only
1163
+ npm run bench Edit success rate
1164
+ npm run demo See what the UI actually looks like
1165
+ npm run check Syntax check every file
1166
+ ```
1167
+
1168
+ Tests run against a **fake gateway**, so the loop, streaming, tool execution, undo and compaction
1169
+ are verified deterministically without any model. ZIP output is cross-checked with the real
1170
+ `unzip`; the TAR reader is fed archives produced by the real `tar`.
1171
+
1172
+ `npm test` runs each file separately and reports **per-file exit codes**, because the exit code
1173
+ not the pass marks on screen is what CI reads, and the two can disagree: a file can pass
1174
+ every check and still die on the way out, leaving the screen green and the exit code 1. That
1175
+ happened once on Windows and cost a lot of time. The runner does not stop at the first failure,
1176
+ so one run tells you everything.
1177
+
1178
+ | Suite | Checks | Covers |
1179
+ |---|---|---|
1180
+ | `smoke` | 20 | Tools, scope, undo, audit log |
1181
+ | `loop` | 16 | Agent loop, streaming, tool calls |
1182
+ | `guard` | 24 | **What it refuses to do** — denied edits, unknown tools, repeated mutations, out-of-scope writes |
1183
+ | `network` | 30 | Nothing escapes the configured address |
1184
+ | `web` | 25 | Web reads stay read-only |
1185
+ | `abort` · `steer` | 16 · 15 | Ctrl+C leaves the conversation valid · a line typed mid-turn rides the next call |
1186
+ | `parallel` | 23 | Read-only tools run together; checklists |
1187
+ | `cli` | 75 | **Spawns the real `deel`** and drives it to completion |
1188
+ | `setup` | 42 | First-run wizard, driven through a fake TTY |
1189
+ | `detect` | 66 | Identifying shape and auth from one address |
1190
+ | `modes` · `route` | 89 · 33 | Work modes; auto-switching from Auto |
1191
+ | `ctxsize` | 43 | Reading context length off the model |
1192
+ | `commands` · `commands-more` | 128 · 62 | Every slash command |
1193
+ | `ui` · `ui2` | 60 · 40 | Password masking, CJK width, status line, session list, Excel→text |
1194
+ | `encoding` · `xlsx` | 68 · 72 | Legacy-encoding detection; Excel reading |
1195
+ | `compact` | 21 | Summary folding, pairing intact, graceful fallback |
1196
+ | `store` | 34 | Session persistence, resume, crash recovery |
1197
+ | `scan` | 29 | Distinguishing multiple runtimes |
1198
+ | `plugins` | 38 | Plugin fetch/pack, ZIP/TAR |
1199
+ | `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
1200
+ | `edit-bench` | 20 cases | Edit success rate |
1201
+
1202
+ > **More** — Coverage · Layout
1203
+ >
1204
+ > **[Development read →](docs/en/develop.md#development)**
1205
+
1206
+ ---
1207
+
1208
+ ## Release notes
1209
+
1210
+ | Version | What changed |
1211
+ |---|---|
1212
+ | **[1.10.0](docs/en/releases/1.10.md#1100)** | ESC actually stops · a full context carries on inside the same turn · every vendor endpoint measured |
1213
+ | [1.9.2](docs/en/releases/1.9.md#192) | Guards that said they were blocking were not blocking · folding lost the request and the outstanding work |
1214
+ | [1.9.1](docs/en/releases/1.9.md#191) | MCP tools with Korean names all looked the same to the model names are now distinct and stable |
1215
+ | **[1.9.0](docs/en/releases/1.9.md#190)** | Tables are drawn as tables · reasoning effort reaches Claude and Bedrock · tool schemas shaped per vendor · only the changed part of a file is re-sent |
1216
+ | **[1.8.0](docs/en/releases/1.8.md#180)** | A full day against a gateway that hands out one-hour tokens — fetch the key instead of storing it · `deel reset` · an English screen that is actually English |
1217
+
1218
+ The five most recent are listed here. Every version, and why each thing changed, is in the **[release notes](docs/en/releases.md)**.
1219
+
1220
+ ---
1221
+
1222
+ ## Licence
1223
+
1224
+ [MIT](LICENSE)