deel-local-cli 0.5.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -1,246 +1,852 @@
1
- # deel-local-cli
1
+ <div align="center">
2
2
 
3
- A coding agent CLI that runs entirely on **local models or your own private gateway**.
4
- **Zero dependencies** — nothing but Node's own standard library.
3
+ # deel
5
4
 
6
- > The interface and source comments are in Korean. This file is the English guide.
5
+ **A coding-agent CLI that runs on local models and private gateways only**
7
6
 
8
- ---
7
+ Zero dependencies · Node 20+ · Exactly one place your source can go
8
+
9
+ [한국어](README.md) · [Corporate review](#corporate-review-package) · [Troubleshooting](#troubleshooting)
9
10
 
10
- ## Why zero dependencies
11
+ </div>
11
12
 
12
- This was built for an offline corporate network where installing third-party
13
- software requires review. `dependencies` is empty and stays empty — that fact is
14
- the argument you hand to a security team.
13
+ ---
15
14
 
16
15
  ```
17
- npm ls → no dependencies
18
- cat package.json "dependencies": {}
16
+ ╭──────────────────────────────────────────────────────────────╮
17
+ deel OpenAI-compatible │
18
+ │ │
19
+ │ Model qwen2.5-coder:7b (40k tokens) │
20
+ │ Sends to this machine 127.0.0.1:11434 ← nowhere else │
21
+ │ Link streaming · tools · reasoning control │
22
+ │ Folder C:\work\myproject │
23
+ │ This PC 337 skills · 127 commands · 42 plugins │
24
+ ╰──────────────────────────────────────────────────────────────╯
25
+
26
+ ▏myproject ▏qwen2.5-coder:7b ▏▰▰▱▱▱▱▱▱▱▱ 22% 28k/128k ▏◇ medium·save ▏auto
27
+ ❯ unify the logging style
28
+
29
+ ❊ Grep(console.log)
30
+ └ 1 file · 1 hit
31
+ ◧ Read(src/runner.js)
32
+ └ 5 lines
33
+ ◈ Edit(src/runner.js)
34
+ └ 1 spot
35
+
36
+ Unified log calls to the logger format. One change in runner.js.
37
+
38
+ ── 4.2s · 3 tools · ↑3,900 ↓180
19
39
  ```
20
40
 
21
- All you need is **Node 20+**. There is no `npm install` step.
41
+ ---
42
+
43
+ ## Contents
44
+
45
+ - [Why this exists](#why-this-exists)
46
+ - [Quick start](#quick-start)
47
+ - [Where your data can go](#where-your-data-can-go)
48
+ - [Multiple local runtimes](#multiple-local-runtimes)
49
+ - [Slash commands](#slash-commands)
50
+ - [Work modes](#work-modes)
51
+ - [Simple vs developer](#simple-vs-developer)
52
+ - [Tools](#tools)
53
+ - [Korean text and Excel](#korean-text-and-excel)
54
+ - [Skills and plugins](#skills-and-plugins)
55
+ - [Reasoning effort](#reasoning-effort)
56
+ - [Auto-compaction](#auto-compaction)
57
+ - [Resuming a conversation](#resuming-a-conversation)
58
+ - [Safety](#safety)
59
+ - [Corporate review package](#corporate-review-package)
60
+ - [Configuration](#configuration)
61
+ - [Troubleshooting](#troubleshooting)
62
+ - [Development](#development)
22
63
 
23
64
  ---
24
65
 
25
- ## Install
66
+ ## Why this exists
67
+
68
+ When a corporate security policy blocks **unapproved software**, most coding-agent tools are
69
+ unusable: hundreds of transitive dependencies, scripts that run at install time, and no
70
+ one-line answer to "where does it send my code?"
71
+
72
+ deel is built to pass that review.
73
+
74
+ | | deel |
75
+ |---|---|
76
+ | External dependencies | **0** — Node built-ins only |
77
+ | Install scripts | **none** — unzip and run |
78
+ | Where source can go | **one address** — the one you configured |
79
+ | Requirement | Node 20+ |
80
+
81
+ Verify it yourself:
82
+
83
+ ```bash
84
+ npm view deel-local-cli dependencies # {}
85
+ npm view deel-local-cli scripts # no install/postinstall
86
+ deel audit # full review sheet
87
+ ```
88
+
89
+ ---
90
+
91
+ ## Quick start
92
+
93
+ ### Install
26
94
 
27
95
  ```bash
28
96
  npm install -g deel-local-cli
29
- # or run without installing
30
- npx deel-local-cli setup
31
97
  ```
32
98
 
33
- For an air-gapped machine, copy the folder across and run `node bin/deel.js`
34
- directly. Nothing is installed on the target machine.
99
+ Or skip installing entirely there is no `npm install` step:
35
100
 
36
- ---
101
+ ```bash
102
+ git clone https://github.com/jysvai/deel-local-cli
103
+ node deel-local-cli/bin/deel.js
104
+ ```
105
+
106
+ > **Note** — do not run `npm install` in your home directory. A `node_modules` there makes every
107
+ > later npm command scan it and report warnings about unrelated packages. Use `-g` or `npx`.
108
+
109
+ ### Point it at a model
110
+
111
+ Scan this machine and pick one:
112
+
113
+ ```bash
114
+ deel scan --pick
115
+ ```
37
116
 
38
- ## Connect
117
+ Or enter an address directly (use this for a corporate gateway):
39
118
 
40
119
  ```bash
41
120
  deel setup
42
121
  ```
43
122
 
44
- It asks for a name, base URL and API key, probes the endpoint, lists the models
45
- it found, and saves the profile to `~/.deel/config.json`.
123
+ ### Start
46
124
 
47
- Point it at anything OpenAI-compatible:
125
+ Run `deel` in the folder you want to work in. **That folder becomes the scope — files outside it
126
+ cannot be read or written.**
48
127
 
49
- | | Example URL |
50
- |---|---|
51
- | Private AI gateway | `https://ai-gw.example.corp/v1` |
52
- | Ollama | `http://localhost:11434` |
53
- | LM Studio | `http://localhost:1234/v1` |
54
- | vLLM / LiteLLM | `http://host:port/v1` |
128
+ ```bash
129
+ cd C:\work\myproject
130
+ deel
131
+ ```
132
+
133
+ ---
55
134
 
56
- The auth style is detected automatically — `Authorization: Bearer`, `x-api-key`,
57
- `api-key` (Azure-style), or none.
135
+ ## Where your data can go
58
136
 
59
- ### Keeping the key out of the config file
137
+ A coding agent ships your whole source to a model. **The address is everything.**
138
+ Rather than promising in prose, the code enforces it: `src/safety/network.js` checks every request
139
+ and never builds one for an address that is not on the allow-list.
140
+
141
+ ```
142
+ [A] Model gateway ────── the only path your source travels
143
+ One address, set in `setup`. Switching models closes the previous one.
144
+
145
+ [B] Web read (WebFetch) ─ receive-only
146
+ GET only, zero-byte body. Private/loopback addresses refused. Every visit logged.
147
+
148
+ [C] Plugin fetch ─────── open only while /plugin install runs
149
+ ```
150
+
151
+ Pass `--offline` and **both B and C are closed** — traffic stays on this machine.
60
152
 
61
153
  ```bash
62
- export DEEL_API_KEY=sk-xxxx # takes precedence over the file
63
- deel diagnose --url https://ai-gw.example.corp/v1 --model sec-llm-01
154
+ deel --offline
155
+ ```
156
+
157
+ The destination is printed at the top of every session:
158
+
64
159
  ```
160
+ Sends to this machine 127.0.0.1:11434 ← nowhere else
161
+ ```
162
+
163
+ Nothing is collected or transmitted. No telemetry, no usage stats, no crash reporting.
164
+ Conversation history, undo snapshots and config live only in `.deel/` inside your working folder.
65
165
 
66
- Other environment variables: `NODE_EXTRA_CA_CERTS` (corporate CA),
67
- `HTTPS_PROXY`, `DEEL_DEBUG=1` (full stack traces).
166
+ > Verified by 55 checks in `npm test` (network + web), including bringing up a real server and
167
+ > confirming that **not a single request reaches it** when it is not allow-listed.
68
168
 
69
169
  ---
70
170
 
71
- ## Diagnose a gateway
171
+ ## Multiple local runtimes
72
172
 
73
- Before trusting an endpoint, find out what it actually supports:
173
+ People rarely run just one. `deel scan` knocks on 13 known ports concurrently and identifies
174
+ each runtime from its **response**, not its port number — Ollama by `/api/version`,
175
+ LM Studio by `/api/v0/models`, llama.cpp by `/props`. Unrecognised ones are marked as a guess.
74
176
 
75
- ```bash
76
- deel diagnose --url <base-url> --key <key> --model <model> --out report.txt
77
177
  ```
178
+ $ deel scan
78
179
 
79
- | Check | Why it matters |
180
+ found 3
181
+
182
+ ◆ Ollama 127.0.0.1:11434 Ollama API 36ms
183
+ · qwen2.5-coder:7b 7B · 4.4GB
184
+ · llama3.2:1b 1B · 1.2GB
185
+ ◆ LM Studio 127.0.0.1:1234 OpenAI-compat 7ms
186
+ · devstral-small-2507
187
+ ◆ llama.cpp 127.0.0.1:8080 OpenAI-compat 7ms
188
+ · gemma-3-4b-it
189
+
190
+ Recommended Ollama · qwen2.5-coder:7b
191
+ ```
192
+
193
+ | Command | What it does |
80
194
  |---|---|
81
- | Basic chat | URL, key and model name are right |
82
- | System message | Whether rules and skills take effect |
83
- | Streaming | Whether output can arrive token by token |
84
- | **Tool calling** | **Whether files can be read and edited — the critical one** |
85
- | **Tool result round-trip** | **Whether multi-turn works — the agent loop depends on it** |
86
- | Structured output | Whether edit formats can be schema-enforced |
87
- | Reasoning effort | Whether `/think` reaches the model |
88
- | Context length | How many files fit at once |
195
+ | `deel scan` | Show what is running |
196
+ | `deel scan --pick` | Choose one from the list |
197
+ | `deel scan --save` | Register everything found |
198
+ | `deel scan --ports 9000,9100` | Extra ports to probe |
199
+ | `deel scan --host <addr>` | Defaults to `127.0.0.1` |
89
200
 
90
- It ends with a verdict: **ready / limited / blocked / unreachable**, plus a
91
- plain-text report you can hand to whoever runs the gateway.
201
+ Switch with `/model` mid-conversation **the conversation carries over.**
92
202
 
93
- Reasoning models are handled correctly: if all output lands in `thinking` and
94
- the body gets truncated, it retries with thinking disabled instead of reporting
95
- a false failure.
203
+ ---
204
+
205
+ ## Slash commands
206
+
207
+ Names follow Claude Code / Codex conventions.
208
+
209
+ | Command | What it does |
210
+ |---|---|
211
+ | `/help` | Command list |
212
+ | `/context` | What is consuming the context window |
213
+ | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
214
+ | `/compact` | Summarise and fold older turns |
215
+ | `/clear` | Clear the conversation (keeps link and rules) |
216
+ | `/model` | Switch connection / model |
217
+ | `/think <level\|profile>` | `off·low·medium·high·max` or `even·save·deep` |
218
+ | `/mode <mode>` | Approval policy — how much it asks (`auto` · `confirm` · `strict`) |
219
+ | `/work [mode]` | Work mode — what kind of work you are doing |
220
+ | `/auto` | Hand the wheel back — it picks the mode from what you type |
221
+ | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
222
+ | `/level [level]` | How much to show (`쉬움` simple · `개발자` developer) |
223
+ | `/undo [turns]` | Revert file changes |
224
+ | `/tools` | Available tools |
225
+ | `/skills [query\|all\|off]` | Browse, search, load skills |
226
+ | `/plugin [install\|remove\|pack]` | Manage plugins |
227
+ | `/cost` | Session usage |
228
+ | `/status` | Connection status |
229
+ | `/scan [save]` | Sweep this machine for local model servers (`save` registers them) |
230
+ | `/sessions` | Past conversations in this folder |
231
+ | `/init` | Create a `DEEL.md` rules file |
232
+ | `/exit` | Quit |
233
+
234
+ Discovered plugin commands are invoked as `/<plugin>:<name>`, with `$ARGUMENTS` substituted.
235
+
236
+ `/scan` and `/sessions` work without leaving the session. If you just started another local
237
+ server or loaded a different model, `/scan save` then `/model` switches over without losing
238
+ the conversation.
239
+
240
+ ### Interrupting
241
+
242
+ Press **Ctrl+C** to stop the model mid-answer when it is heading the wrong way.
243
+
244
+ ```
245
+ ❯ rewrite the whole test suite
246
+ ◧ Read test/smoke.js
247
+ ◧ Read test/loop.test.js
248
+ ^C
249
+ ⚠ Stopped (after step 2)
250
+
251
+ ❯ ▊
252
+ ```
253
+
254
+ The conversation stays valid. If the model had announced tool calls, each unanswered one is
255
+ filled with a `stopped by user` result so the call/result pairing holds — a conversation with
256
+ broken pairing is rejected with HTTP 400 on the next request, which would waste the whole
257
+ session. Tools already running finish; **tools not yet started never run.**
258
+
259
+ Pressing Ctrl+C again on an empty line quits.
96
260
 
97
261
  ---
98
262
 
99
- ## Chat
263
+ ## Work modes
264
+
265
+ What you are working on changes **which tools the model is given and how hard it thinks.**
266
+ Cycle with `Shift+Tab`, or type the name.
267
+
268
+ | Mode | For | Can edit files | Reasoning |
269
+ |---|---|---|---|
270
+ | `/auto` ◎ Auto | **Default.** Reads your message and switches for you | Yes | Normal (`save`) |
271
+ | `/code` ◆ Code | Writing and fixing | Yes | Normal (`save`) |
272
+ | `/plan` ☰ Plan | Planning first | **No** | Deep (`deep`·high) |
273
+ | `/architect` ◈ Architect | Shaping structure | **No** | Deep (`deep`·high) |
274
+ | `/debug` ◉ Debug | Finding causes | Yes | Deep, more steps (32) |
275
+ | `/ask` ◇ Ask | Explaining only | **No** | Shallow (`low`) |
276
+ | `/orchestrator` ❋ Orchestrator | Breaking up large work | Yes | Many steps (40) |
277
+
278
+ In read-only modes, `Write`, `Edit` and `Bash` are **never sent to the model at all.**
279
+ It is not asked politely not to edit — models forget requests. A tool that isn't there can't be used.
280
+
281
+ Don't confuse this with `/mode`. They are separate axes:
282
+
283
+ - `/mode` — **how much it asks you** (auto · confirm · strict)
284
+ - `/work` — **what kind of work you are doing** (the seven above)
285
+
286
+ If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
287
+ overrides something a person chose.
288
+
289
+ ### Switching by itself (Auto mode)
100
290
 
101
- Run it inside the folder you want to work in. That folder becomes the **scope** —
102
- nothing outside it can be read or written, even if the model asks.
291
+ You start in **Auto**. Nothing has been decided about what kind of work is coming.
292
+ Every turn, deel reads what you typed, picks the mode that fits, and works in it.
103
293
 
104
294
  ```
105
- deel sec-llm-01 · /home/you/project
106
- skills: 337 · commands: 127 (42 plugins)
295
+ ❯ why won't the login go through?
107
296
 
108
- unify the log format
297
+ Debug because your message contained "why won't", "won't"
298
+ Not what you wanted? Type /code to pin a mode yourself.
299
+ ```
109
300
 
110
- Grep(console.log)
111
- 1 file · 1 match
301
+ Switching brings **the whole mode** with it — its working protocol, its tool set, its
302
+ reasoning settings. It isn't a label saying "debug mode": the model is actually walked
303
+ through symptom → reproduce → hypothesis → evidence, and in Plan mode `Write` and `Edit`
304
+ are not handed over at all.
112
305
 
113
- Read(src/runner.js)
114
- └ 5 lines
306
+ | When you say | It goes to |
307
+ |---|---|
308
+ | why won't · error · fails · crashes · what's causing | ◉ Debug |
309
+ | plan · roadmap · what order · let's map it out first | ☰ Plan |
310
+ | design · architecture · how should this be structured · how to split | ◈ Architect |
311
+ | what is · explain · how does it work · difference between | ◇ Ask |
312
+ | all of · everything · one by one · to the end · unify | ❋ Orchestrator |
313
+ | fix · add · implement · rename · delete | ◆ Code |
115
314
 
116
- Edit(src/runner.js)
117
- 1 occurrence
315
+ **When it's close, it doesn't switch.** "ok", "go on", "that thing from earlier" leave you
316
+ in Auto. So does a near-tie between first and second place — a wrong switch into a read-only
317
+ mode leaves you blocked without knowing *why*. Read-only modes (Plan, Architect, Ask)
318
+ therefore carry a higher bar: "explain this and fix it" routes to Code, not Ask.
118
319
 
119
- Unified the log calls to the logger format. One change in runner.js.
320
+ A switch lasts **one turn only.** The next message is judged fresh.
321
+ A `~` in the status line means it switched by itself; no `~` means you chose it.
120
322
 
121
- ─ 4.2s · 3 tools · 180 tokens
122
323
  ```
324
+ ◎ Auto ← waiting
325
+ ~◉ Debug ← this turn only, chosen for you
326
+ ◉ Debug ← you typed /debug. It stays.
327
+ ```
328
+
329
+ Choosing a mode yourself **pins** it. `/auto` (or `/work auto`) hands the wheel back.
330
+
331
+ ---
332
+
333
+ ## Simple vs developer
334
+
335
+ Twenty commands on first launch means nothing gets chosen. Locking features away means
336
+ hitting a wall later. So only **what is shown** differs.
337
+
338
+ | | Simple (`쉬움`, default) | Developer (`개발자`) |
339
+ |---|---|---|
340
+ | `/help` listing | Common commands only | Everything |
341
+ | Error messages | What to do about it | The original text |
342
+ | Safety | **Identical** | **Identical** |
343
+
344
+ `/level 개발자` is saved to config and persists across sessions.
345
+
346
+ Two things matter here:
347
+
348
+ - **Hidden commands still work.** `/think high` works in simple mode. It just isn't listed.
349
+ - **Beginners do not get fewer safeguards.** Undo, workspace scope and dangerous-command
350
+ blocking are identical. A beginner needs the undo more, not less.
123
351
 
124
- ### Slash commands
352
+ ---
353
+
354
+ ## Tools
125
355
 
126
- Names follow the Claude Code / Codex convention.
356
+ Names and arguments match Claude Code, so skills written for that convention work unchanged.
127
357
 
128
- | Command | |
358
+ | Tool | What it does |
129
359
  |---|---|
130
- | `/help` | list commands |
131
- | `/context` | context usage what is taking up room |
132
- | `/compact` · `/clear` | shrink · wipe the conversation |
133
- | `/model` | switch connection or model (conversation continues) |
134
- | `/think off\|low\|medium\|high\|max` | reasoning effort |
135
- | `/mode auto\|confirm\|strict` | execution mode |
136
- | `/undo [n]` | roll back the last n turns |
137
- | `/tools` · `/skills` | what is available |
138
- | `/cost` · `/status` | usage · connection |
139
- | `/init` | create a `DEEL.md` rules file |
360
+ | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV**) |
361
+ | `Write` | Write / overwrite a file |
362
+ | `Edit` | Replace an exact string (`replace_all` supported) |
363
+ | `Glob` | Find files by name pattern |
364
+ | `Grep` | Regex search file contents |
365
+ | `Bash` | Run a command |
366
+ | `Skill` | Expand a skill body (shown to the model only when skills exist) |
367
+ | `WebFetch` | Read a web page (read-only; hidden under `--offline`) |
368
+ | `TodoWrite` | Checklist breaks long work into steps and shows progress |
369
+
370
+ ### Checklists
371
+
372
+ Keeps the model from losing its place on multi-step work. The list is redrawn whenever the
373
+ model updates it.
374
+
375
+ ```
376
+ ☰ Todo 1/3 done ← just finished 1
377
+
378
+ ✓ unify log format
379
+ ▶ fix the tests
380
+ ☐ update the docs
381
+ ```
382
+
383
+ Only one item may be **in progress** at a time; setting two is refused. Holding several at
384
+ once is how nothing gets finished.
385
+
386
+ ### Read-only tools run together
387
+
388
+ When the model asks for three `Read` calls at once, all three run **concurrently** — sweeping
389
+ five files costs about what reading one costs.
390
+
391
+ ```
392
+ ◧ Read src/a.js ◧ Read src/b.js ◧ Read src/c.js together
393
+ ```
140
394
 
141
- ### Tools
395
+ Only `Read`, `Glob`, `Grep`, `Skill` and `WebFetch` are eligible. `Write`, `Edit` and `Bash`
396
+ always run one at a time — two concurrent writes to one file scramble the undo snapshot
397
+ order, and `Bash` can do anything. Results come back **in the order the model asked for
398
+ them**, even when they finish out of order; shuffled results confuse the model about which
399
+ result belongs to which call.
142
400
 
143
- Names and arguments match Claude Code, so skills and commands written for that
144
- convention work unchanged.
401
+ ### Edits survive small mistakes
145
402
 
146
- `Read` · `Write` · `Edit` · `Glob` · `Grep` · `Bash` · `Skill`
403
+ Models routinely get whitespace, indentation and line endings wrong. deel relaxes matching in
404
+ stages but **refuses outright when the match is ambiguous** — silently editing the wrong place is
405
+ far worse than not finding it.
406
+
407
+ ```
408
+ exact → ignore trailing space / CRLF → ignore indentation → ignore all whitespace
409
+ ```
410
+
411
+ Measured with `npm run bench`:
412
+
413
+ | | Success | Wrong place edited |
414
+ |---|---|---|
415
+ | Exact match only | 20% | 0 |
416
+ | Staged relaxation | **100%** | **0** |
417
+
418
+ On failure it points at the closest line in the file.
147
419
 
148
420
  ---
149
421
 
150
- ## Edits survive sloppy models
422
+ ## Korean text and Excel
151
423
 
152
- Models routinely get whitespace, indentation and line endings slightly wrong.
153
- `Edit` relaxes matching in stages — but **refuses outright when ambiguous**,
154
- because silently editing the wrong place is far worse than not finding it.
424
+ ### Encoding written back the way it was read
425
+
426
+ Corporate documents are often not UTF-8. Files saved by old Windows Notepad in a legacy
427
+ codepage (CP949 in Korea, CP932 in Japan, GBK in China) are still around. Reading one as
428
+ UTF-8 garbles it completely: `한글` becomes `�ѱ�`.
429
+
430
+ Writing is the dangerous part. Read it garbled, save it as UTF-8, and the original is gone.
431
+ So there is one rule: **write it back in the encoding it was read in.**
432
+
433
+ Which encoding that is comes from **the file's contents, not the machine's settings.**
434
+ Each candidate is decoded strictly, then scored on whether the result looks like real text
435
+ written in that encoding. So the same CP949 document reads identically on Ubuntu, on a US
436
+ Windows machine, and on a Korean one.
155
437
 
156
438
  ```
157
- exact → trailing space & CRLF → indentation → all whitespace
439
+ Read report.txt
440
+ └ 4 lines · CP949
158
441
  ```
159
442
 
160
- Measured by `npm run bench`:
443
+ If you try to insert a character that encoding **cannot hold**, it refuses instead of saving.
161
444
 
162
445
  ```
163
- should-fix cases exact-only 2/10 (20%) staged 10/10 (100%)
164
- should-refuse 5/5 (100%) · wrong-place edits: 0
446
+ Edit report.txt notenote 🚀
447
+ This file is CP949, and you are inserting a character that encoding does not have: 🚀
165
448
  ```
166
449
 
167
- When it fails, it points at the closest real line so the model can correct itself:
450
+ Silently substituting question marks would be worse than not writing at all.
451
+ Newly created files are UTF-8.
452
+
453
+ Command output is handled the same way. A Windows console is not UTF-8, so taking `Bash`
454
+ output as utf8 garbles non-ASCII text. It is collected as bytes and decoded afterwards.
455
+
456
+ ### Excel — read as CSV
457
+
458
+ An Excel file is a compressed archive, not text, so normally you get "this is a binary file"
459
+ and somebody has to export a CSV by hand. `Read` just does it.
168
460
 
169
461
  ```
170
- Not found.
171
- Line 2 of the file is closest:
172
- console.log("start: " + id);
173
- Copy that line verbatim and try again.
462
+ Read report.xlsx
463
+ 3 sheets · 128 rows · unpacked directly
174
464
  ```
175
465
 
466
+ - **Still zero dependencies.** An xlsx is a zip full of XML, so Node's built-in `zlib` is enough.
467
+ - Every sheet is returned. Hidden sheets too, marked as hidden.
468
+ - Dates come back as dates, not serial numbers — the cell format is read to decide.
469
+ - Formulas come back as **computed values**, and error values like `#REF!` are not dropped.
470
+
471
+ **Password-protected files and legacy `.xls`** are handed to Excel itself; those cannot be
472
+ unpacked directly. You are asked for the password at that point.
473
+
474
+ The password is **not stored anywhere**:
475
+
476
+ - not in the config file
477
+ - not in the session log
478
+ - not in the audit log
479
+ - not as a command-line argument (other people can see your command lines)
480
+
481
+ The only path out is the child process's stdin, and a test asserts that this stays true.
482
+ Extracted intermediate files are deleted after use.
483
+
484
+ > **Excel files are read-only here.** `Edit` and `Write` refuse them, and say why and what
485
+ > to do instead. Round-tripping a file with formatting, formulas and charts through CSV
486
+ > always loses something. Better not to write than to write knowing you'll lose data.
487
+
176
488
  ---
177
489
 
178
- ## Skills and commands come from the host machine
490
+ ## Skills and plugins
179
491
 
180
- Nothing is bundled. On startup it scans:
492
+ **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
493
+ whatever is there. On a clean PC: zero. On a PC with skills installed: those skills.
181
494
 
182
495
  ```
183
496
  project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
184
497
  user ~/.deel/skills ~/.claude/skills ~/.claude/commands
185
- plugins ~/.claude/plugins/** (any folder with .claude-plugin/plugin.json)
498
+ plugins ~/.claude/plugins/** ~/.deel/plugins/**
186
499
  ```
187
500
 
188
- It reads the Claude Code format: `SKILL.md` with YAML frontmatter,
189
- `commands/*.md` with `$ARGUMENTS` substitution.
501
+ Reads the Claude Code format: `SKILL.md` with YAML front matter, `commands/*.md`, `$ARGUMENTS`.
190
502
 
191
503
  ### Loaded in three stages
192
504
 
193
- Listing everything would blow the context window. So:
505
+ Loading everything would blow the context window.
194
506
 
195
507
  | Stage | What | Cost |
196
508
  |---|---|---|
197
- | 1 | name + one-line description in the system prompt | ~1,800 tokens for 40 |
198
- | 2 | the model calls `Skill` for the one body it wants | one at a time |
199
- | 3 | files that body references, via `Read` | only if needed |
509
+ | 1 | Name + one-line description in the prompt | ~1,800 tokens for 40 skills |
510
+ | 2 | Body of the one the model picks via `Skill` | one at a time |
511
+ | 3 | Files that body references, via `Read` | on demand |
512
+
513
+ ### Fetching plugins
200
514
 
201
- On a machine with 337 skills, listing 40 costs about 1,800 tokens instead of
202
- well over 100,000.
515
+ ```bash
516
+ # on a connected machine
517
+ /plugin install affaan-m/ECC # git clone, or tarball when git is absent
518
+ /plugin pack import.zip # bundle, excluding executable scripts
203
519
 
204
- Not supported on purpose: **hooks** (executable scripts a review problem and an
205
- extra failure path for autonomous execution), **subagents** (doubles model calls
206
- against a gateway quota), and **MCP** (a separate protocol, a project of its own).
520
+ # on the air-gapped machinejust unzip
521
+ unzip import.zip -d ~/.deel/plugins/
522
+ ```
523
+
524
+ `/plugin pack` omits `.js` `.sh` `.ps1` `.py` and friends, and includes a plain-text manifest
525
+ with a licence table — ready to hand to a security reviewer.
526
+
527
+ ### Deliberately not included
528
+
529
+ | | Why |
530
+ |---|---|
531
+ | hooks | Executable scripts — fails import review, widens the blast radius of autonomy |
532
+ | sub-agents | Doubles model calls against a gateway quota |
533
+ | MCP | A separate protocol; a project of its own |
207
534
 
208
535
  ---
209
536
 
210
- ## Safety without approval prompts
537
+ ## Reasoning effort
538
+
539
+ One answer means several model calls, and **each needs a different amount of thinking.**
540
+ All-high is slow; all-low wanders off.
541
+
542
+ ```
543
+ $ /think
211
544
 
212
- The default mode is `auto`: it edits files and runs commands without asking.
213
- The safety net is that everything is **reversible**, not that everything is gated.
545
+ Base medium Profile save Hard on the first decision, light while continuing
214
546
 
215
- | | |
547
+ Stage Effort Cap When
548
+ first call · medium 4,096 deciding what to do
549
+ continuing ↓ low 2,048 reading a tool result, picking the next step
550
+ stuck ↑ high 4,096 the previous tool errored
551
+ ```
552
+
553
+ | Profile | Character |
554
+ |---|---|
555
+ | `even` | Same effort everywhere — predictable, slower |
556
+ | `save` (default) | Hard on the first decision only |
557
+ | `deep` | Everything one notch up — for hard work |
558
+
559
+ ### Context length is read off the model
560
+
561
+ This one number sizes the whole program: how many files fit in one read, when the
562
+ conversation gets folded, how long a single reply may be — **all of it comes from here.**
563
+
564
+ So deel asks the server on every launch rather than trusting the saved value. The same
565
+ model name can be loaded at a different length each time, and if that difference never
566
+ reaches the screen there is no way to notice. **It just quietly gets smaller.**
567
+
568
+ ```
569
+ │ Model qwen3-coder (640k tokens) │
570
+ ╰─────────────────────────────────────────────────────────╯
571
+ ✓ Context adjusted 32,768 → 655,360 (read from LM Studio)
572
+ ```
573
+
574
+ Every server puts this number under a different name in a different place, so deel checks
575
+ all of them.
576
+
577
+ | Server | Where it reads |
216
578
  |---|---|
217
- | **Undo** | every file is snapshotted before it changes; `/undo` restores by turn |
218
- | **Scope** | the starting folder is a hard boundary |
219
- | **Blocked commands** | only irreversible ones disk format, recursive delete, force-push, piping downloads into a shell. Ordinary commands pass |
220
- | **No re-run** | a mutating command is never retried after failure running it twice is the accident |
221
- | **Audit log** | everything lands in `.deel/audit.jsonl` |
579
+ | LM Studio | `/api/v0/models` `max_context_length`, `loaded_context_length` |
580
+ | llama.cpp | `/props` `n_ctx` |
581
+ | vLLM | `/v1/models``max_model_len` |
582
+ | Ollama | `/api/show``<model>.context_length` |
583
+ | Other OpenAI-compatible | `/v1/models/<model>` `context_window`, `context_length`, `max_input_tokens`, `max_position_embeddings` (found even when nested) |
584
+
585
+ **Model maximum and loaded length are not the same thing.** LM Studio will happily load a
586
+ 655,360-capable model at 8,192. Trusting the maximum there gets your requests rejected. So
587
+ the **loaded length is what deel uses**, and the maximum is reported separately.
588
+
589
+ ```
590
+ ⚠ This model goes up to 655,360 — raise it on the server, then /ctx auto
591
+ ```
592
+
593
+ | Command | What it does |
594
+ |---|---|
595
+ | `/ctx` | Current length and remaining room |
596
+ | `/ctx auto` | Ask the server again and match the model |
597
+ | `/ctx 655360` | Set it yourself (`640k`, `128k`, `1m` also work) |
598
+ | `/ctx out 32k` | Cap on a **single reply** — a different axis from context |
599
+ | `deel --ctx 655360` | Start at this value (skips the lookup) |
600
+
601
+ **`k` means 1024 here.** Context lengths are all powers of two, so that is the only base
602
+ that lines up: 655,360 is `640k`, not `655k`; 131,072 is `128k`, not `131k`. The display and
603
+ `/ctx` use the same unit, so typing back what you see gives you the same number.
604
+
605
+ **Caps are not fixed numbers.** They are computed from the model's context window and how
606
+ much of it is currently used — the profile decides what share of the remaining room a stage gets.
607
+
608
+ | Model | First call | Continuing | Stuck |
609
+ |---|---|---|---|
610
+ | 2k local | 554 | 512 | 554 |
611
+ | 8k local | 2,007 | 1,003 | 2,007 |
612
+ | 40k (qwen3) | 11,688 | 5,844 | 11,688 |
613
+ | 128k gateway | 16,384 | 16,384 | 16,384 |
614
+ | 128k, 80% full | 7,680 | 3,840 | 7,680 |
615
+
616
+ Caps shrink as the context fills. Handing a 4k model a 4,096-token cap would leave no room for input.
617
+ Raise the ceiling with `maxTokens` in the profile if you need more.
618
+
619
+ If a saved cap truncates a reply, **that step alone is retried with the cap lifted.**
620
+ A truncated reply means a half-written tool call, which fails silently.
621
+
622
+ ---
623
+
624
+ ## Auto-compaction
625
+
626
+ At 80% context, older turns are **summarised and folded** so work continues.
627
+ Plain truncation makes the model forget: it re-reads files and re-fixes what it already fixed.
628
+
629
+ ```
630
+ ◱ Folded 44 turns into a summary — 10,399 → 3,170 tokens (70% smaller)
631
+ ```
632
+
633
+ The summary keeps goal / done / learned / decided / remaining. The cut point is chosen so a
634
+ **tool call is never separated from its result** — splitting them makes the server return 400.
635
+ If the summary request fails, it falls back to plain trimming rather than stopping.
636
+
637
+ `/compact` folds on demand.
638
+
639
+ ---
640
+
641
+ ## Resuming a conversation
642
+
643
+ Close the terminal by accident, or reboot, and the conversation is still there.
644
+ Messages are written to `.deel/sessions/` **as each one completes**, so a crash
645
+ loses at most the message in flight.
646
+
647
+ ```
648
+ $ deel sessions
649
+
650
+ ── conversations in this folder ────────────────────────────────
651
+ ● 20260824-090200 just now 1 turn devstral-small-2507
652
+ fix the failing test
653
+ · 20260824-084500 2h ago 2 turns qwen2.5-coder:7b
654
+ switch src/a.js logging to the logger
655
+ ```
656
+
657
+ | Command | What it does |
658
+ |---|---|
659
+ | `deel --continue` | Resume the most recent conversation in this folder |
660
+ | `deel --resume <id>` | Resume a specific one |
661
+ | `deel sessions` | List what is stored |
662
+ | `deel sessions --rm <id>` | Delete one |
663
+
664
+ The format is `jsonl` — one message per line — so a power cut costs only the last line.
665
+ Resumed history keeps tool calls paired with their results, so work continues immediately.
666
+ Conversations older than 30 days and outside the most recent 30 are pruned automatically.
222
667
 
223
- `/mode confirm` asks before irreversible commands; `/mode strict` asks before
224
- every file change and command.
668
+ Everything lives in `.deel/sessions/` inside the working folder, and `.gitignore`
669
+ covers `.deel/` so it never reaches a repository.
670
+
671
+ ---
672
+
673
+ ## Safety
674
+
675
+ Instead of approval prompts, the design makes things **reversible**. The default `auto` mode
676
+ does not ask.
677
+
678
+ | Mechanism | Detail |
679
+ |---|---|
680
+ | **Undo** | Snapshot before every write. `/undo` restores per turn |
681
+ | **Scope** | Outside the starting folder is refused, even if the model insists |
682
+ | **Blocked commands** | Only irreversible ones (disk format, recursive delete, `--force` push) |
683
+ | **No re-run** | A mutating command is never retried after failure |
684
+ | **Interrupt** | Ctrl+C stops mid-answer and leaves the conversation valid |
685
+ | **Audit log** | Everything recorded in `.deel/audit.jsonl` |
686
+
687
+ | Mode | Asks when |
688
+ |---|---|
689
+ | `auto` (default) | Never — undo is the safety net |
690
+ | `confirm` | Irreversible commands only |
691
+ | `strict` | All file changes and commands |
692
+
693
+ Undo history stores whole file contents, so repeated edits to large files add up. Past 32MB
694
+ it keeps the **most recent 50 turns** and drops the rest. What you just did is always
695
+ undoable; `/status` shows how large the history currently is.
696
+
697
+ ---
698
+
699
+ ## Corporate review package
700
+
701
+ ```bash
702
+ deel pack --out deel-import.zip
703
+ ```
704
+
705
+ ```
706
+ ✓ deel-import.zip
707
+ 39 files · 100.2KB
708
+
709
+ Dependencies 0
710
+ Install scripts none
711
+ External imports 0
712
+ Network calls 3 sites (configured address only)
713
+ Ports opened none
714
+ ```
715
+
716
+ The bundled review sheet contains:
717
+
718
+ - Dependency list and every external `import` in the source
719
+ - Presence of `preinstall` / `install` / `postinstall` / `prepare`
720
+ - **Every network and process-spawn call site found by scanning the source** (file:line)
721
+ - The three outbound lanes, explained
722
+ - SHA-256 per file (verify with `certutil -hashfile`)
723
+
724
+ It is generated by scanning the source, not written by hand — hand-written sheets drift from reality.
725
+ Use `deel audit` to read it without building a zip.
726
+
727
+ ### Diagnosing a corporate gateway
728
+
729
+ ```bash
730
+ node bin/deel.js diagnose --url <gateway> --key <key> --model <model> --out report.txt
731
+ ```
732
+
733
+ Hand over `report.txt` alone — plain text, no colour codes.
734
+
735
+ | Check | Why it matters |
736
+ |---|---|
737
+ | Basic chat | Address, key and model name are right |
738
+ | System message | Rules (`DEEL.md`) and skills take effect |
739
+ | Streaming | Output can flow token by token |
740
+ | **Tool calls** | **Whether it can read and edit files — the critical one** |
741
+ | **Tool results** | **Whether multi-turn works — the premise of the agent loop** |
742
+ | Structured output | Edit format can be enforced by schema |
743
+ | Reasoning control | Whether `/think` works at the model layer |
744
+ | Context length | How many files can be read at once |
745
+
746
+ Verdict is one of **ready · limited · blocked · unreachable**.
747
+
748
+ ---
749
+
750
+ ## Configuration
751
+
752
+ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder takes precedence.
753
+
754
+ ### Supported servers
755
+
756
+ | | Example address |
757
+ |---|---|
758
+ | Corporate AI gateway (OpenAI-compatible) | `https://ai-gw.example.corp/v1` |
759
+ | Ollama | `http://localhost:11434` |
760
+ | LM Studio | `http://localhost:1234/v1` |
761
+ | llama.cpp · vLLM · LiteLLM | `http://host:port/v1` |
762
+
763
+ Auth style is detected automatically: `Authorization: Bearer` → `x-api-key` → `api-key` (Azure) → none.
764
+
765
+ ### Environment variables
766
+
767
+ | Variable | Use |
768
+ |---|---|
769
+ | `DEEL_API_KEY` | Keep the key out of the config file (takes precedence) |
770
+ | `DEEL_KEY_<PROFILE_ID>` | Per-profile key |
771
+ | `NODE_EXTRA_CA_CERTS` | Corporate TLS certificate |
772
+ | `HTTPS_PROXY` | Behind a proxy |
773
+ | `DEEL_DEBUG=1` | Verbose errors |
774
+ | `NO_COLOR` | Disable colour |
775
+
776
+ ### Flags
777
+
778
+ ```bash
779
+ deel --root <folder> Working scope. Defaults to the current folder
780
+ deel --mode <mode> auto (default) / confirm / strict
781
+ deel --work <mode> auto (default) / code / plan / architect / debug / ask / orchestrator
782
+ deel --level <level> 쉬움 (simple) / 개발자 (developer)
783
+ deel --think <level> off / low / medium (default) / high / max
784
+ deel --effort <profile> even / save (default) / deep
785
+ deel --offline Nothing leaves this machine
786
+ deel --continue Resume the most recent conversation
787
+ deel --resume <id> Resume a specific one
788
+ ```
789
+
790
+ ### Project rules
791
+
792
+ If the working folder has `DEEL.md`, `CLAUDE.md` or `AGENTS.md`, it is loaded as project rules.
793
+ `/init` scaffolds one.
794
+
795
+ ---
796
+
797
+ ## Troubleshooting
798
+
799
+ | Symptom | Check |
800
+ |---|---|
801
+ | `address not found` | Typo, DNS, VPN / intranet connectivity |
802
+ | `connection refused` | Server is down or the port differs |
803
+ | certificate error | `set NODE_EXTRA_CA_CERTS=C:\path\corp-ca.pem` |
804
+ | behind a proxy | `set HTTPS_PROXY=http://proxy:port` |
805
+ | 401 / 403 | Wrong key or auth header style (four are tried automatically) |
806
+ | `address not permitted` | The lock did its job — pick a connection with `/model` |
807
+ | Tool calls don't work | Run `deel diagnose`. Small models (1B–3B) often can't |
808
+ | Empty replies | A heavy-reasoning model — try `/think low` |
809
+ | `deel scan` finds nothing | Server is off or on another port — use `--ports` |
225
810
 
226
811
  ---
227
812
 
228
813
  ## Development
229
814
 
230
815
  ```bash
231
- npm run check # syntax across every module
232
- npm test # 20 tool checks + 16 engine checks + the edit benchmark
233
- npm run demo # see the chat screen, driven by a fake gateway
234
- npm run bench # edit reliability numbers
816
+ npm test Full suite (254 checks)
817
+ npm run verify Import + network checks only
818
+ npm run bench Edit success rate
819
+ npm run demo See what the UI actually looks like
820
+ npm run check Syntax check every file
235
821
  ```
236
822
 
237
- The engine tests spin up a **fake OpenAI-compatible gateway** over HTTP. No real
238
- model is involved, so the loop, streaming parser, tool execution and undo are all
239
- verified deterministically including the case where a gateway splits tool-call
240
- arguments across streaming chunks.
823
+ Tests run against a **fake gateway**, so the loop, streaming, tool execution, undo and compaction
824
+ are verified deterministically without any model. ZIP output is cross-checked with the real
825
+ `unzip`; the TAR reader is fed archives produced by the real `tar`.
826
+
827
+ `npm test` runs each file separately and reports **per-file exit codes**, because the exit code
828
+ — not the pass marks on screen — is what CI reads, and the two can disagree: a file can pass
829
+ every check and still die on the way out, leaving the screen green and the exit code 1. That
830
+ happened once on Windows and cost a lot of time. The runner does not stop at the first failure,
831
+ so one run tells you everything.
832
+
833
+ | Suite | Checks | Covers |
834
+ |---|---|---|
835
+ | `smoke` | 20 | Tools, scope, undo, audit log |
836
+ | `loop` | 16 | Agent loop, streaming, tool calls |
837
+ | `network` | 30 | Nothing escapes the configured address |
838
+ | `web` | 25 | Web reads stay read-only |
839
+ | `abort` | 16 | Ctrl+C leaves the conversation valid |
840
+ | `parallel` | 23 | Read-only tools run together; checklists |
841
+ | `compact` | 21 | Summary folding, pairing intact, graceful fallback |
842
+ | `store` | 34 | Session persistence, resume, crash recovery |
843
+ | `scan` | 19 | Distinguishing multiple runtimes |
844
+ | `plugins` | 38 | Plugin fetch/pack, ZIP/TAR |
845
+ | `no-bundle` | 12 | Nothing foreign in the published package; test-file hygiene |
846
+ | `edit-bench` | 20 cases | Edit success rate |
241
847
 
242
848
  ---
243
849
 
244
- ## License
850
+ ## Licence
245
851
 
246
- MIT
852
+ [MIT](LICENSE)