deel-local-cli 0.8.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
@@ -47,7 +47,10 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
47
47
  - [Where your data can go](#where-your-data-can-go)
48
48
  - [Multiple local runtimes](#multiple-local-runtimes)
49
49
  - [Slash commands](#slash-commands)
50
+ - [Work modes](#work-modes)
51
+ - [Simple vs developer](#simple-vs-developer)
50
52
  - [Tools](#tools)
53
+ - [Korean text and Excel](#korean-text-and-excel)
51
54
  - [Skills and plugins](#skills-and-plugins)
52
55
  - [Reasoning effort](#reasoning-effort)
53
56
  - [Auto-compaction](#auto-compaction)
@@ -207,11 +210,16 @@ Names follow Claude Code / Codex conventions.
207
210
  |---|---|
208
211
  | `/help` | Command list |
209
212
  | `/context` | What is consuming the context window |
213
+ | `/ctx [auto\|number]` | Context **length** — re-read it off the model, or set it yourself |
210
214
  | `/compact` | Summarise and fold older turns |
211
215
  | `/clear` | Clear the conversation (keeps link and rules) |
212
216
  | `/model` | Switch connection / model |
213
217
  | `/think <level\|profile>` | `off·low·medium·high·max` or `even·save·deep` |
214
- | `/mode <mode>` | `auto` · `confirm` · `strict` |
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) |
215
223
  | `/undo [turns]` | Revert file changes |
216
224
  | `/tools` | Available tools |
217
225
  | `/skills [query\|all\|off]` | Browse, search, load skills |
@@ -252,13 +260,104 @@ Pressing Ctrl+C again on an empty line quits.
252
260
 
253
261
  ---
254
262
 
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)
290
+
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.
293
+
294
+ ```
295
+ ❯ why won't the login go through?
296
+
297
+ ◉ Debug because your message contained "why won't", "won't"
298
+ Not what you wanted? Type /code to pin a mode yourself.
299
+ ```
300
+
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.
305
+
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 |
314
+
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.
319
+
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.
322
+
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.
351
+
352
+ ---
353
+
255
354
  ## Tools
256
355
 
257
356
  Names and arguments match Claude Code, so skills written for that convention work unchanged.
258
357
 
259
358
  | Tool | What it does |
260
359
  |---|---|
261
- | `Read` | Read a file (line numbers, `offset`/`limit`) |
360
+ | `Read` | Read a file (line numbers, `offset`/`limit`, **Excel as CSV**) |
262
361
  | `Write` | Write / overwrite a file |
263
362
  | `Edit` | Replace an exact string (`replace_all` supported) |
264
363
  | `Glob` | Find files by name pattern |
@@ -320,6 +419,74 @@ On failure it points at the closest line in the file.
320
419
 
321
420
  ---
322
421
 
422
+ ## Korean text and Excel
423
+
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.
437
+
438
+ ```
439
+ › Read report.txt
440
+ └ 4 lines · CP949
441
+ ```
442
+
443
+ If you try to insert a character that encoding **cannot hold**, it refuses instead of saving.
444
+
445
+ ```
446
+ › Edit report.txt note → note 🚀
447
+ └ This file is CP949, and you are inserting a character that encoding does not have: 🚀
448
+ ```
449
+
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.
460
+
461
+ ```
462
+ › Read report.xlsx
463
+ └ 3 sheets · 128 rows · unpacked directly
464
+ ```
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
+
488
+ ---
489
+
323
490
  ## Skills and plugins
324
491
 
325
492
  **deel does not carry skills with it.** On startup it scans the machine it is running on and uses
@@ -389,6 +556,52 @@ $ /think
389
556
  | `save` (default) | Hard on the first decision only |
390
557
  | `deep` | Everything one notch up — for hard work |
391
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 |
578
+ |---|---|
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
+
392
605
  **Caps are not fixed numbers.** They are computed from the model's context window and how
393
606
  much of it is currently used — the profile decides what share of the remaining room a stage gets.
394
607
 
@@ -565,6 +778,8 @@ Auth style is detected automatically: `Authorization: Bearer` → `x-api-key`
565
778
  ```bash
566
779
  deel --root <folder> Working scope. Defaults to the current folder
567
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)
568
783
  deel --think <level> off / low / medium (default) / high / max
569
784
  deel --effort <profile> even / save (default) / deep
570
785
  deel --offline Nothing leaves this machine
package/README.md CHANGED
@@ -219,13 +219,15 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
219
219
  |---|---|
220
220
  | `/help` | 명령 목록 |
221
221
  | `/context` | 무엇이 컨텍스트를 먹고 있는지 |
222
+ | `/ctx [auto\|숫자]` | 컨텍스트 **길이** — 모델에 맞춰 다시 재거나 직접 지정 |
222
223
  | `/compact` | 앞선 대화를 요약해서 접기 |
223
224
  | `/clear` | 대화 비우기 (연결·규칙은 유지) |
224
225
  | `/model` | 연결·모델 바꾸기 |
225
226
  | `/think <강도\|배분>` | 추론 강도 (`off·low·medium·high·max`) 또는 배분 (`even·save·deep`) |
226
227
  | `/mode <모드>` | 승인 정책 — 얼마나 물어보나 (`auto` · `confirm` · `strict`) |
227
228
  | `/work [모드]` | 작업 모드 — 무슨 일을 하는 중인가 |
228
- | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | 작업 모드 바로 바꾸기 |
229
+ | `/auto` | 다시 맡기기 말을 보고 알맞은 모드로 저절로 옮겨 갑니다 |
230
+ | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | 작업 모드 바로 바꾸기 (그때부터 고정) |
229
231
  | `/level [수준]` | 화면에 무엇을 내놓을지 (`쉬움` · `개발자`) |
230
232
  | `/undo [턴수]` | 파일 변경 되돌리기 |
231
233
  | `/tools` | 쓸 수 있는 도구 |
@@ -273,6 +275,7 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
273
275
 
274
276
  | 모드 | 하는 일 | 파일을 고치나 | 추론 |
275
277
  |---|---|---|---|
278
+ | `/auto` ◎ 종합 | **처음 값.** 말을 보고 알맞은 모드로 옮겨 간다 | 예 | 보통 (`save`) |
276
279
  | `/code` ◆ 코드 | 고치고 만든다 | 예 | 보통 (`save`) |
277
280
  | `/plan` ☰ 계획 | 먼저 계획만 세운다 | **아니오** | 깊게 (`deep`·high) |
278
281
  | `/architect` ◈ 설계 | 구조를 짠다 | **아니오** | 깊게 (`deep`·high) |
@@ -286,11 +289,53 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
286
289
  `/mode` 와 헷갈리지 마세요. 둘은 다른 축입니다.
287
290
 
288
291
  - `/mode` — **얼마나 물어보나** (auto · confirm · strict)
289
- - `/work` — **무슨 일을 하는 중인가** (위 여섯 가지)
292
+ - `/work` — **무슨 일을 하는 중인가** (위 일곱 가지)
290
293
 
291
294
  `/think` 나 `/mode` 를 직접 고른 적이 있으면 그 선택이 우선합니다.
292
295
  모드가 사람이 고른 값을 덮어쓰지 않습니다.
293
296
 
297
+ ### 저절로 옮겨 가기 (종합 모드)
298
+
299
+ 처음에는 **종합** 으로 시작합니다. 무슨 일이 올지 모르는 상태입니다.
300
+ 한마디를 받을 때마다 그 말을 보고 알맞은 모드로 옮겨 간 다음 일합니다.
301
+
302
+ ```
303
+ ❯ 로그인이 왜 안 되지?
304
+
305
+ ◉ 디버그 (debug) 말 속에 '왜 안 되', '왜 안' 가 있어서
306
+ 다르면 /code 처럼 직접 고르세요. 그때부터는 안 바뀝니다.
307
+ ```
308
+
309
+ 옮겨 가면 그 모드의 **절차·도구·추론 설정이 전부** 따라옵니다.
310
+ "디버그 모드입니다" 라고 이름만 붙는 게 아니라, 실제로 증상→재현→가설→증거 순서를
311
+ 밟게 하고, 계획 모드에서는 `Write`·`Edit` 를 아예 안 줍니다.
312
+
313
+ | 이런 말이면 | 이 모드로 |
314
+ |---|---|
315
+ | 왜 안 돼 · 에러 · 실패 · 죽어요 · 원인 | ◉ 디버그 |
316
+ | 계획 · 순서 · 로드맵 · 먼저 잡자 | ☰ 계획 |
317
+ | 설계 · 구조를 어떻게 · 아키텍처 · 어떻게 나눌까 | ◈ 설계 |
318
+ | 뭐야? · 설명해줘 · 어떻게 동작해 · 차이가 뭐야 | ◇ 묻기 |
319
+ | 전체 · 전부 · 하나씩 · 끝까지 · 통일 | ❋ 총괄 |
320
+ | 고쳐줘 · 만들어줘 · 구현해줘 · 지워줘 | ◆ 코드 |
321
+
322
+ **애매하면 안 옮깁니다.** "음", "ㅇㅇ", "계속해줘", "아까 그거" 같은 말에는
323
+ 종합 그대로 있습니다. 1등과 2등이 비슷할 때도 안 옮깁니다 —
324
+ 잘못 옮겨서 읽기 전용 모드에 갇히면 사용자는 *왜* 막혔는지 모른 채 막힙니다.
325
+ 그래서 읽기 전용 모드(계획·설계·묻기)는 문턱을 더 높게 뒀습니다.
326
+ "설명해주고 고쳐줘" 는 묻기가 아니라 코드로 갑니다.
327
+
328
+ 옮겨 간 것은 **그 한마디에만** 붙습니다. 다음 말은 다시 처음부터 고릅니다.
329
+ 상태줄에 `~` 가 붙으면 저절로 옮겨 간 것이고, 없으면 직접 고르신 것입니다.
330
+
331
+ ```
332
+ ◎ 종합 ← 대기 중
333
+ ~◉ 디버그 ← 이번 한마디만 저절로
334
+ ◉ 디버그 ← /debug 로 직접 고름. 저절로 안 바뀝니다
335
+ ```
336
+
337
+ 직접 고르면 그때부터 **고정** 됩니다. 다시 맡기려면 `/auto` 또는 `/work 종합`.
338
+
294
339
  ---
295
340
 
296
341
  ## 쉬움 · 개발자
@@ -527,6 +572,51 @@ $ /think
527
572
  | `save` (절약, 기본) | 첫 판단만 세게, 이어가기는 얕게 |
528
573
  | `deep` (깊게) | 전 단계 한 칸씩 위로 — 어려운 일에만 |
529
574
 
575
+ ### 컨텍스트 길이는 모델에서 긁어옵니다
576
+
577
+ 이 숫자 하나가 프로그램 전체 크기를 정합니다. 한 번에 읽힐 수 있는 파일 수,
578
+ 대화가 접히는 시점, 한 번에 쓸 수 있는 답 길이가 **전부 여기서 나옵니다.**
579
+
580
+ 그래서 켤 때마다 서버에 물어봅니다. 저장된 값을 그대로 믿지 않습니다 —
581
+ 같은 이름의 모델이라도 서버에서 몇 k 로 올렸는지가 그때그때 다르고,
582
+ 그 차이는 화면에 안 뜨면 알 길이 없습니다. **그냥 조용히 작아집니다.**
583
+
584
+ ```
585
+ │ 모델 qwen3-coder (640k 토큰) │
586
+ ╰─────────────────────────────────────────────────────────╯
587
+ ✓ 컨텍스트를 32,768 → 655,360 로 맞췄습니다 (LM Studio에서 읽음)
588
+ ```
589
+
590
+ 서버마다 이 숫자를 다른 이름, 다른 자리에 둡니다. 한 군데만 보지 않습니다.
591
+
592
+ | 서버 | 어디서 읽나 |
593
+ |---|---|
594
+ | LM Studio | `/api/v0/models` — `max_context_length` · `loaded_context_length` |
595
+ | llama.cpp | `/props` — `n_ctx` |
596
+ | vLLM | `/v1/models` — `max_model_len` |
597
+ | Ollama | `/api/show` — `<모델>.context_length` |
598
+ | 그 밖의 OpenAI 호환 | `/v1/models/<모델>` — `context_window` · `context_length` · `max_input_tokens` · `max_position_embeddings` (깊이 박혀 있어도 찾습니다) |
599
+
600
+ **모델 최대와 올려 둔 길이를 구분합니다.** LM Studio 는 655,360 까지 되는 모델을
601
+ 8,192 로 올려 둘 수 있습니다. 그 상태에서 최대치를 믿고 보내면 서버가 거절합니다.
602
+ 그래서 **실제로 쓸 값은 올려 둔 길이**로 잡고, 최대치는 따로 알려 줍니다.
603
+
604
+ ```
605
+ ⚠ 이 모델은 655,360 까지 됩니다 — 서버에서 더 올린 뒤 /ctx auto
606
+ ```
607
+
608
+ | 명령 | 하는 일 |
609
+ |---|---|
610
+ | `/ctx` | 지금 값과 남은 자리 |
611
+ | `/ctx auto` | 서버에 다시 물어 모델에 맞춤 |
612
+ | `/ctx 655360` | 직접 지정 (`640k` · `128k` · `1m` 도 됩니다) |
613
+ | `/ctx out 32k` | 한 번에 받을 **답 길이** 상한 — 컨텍스트와 다른 축 |
614
+ | `deel --ctx 655360` | 켤 때부터 이 값으로 (긁어오기를 건너뜁니다) |
615
+
616
+ **`k` 는 1024 입니다.** 컨텍스트 길이는 전부 2의 거듭제곱이라 그래야 아귀가 맞습니다 —
617
+ 655,360 은 `655k` 가 아니라 `640k`, 131,072 는 `131k` 가 아니라 `128k` 입니다.
618
+ 화면에 뜨는 표기와 `/ctx` 가 받는 단위가 같아서, 보이는 대로 쳐도 같은 값이 됩니다.
619
+
530
620
  **출력 상한은 고정 숫자가 아닙니다.** 모델 컨텍스트와 지금 찬 양에서 매번 계산합니다 —
531
621
  남은 자리의 몇 %를 이 단계에 내줄지가 배분입니다.
532
622
 
@@ -709,6 +799,8 @@ node bin/deel.js diagnose --url <게이트웨이주소> --key <키> --model <모
709
799
  ```bash
710
800
  deel --root <폴더> 작업 범위. 기본은 지금 폴더
711
801
  deel --mode <모드> auto(기본) / confirm / strict
802
+ deel --work <모드> auto(기본·종합) / code / plan / architect / debug / ask / orchestrator
803
+ deel --level <수준> 쉬움 / 개발자
712
804
  deel --think <강도> off / low / medium(기본) / high / max
713
805
  deel --effort <배분> even / save(기본) / deep
714
806
  deel --offline 이 컴퓨터 밖으로 아무것도 안 보냄
package/bin/deel.js CHANGED
@@ -6,6 +6,8 @@ import { runSetup, runDiagnose, showStatus, banner } from '../src/setup.js';
6
6
  import { chatLoop } from '../src/repl.js';
7
7
  import { packSelf, audit, reviewSheet } from '../src/pack/selfpack.js';
8
8
  import { runScan } from '../src/backend/scanui.js';
9
+ import { closeConnections } from '../src/backend/http.js';
10
+ import { parseSize } from '../src/backend/ctxsize.js';
9
11
  import { runSessions } from '../src/agent/sessionui.js';
10
12
 
11
13
  const MIN_NODE = 20;
@@ -85,6 +87,9 @@ function help() {
85
87
  say('');
86
88
  say(` ${c.gray('--root <폴더>')} 작업 범위. 기본은 지금 폴더`);
87
89
  say(` ${c.gray('--mode <모드>')} auto(기본) / confirm / strict`);
90
+ say(` ${c.gray('--work <모드>')} auto(기본·종합) / code / plan / architect / debug / ask / orchestrator`);
91
+ say(` ${c.gray('--level <수준>')} 쉬움(기본) / 개발자`);
92
+ say(` ${c.gray('--ctx <길이>')} 컨텍스트 길이 직접 지정 (655360 · 640k · 128k). 없으면 서버에 맞춤`);
88
93
  say(` ${c.gray('--think <수준>')} off / low / medium(기본) / high / max`);
89
94
  say(` ${c.gray('--effort <배분>')} even(균일) / save(절약, 기본) / deep(깊게)`);
90
95
  say(` ${c.gray('--offline')} 이 컴퓨터 밖으로는 아무것도 안 보냄 (자물쇠)`);
@@ -119,6 +124,9 @@ async function main() {
119
124
  return chatLoop({
120
125
  root: flags.root ? String(flags.root) : undefined,
121
126
  mode: flags.mode ? String(flags.mode) : undefined,
127
+ work: flags.work ? String(flags.work) : undefined,
128
+ level: flags.level ? String(flags.level) : undefined,
129
+ ctx: flags.ctx ? parseSize(String(flags.ctx)) : undefined,
122
130
  think: flags.think ? String(flags.think) : undefined,
123
131
  effort: flags.effort ? String(flags.effort) : undefined,
124
132
  offline: flags.offline === true || flags.offline === 'true',
@@ -149,12 +157,30 @@ async function main() {
149
157
  }
150
158
  }
151
159
 
160
+ /**
161
+ * 끝낸다.
162
+ *
163
+ * process.exit() 을 바로 부르면 안 된다. fetch 가 살려 둔 소켓이 닫히는 중일 때
164
+ * 끊으면 윈도우에서 libuv 가 abort 한다 — 화면에는 정상으로 보이는데 종료코드가
165
+ * 3221226505 로 나온다. `deel scan` 이 실제로 그랬다.
166
+ *
167
+ * 그래서 연결을 먼저 닫고, 종료코드만 정해 두고 이벤트 루프가 저절로 비기를
168
+ * 기다린다. 그래도 안 비면(무언가 물고 있으면) 잠깐 뒤에 확실히 끝낸다.
169
+ * 그 타이머는 unref 라서, 정상적으로 끝나는 길을 막지 않는다.
170
+ */
171
+ async function 끝내기(code) {
172
+ process.exitCode = code;
173
+ await closeConnections();
174
+ const 마지막수단 = setTimeout(() => process.exit(code), 400);
175
+ 마지막수단.unref();
176
+ }
177
+
152
178
  main()
153
- .then((code) => process.exit(code ?? 0))
154
- .catch((err) => {
179
+ .then((code) => 끝내기(code ?? 0))
180
+ .catch(async (err) => {
155
181
  say('');
156
182
  say(` ${c.red('오류')} ${err?.message ?? err}`);
157
183
  if (process.env.DEEL_DEBUG) say(c.gray(err?.stack ?? ''));
158
184
  say('');
159
- process.exit(1);
185
+ await 끝내기(1);
160
186
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deel-local-cli",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "로컬 모델·사내 게이트웨이 전용 코딩 에이전트 CLI — 외부 의존성 0개 / Zero-dependency coding agent CLI for local LLMs and private gateways",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -46,7 +46,7 @@
46
46
  "verify": "node test/no-bundle.test.js && node test/network.test.js && node test/web.test.js",
47
47
  "bench": "node test/edit-bench.js",
48
48
  "demo": "node test/demo.js",
49
- "check": "node --check bin/deel.js && node --check src/repl.js && node --check src/commands.js && node --check src/agent/loop.js && node --check src/agent/session.js && node --check src/agent/effort.js && node --check src/agent/modes.js && node --check src/tools/encoding.js && node --check src/tools/excel-com.js && node --check src/tools/excel.js && node --check src/tools/xlsx.js && node --check src/agent/compact.js && node --check src/agent/store.js && node --check src/agent/sessionui.js && node --check src/backend/adapter.js && node --check src/backend/probe.js && node --check src/backend/detect.js && node --check src/backend/http.js && node --check src/backend/scan.js && node --check src/backend/scanui.js && node --check src/tools/index.js && node --check src/tools/fsutil.js && node --check src/tools/edit-match.js && node --check src/tools/webfetch.js && node --check src/tools/todo.js && node --check src/skills/discover.js && node --check src/plugins/manage.js && node --check src/pack/zip.js && node --check src/pack/tar.js && node --check src/pack/selfpack.js && node --check src/safety/guard.js && node --check src/safety/undo.js && node --check src/safety/audit.js && node --check src/safety/network.js && node --check src/setup.js && node --check src/report.js && node --check src/config.js && node --check src/ui/ansi.js && node --check src/ui/status.js && node --check src/ui/level.js && node --check src/ui/prompt.js && node --check src/ui/spinner.js && echo OK"
49
+ "check": "node --check bin/deel.js && node --check src/repl.js && node --check src/commands.js && node --check src/agent/loop.js && node --check src/agent/session.js && node --check src/agent/effort.js && node --check src/agent/modes.js && node --check src/agent/route.js && node --check src/tools/encoding.js && node --check src/tools/excel-com.js && node --check src/tools/excel.js && node --check src/tools/xlsx.js && node --check src/agent/compact.js && node --check src/agent/store.js && node --check src/agent/sessionui.js && node --check src/backend/adapter.js && node --check src/backend/ctxsize.js && node --check src/backend/probe.js && node --check src/backend/detect.js && node --check src/backend/http.js && node --check src/backend/scan.js && node --check src/backend/scanui.js && node --check src/tools/index.js && node --check src/tools/fsutil.js && node --check src/tools/edit-match.js && node --check src/tools/webfetch.js && node --check src/tools/todo.js && node --check src/skills/discover.js && node --check src/plugins/manage.js && node --check src/pack/zip.js && node --check src/pack/tar.js && node --check src/pack/selfpack.js && node --check src/safety/guard.js && node --check src/safety/undo.js && node --check src/safety/audit.js && node --check src/safety/network.js && node --check src/setup.js && node --check src/report.js && node --check src/config.js && node --check src/ui/ansi.js && node --check src/ui/status.js && node --check src/ui/level.js && node --check src/ui/prompt.js && node --check src/ui/spinner.js && echo OK"
50
50
  },
51
51
  "dependencies": {},
52
52
  "devDependencies": {}
package/src/agent/loop.js CHANGED
@@ -68,10 +68,10 @@ export async function* run(session, ctx, userText, { signal = null } = {}) {
68
68
  const tools = toolSchemas(null, {
69
69
  hasSkills: (session.skills?.length ?? 0) > 0,
70
70
  web: session.web !== false && !isOffline(), // 오프라인이면 웹 도구는 아예 안 보여 준다
71
- work: session.work, // 작업 모드가 쓰는 것만 (modes.js)
71
+ work: session.effectiveWork(), // 작업 모드가 쓰는 것만 (modes.js)
72
72
  });
73
73
  // 모드마다 생각의 배분과 걸음 수가 다르다. 사용자가 따로 정했으면 그걸 존중한다.
74
- const 모드 = workMode(session.work);
74
+ const 모드 = workMode(session.effectiveWork());
75
75
  const effort = session.effortSet ? session.effort : (모드.effort ?? session.effort);
76
76
  const think = session.thinkSet ? session.think : (모드.think ?? session.think);
77
77
  const maxSteps = session.stepsSet ? session.maxSteps : (모드.steps ?? session.maxSteps);