deel-local-cli 1.15.1 → 1.16.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.ko.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
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
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-7%2C152%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/ko/develop.md)
23
+ [![tests](https://img.shields.io/badge/tests-7%2C162%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/ko/develop.md)
24
24
 
25
25
  [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
26
26
  [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
@@ -94,7 +94,7 @@
94
94
  | [속도와 씀씀이](docs/ko/tuning.md) | 단계별 추론 강도 · 프리픽스 캐시 · 컨텍스트 길이 |
95
95
  | [안전망과 사내 반입](docs/ko/safety.md) | 되돌리기 · 작업 범위 · 감사기록 · 심사 서류 |
96
96
  | [설정](docs/ko/config.md) · [개발](docs/ko/develop.md) | 환경변수 · 실행 옵션 · 검사 돌리기 · 폴더 구조 |
97
- | [릴리스 노트](docs/ko/releases.md) | [1.15.x](docs/ko/releases/1.15.md) · [1.14.x](docs/ko/releases/1.14.md) · [1.13.x](docs/ko/releases/1.13.md) · [1.12.x](docs/ko/releases/1.12.md) · [1.10.x](docs/ko/releases/1.10.md) · [1.9.x](docs/ko/releases/1.9.md) · [그 앞](docs/ko/releases.md) |
97
+ | [릴리스 노트](docs/ko/releases.md) | [1.16.x](docs/ko/releases/1.16.md) · [1.15.x](docs/ko/releases/1.15.md) · [1.14.x](docs/ko/releases/1.14.md) · [1.13.x](docs/ko/releases/1.13.md) · [1.12.x](docs/ko/releases/1.12.md) · [1.10.x](docs/ko/releases/1.10.md) · [1.9.x](docs/ko/releases/1.9.md) · [그 앞](docs/ko/releases.md) |
98
98
 
99
99
  ---
100
100
 
@@ -267,7 +267,7 @@ deel --offline
267
267
  무엇이 어디로 갈 수 있는지는 켤 때 화면 맨 위에 늘 적혀 있습니다.
268
268
 
269
269
  ```
270
- deel 1.15.1 ⌂ 이 안
270
+ deel 1.16.0 ⌂ 이 안
271
271
  보냄 이 컴퓨터 안 127.0.0.1:11434 ← 여기 말고는 어디로도 안 갑니다
272
272
  ```
273
273
 
@@ -482,7 +482,7 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
482
482
  | `/mode <모드>` | 승인 정책 — 얼마나 물어보나 (`auto` · `confirm` · `strict`) |
483
483
  | `/work [모드]` | 작업 모드 — 무슨 일을 하는 중인가 |
484
484
  | `/auto` | 다시 맡기기 — 말을 보고 알맞은 모드로 저절로 옮겨 갑니다 |
485
- | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | 작업 모드 바로 바꾸기 (그때부터 고정) |
485
+ | `/code` `/plan` `/architect` `/debug` `/inspect` `/ask` `/orchestrator` | 작업 모드 바로 바꾸기 (그때부터 고정) |
486
486
  | `/level [수준]` | 화면에 무엇을 내놓을지 (`쉬움` · `개발자`) |
487
487
  | `/motion [기본\|기사\|동물\|사무실\|끔]` | 일하는 동안 뭐가 도나 — 그 자리에서 바뀌고 설정에 남습니다 |
488
488
  | `/undo [턴수]` | 파일 변경 되돌리기 |
@@ -525,6 +525,11 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
525
525
 
526
526
  ## 작업 모드
527
527
 
528
+ <picture>
529
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-work-ko-dark.svg">
530
+ <img alt="deel 작업 모드 목록 — 모드마다 파일을 바꿀 수 있는지, 생각 강도, 최대 걸음 수" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-work-ko-light.svg" width="900">
531
+ </picture>
532
+
528
533
  무슨 일을 하는 중인지에 따라 **줄 수 있는 도구와 추론 설정이 같이 바뀝니다.**
529
534
  `Shift+Tab` 으로 차례로 돌리거나, 이름을 그대로 치면 됩니다.
530
535
 
@@ -534,9 +539,13 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
534
539
  | `/code` ◆ 코드 | 고치고 만든다 | 예 | 보통 (`save`) |
535
540
  | `/plan` ☰ 계획 | 먼저 계획만 세운다 | **아니오** | 깊게 (`deep`·high) |
536
541
  | `/architect` ◈ 설계 | 구조를 짠다 | **아니오** | 깊게 (`deep`·high) |
537
- | `/debug` ◉ 디버그 | 원인을 찾는다 | 예 | 깊게 · 단계 많이 (32) |
538
- | `/ask` 묻기 | 설명만 한다 | **아니오** | 얕게 (`low`) |
539
- | `/orchestrator` 총괄 | 일을 쪼개서 | | 단계 아주 많이 (40) |
542
+ | `/debug` ◉ 디버그 | 원인을 찾는다 | 예 | 깊게 · 걸음 많이 |
543
+ | `/inspect` 점검 | 결함을 찾는다 — 근거를 대고, 안 고친다 | **아니오** | 깊게 (`deep`·high) · 걸음 많이 |
544
+ | `/ask` 묻기 | 설명만 한다 | **아니오** | 얕게 (`low`) — 다만 시킨 것이 여럿이면 안 낮춥니다 |
545
+ | `/orchestrator` ❋ 총괄 | 큰 일을 쪼개서 | 예 | 걸음 아주 많이 |
546
+
547
+ 걸음 수는 **모델 창 크기를 따라갑니다** — 128k 짜리에서 디버그는 250걸음, 묻기는 12걸음입니다.
548
+ 작은 모델에 400걸음을 주면 창이 넘쳐 앞엣말을 잊은 채로 도는 것뿐입니다.
540
549
 
541
550
  읽기만 하는 모드에서는 `Write`·`Edit`·`Bash` 를 **모델에게 아예 보내지 않습니다.**
542
551
  "고치지 마세요" 라고 부탁하지 않습니다 — 모델은 부탁을 잊습니다. 없는 도구는 못 씁니다.
@@ -544,7 +553,7 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
544
553
  `/mode` 와 헷갈리지 마세요. 둘은 다른 축입니다.
545
554
 
546
555
  - `/mode` — **얼마나 물어보나** (auto · confirm · strict)
547
- - `/work` — **무슨 일을 하는 중인가** (위 일곱 가지)
556
+ - `/work` — **무슨 일을 하는 중인가** (위 여덟 가지)
548
557
 
549
558
  `/think` 나 `/mode` 를 직접 고른 적이 있으면 그 선택이 우선합니다.
550
559
  모드가 사람이 고른 값을 덮어쓰지 않습니다.
@@ -629,12 +638,41 @@ PDF 는 **못 읽은 쪽을 못 읽었다고 말합니다.** 스캔본·암호·
629
638
  빈 글로 넘기지 않고 그 자리에 까닭을 적습니다 — 빈 글은 「그런 내용이 없다」로
630
639
  읽히기 때문입니다.
631
640
 
641
+ 문서를 **마크다운 한 장으로** 뽑아 두려면 `deel doc2md` 입니다. 표가
642
+ `| 이름 | 값 |` 꼴로 살아 있는 채로 나오기 때문에, 평평한 글보다 모델이 훨씬
643
+ 덜 헷갈립니다.
644
+
645
+ <picture>
646
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-doc2md-dark.svg">
647
+ <img alt="deel doc2md 로 docx 를 마크다운으로 — 표가 마크다운 표로 그대로 남는다" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-doc2md-light.svg" width="820">
648
+ </picture>
649
+
650
+ ```bash
651
+ deel doc2md 보고서.pptx # 표준출력으로
652
+ deel doc2md 스펙.pdf --out 스펙.md # 파일로
653
+ ```
654
+
655
+ **Figma 시안(`.fig`)도 읽습니다.** 「이 시안대로 만들어 줘」 하고 건네받은
656
+ 파일이 「이진 파일이라 못 읽습니다」 로 끝나던 자리입니다. 안에는 그림만이
657
+ 아니라 **틀 이름 · 글 · 크기 · 겹친 차례**가 다 글자로 들어 있습니다.
658
+
659
+ ```
660
+ - FRAME · 로그인 화면 375×812
661
+ - TEXT · 제목 "다시 오신 것을 환영합니다"
662
+ - FRAME · 입력칸 335×48
663
+ - TEXT · 라벨 "이메일"
664
+ ```
665
+
666
+ 색·그림자·글꼴은 안 냅니다 — 그리고 **안 낸다고 먼저 말합니다.** 요즘 `.fig`
667
+ 는 zstd 로 눌려 있어 Node 22.15 이상이 필요하고, 낮은 판에서는 「깨진 파일」 이
668
+ 아니라 **여기서 못 푼다**고 말합니다.
669
+
632
670
  옛 형식(`.ppt`·`.doc`·`.xls`·`.rtf`)은 **이 PC 에 깔린 LibreOffice 를 빌려서**
633
671
  읽습니다 — `rg` 를 빌려 쓰는 것과 같은 원칙으로, 아무것도 설치하지 않습니다.
634
672
  없으면 **없다고 못 박고 끝냅니다**(무엇이 없어서 못 하는지, 무엇을 하면 되는지,
635
673
  그리고 「다시 열지 말라」까지). 끄려면 `DEEL_CONVERT=off`.
636
674
 
637
- > **자세히** — 인코딩 · 엑셀 · hwpx/docx/pptx · PDF · 변환기 빌려 쓰기
675
+ > **자세히** — 인코딩 · 엑셀 · hwpx/docx/pptx · PDF · `doc2md` · 변환기 빌려 쓰기
638
676
  >
639
677
  > **[한글 문서와 엑셀 읽기 →](docs/ko/documents.md#한글-문서와-엑셀)**
640
678
 
@@ -898,6 +936,23 @@ deel acp
898
936
  에디터가 이걸 자식 프로세스로 띄우고, 표준입출력으로 줄 단위 JSON-RPC 2.0 을
899
937
  주고받습니다. 사람이 직접 칠 명령은 아닙니다.
900
938
 
939
+ **Zed 라면 이렇게 적습니다.** 에이전트 패널의 `Settings → External Agents →
940
+ Add Custom Agent` 를 눌러도 되고, `settings.json` 에 바로 적어도 됩니다.
941
+
942
+ ```json
943
+ {
944
+ "agent_servers": {
945
+ "deel": { "type": "custom", "command": "npx", "args": ["-y", "deel-local-cli", "acp"], "env": {} }
946
+ }
947
+ }
948
+ ```
949
+
950
+ 그리고 에이전트 패널에서 **deel** 을 고릅니다. 아직 `deel setup` 을 안 했다면
951
+ deel 이 「연결이 없다」 고 에디터에게 **규격대로**(ACP 의 `AuthRequired`) 말하고,
952
+ 같이 보내 둔 ``Run `deel setup` in a terminal`` 이 뜹니다 — 빨간 글씨만 보고
953
+ 버그인지 설정 문제인지 헤매지 않게 하려는 것입니다. 터미널에서 한 번 마치면
954
+ 그 다음부터는 바로 대화가 열립니다.
955
+
901
956
  **붙으면 이렇게 됩니다.**
902
957
 
903
958
  | 에디터 쪽 | deel 쪽 |
@@ -906,7 +961,7 @@ deel acp
906
961
  | 도구 목록 (아이콘·상태) | `Read` 는 읽기, `Edit` 는 고치기, `Bash` 는 실행 — **갈래를 붙여** 보냅니다 |
907
962
  | 파일 링크 | 고친 파일의 **절대 경로**. 눌러서 그 자리로 갑니다 |
908
963
  | 승인 창 | deel 의 안전망이 그대로 에디터 창으로 뜹니다 (`이번만` · `앞으로 묻지 않기` · `안 함`) |
909
- | 모드 고르개 | deel 의 작업 모드 7개 (`종합` · `코드` · `계획` · `설계` · `디버그` · `묻기` · `총괄`) |
964
+ | 모드 고르개 | deel 의 작업 모드 8개 (`종합` · `코드` · `계획` · `설계` · `디버그` · `점검` · `묻기` · `총괄`) |
910
965
  | 멈춤 단추 | 도중에 닿습니다 — 답을 기다리는 중에도 |
911
966
  | 지난 대화 | 껐다 켜도 그대로 열립니다. 터미널과 **같은 자리**에 남아서, 에디터에서 하던 것을 `deel --resume` 으로 이어받는 것도 됩니다 |
912
967
 
@@ -1087,7 +1142,7 @@ deel sbom --only sbom # SBOM 한 장만
1087
1142
  ## 개발
1088
1143
 
1089
1144
  ```bash
1090
- npm test 전체 검증 (7,152항목 — 몇몇은 터미널에 따라 갈립니다)
1145
+ npm test 전체 검증 (7,346항목 — 몇몇은 터미널에 따라 갈립니다)
1091
1146
  npm run coverage 검사가 소스의 어디를 밟았는지
1092
1147
  npm run verify 반입·통신 검증만
1093
1148
  npm run bench 편집 성공률 측정
@@ -1144,7 +1199,7 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
1144
1199
  | `exitcode` · `doorparity` | 7 · 7 | 화면에 적은 종료코드 표가 진짜인가 · 문 **네 개**가 같은 것을 주는가 |
1145
1200
  | `no-bundle` | 21 | 배포 묶음에 남의 것이 안 섞였는가 · 검사 파일 위생 |
1146
1201
  | `edit-bench` | 20건 | 편집 성공률 |
1147
- | `mutate` | 어긋 11개 | **검사가 정말 지키는가** — 지켜야 할 줄을 일부러 어긋내고 빨개지는지 본다 |
1202
+ | `mutate` | 어긋 12개 | **검사가 정말 지키는가** — 지켜야 할 줄을 일부러 어긋내고 빨개지는지 본다 |
1148
1203
 
1149
1204
  > **자세히** — 어디를 밟았는지 · 폴더 구조
1150
1205
  >
@@ -1156,6 +1211,7 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
1156
1211
 
1157
1212
  | 판 | 무엇이 바뀌었나 |
1158
1213
  |---|---|
1214
+ | **[1.16.0](docs/ko/releases/1.16.md#1160)** | 깊이 봐 달라는 말을 얕게 받던 자리 · 게이트웨이 캐시 · `doc2md` · Figma `.fig` |
1159
1215
  | **[1.15.1](docs/ko/releases/1.15.md#1151)** | 에디터에 우리가 어떻게 인증받는지 말합니다 — ACP 레지스트리 등재 조건 |
1160
1216
  | **[1.15.0](docs/ko/releases/1.15.md#1150)** | 같은 것을 두 번 하지 않습니다 — 프리픽스 캐시를 깨던 조용히 비싼 아홉 자리 |
1161
1217
  | **[1.14.0](docs/ko/releases/1.14.md#1140)** | 적어 둔 것이 참인지 기계가 대신 봅니다 — 초록불과 「깨면 잡힌다」 는 다릅니다 |
package/README.md CHANGED
@@ -20,7 +20,7 @@ Vendor APIs connect too — **only when you say so**
20
20
 
21
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
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-7%2C152%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
23
+ [![tests](https://img.shields.io/badge/tests-7%2C162%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
24
24
 
25
25
  [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
26
26
  [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
@@ -119,7 +119,7 @@ This page is the **summary**. Each section links to the detail behind it.
119
119
  | [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
120
120
  | [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
121
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.15.x](docs/en/releases/1.15.md) · [1.14.x](docs/en/releases/1.14.md) · [1.13.x](docs/en/releases/1.13.md) · [1.12.x](docs/en/releases/1.12.md) · [1.10.x](docs/en/releases/1.10.md) · [1.9.x](docs/en/releases/1.9.md) · [older](docs/en/releases.md) |
122
+ | [Release notes](docs/en/releases.md) | [1.16.x](docs/en/releases/1.16.md) · [1.15.x](docs/en/releases/1.15.md) · [1.14.x](docs/en/releases/1.14.md) · [1.13.x](docs/en/releases/1.13.md) · [1.12.x](docs/en/releases/1.12.md) · [1.10.x](docs/en/releases/1.10.md) · [1.9.x](docs/en/releases/1.9.md) · [older](docs/en/releases.md) |
123
123
 
124
124
  ---
125
125
 
@@ -338,7 +338,7 @@ deel --offline
338
338
  The destination is printed at the top of every session:
339
339
 
340
340
  ```
341
- deel 1.15.1 ⌂ inside
341
+ deel 1.16.0 ⌂ inside
342
342
  Sends to this machine 127.0.0.1:11434 ← nowhere else
343
343
  ```
344
344
 
@@ -545,7 +545,7 @@ Names follow Claude Code / Codex conventions.
545
545
  | `/mode <mode>` | Approval policy — how much it asks (`auto` · `confirm` · `strict`) |
546
546
  | `/work [mode]` | Work mode — what kind of work you are doing |
547
547
  | `/auto` | Hand the wheel back — it picks the mode from what you type |
548
- | `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
548
+ | `/code` `/plan` `/architect` `/debug` `/inspect` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
549
549
  | `/level [level]` | How much to show (`simple` · `developer`) |
550
550
  | `/motion [plain\|knight\|animal\|office\|off]` | What animates while it works — takes effect at once, and is saved |
551
551
  | `/undo [turns]` | Revert file changes |
@@ -591,6 +591,11 @@ Korean IME composition, paste, `Ctrl+A/E` and backspace all keep working.
591
591
 
592
592
  ## Work modes
593
593
 
594
+ <picture>
595
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-work-en-dark.svg">
596
+ <img alt="deel work modes: whether each can edit files, its reasoning depth, and its step ceiling" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-work-en-light.svg" width="900">
597
+ </picture>
598
+
594
599
  What you are working on changes **which tools the model is given and how hard it thinks.**
595
600
  Cycle with `Shift+Tab`, or type the name.
596
601
 
@@ -600,9 +605,13 @@ Cycle with `Shift+Tab`, or type the name.
600
605
  | `/code` ◆ Code | Writing and fixing | Yes | Normal (`save`) |
601
606
  | `/plan` ☰ Plan | Planning first | **No** | Deep (`deep`·high) |
602
607
  | `/architect` ◈ Architect | Shaping structure | **No** | Deep (`deep`·high) |
603
- | `/debug` ◉ Debug | Finding causes | Yes | Deep, more steps (32) |
604
- | `/ask` Ask | Explaining only | **No** | Shallow (`low`) |
605
- | `/orchestrator` Orchestrator | Breaking up large work | Yes | Many steps (40) |
608
+ | `/debug` ◉ Debug | Finding causes | Yes | Deep, many steps |
609
+ | `/inspect` Inspect | Auditing for defects — with evidence, changing nothing | **No** | Deep (`deep`/high), many steps |
610
+ | `/ask` Ask | Explaining only | **No** | Shallow (`low`) — not lowered when the ask has several parts |
611
+ | `/orchestrator` ❋ Orchestrator | Breaking up large work | Yes | Very many steps |
612
+
613
+ The step ceiling **follows the model's window** — on a 128k model debug gets 250 steps and ask gets 12.
614
+ Hand a small model 400 steps and it just runs on past the point where its window folded.
606
615
 
607
616
  In read-only modes, `Write`, `Edit` and `Bash` are **never sent to the model at all.**
608
617
  It is not asked politely not to edit — models forget requests. A tool that isn't there can't be used.
@@ -610,7 +619,7 @@ It is not asked politely not to edit — models forget requests. A tool that isn
610
619
  Don't confuse this with `/mode`. They are separate axes:
611
620
 
612
621
  - `/mode` — **how much it asks you** (auto · confirm · strict)
613
- - `/work` — **what kind of work you are doing** (the seven above)
622
+ - `/work` — **what kind of work you are doing** (the eight above)
614
623
 
615
624
  If you have explicitly set `/think` or `/mode`, your choice wins. A work mode never
616
625
  overrides something a person chose.
@@ -689,12 +698,42 @@ their cost; here is why.
689
698
  **A file saved as CP949 is written back as CP949.** The encoding is never changed.
690
699
  Excel (`.xlsx`) is read as CSV — read-only.
691
700
 
701
+ To keep a document as **one Markdown file**, use `deel doc2md`. Tables survive as
702
+ real `| name | value |` tables, so a model has far less to guess at than it does
703
+ with flattened text.
704
+
705
+ <picture>
706
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-doc2md-dark.svg">
707
+ <img alt="deel doc2md turning a docx into Markdown — the table stays a table" src="https://raw.githubusercontent.com/jysvai/deel-local-cli/main/docs/assets/shot-doc2md-light.svg" width="820">
708
+ </picture>
709
+
710
+ ```bash
711
+ deel doc2md report.pptx # to stdout
712
+ deel doc2md spec.pdf --out spec.md # to a file
713
+ ```
714
+
715
+ **Figma designs (`.fig`) are read too.** Handing over a design with "build this"
716
+ used to end at "binary file, cannot be read". What is inside is not only
717
+ pictures — **frame names, text, sizes and stacking order** are all there as
718
+ characters.
719
+
720
+ ```
721
+ - FRAME · Login screen 375×812
722
+ - TEXT · Title "Welcome back"
723
+ - FRAME · Field 335×48
724
+ - TEXT · Label "Email"
725
+ ```
726
+
727
+ No colours, shadows or fonts — and it **says so up front**. Recent `.fig` files
728
+ are zstd-compressed, which needs Node 22.15 or newer; on an older one it says
729
+ **this Node cannot unpack it**, not "corrupt file".
730
+
692
731
  Old formats (`.ppt`, `.doc`, `.xls`, `.rtf`) are read by **borrowing the LibreOffice already
693
732
  on this machine** — the same terms on which deel borrows `rg`, and nothing is ever installed.
694
733
  With no converter it **says so definitively and stops**: what is missing, what you can do about
695
734
  it, and not to open the file again. Turn it off with `DEEL_CONVERT=off`.
696
735
 
697
- > **More** — Encoding · Excel · borrowing a converter
736
+ > **More** — Encoding · Excel · `doc2md` · borrowing a converter
698
737
  >
699
738
  > **[Korean documents and Excel read →](docs/en/documents.md#korean-text-and-excel)**
700
739
 
@@ -977,6 +1016,23 @@ deel acp
977
1016
  The editor spawns that as a child process and exchanges newline-delimited JSON-RPC 2.0 over
978
1017
  stdio. It is not a command you type yourself.
979
1018
 
1019
+ **In Zed, concretely.** Either use `Settings → External Agents → Add Custom Agent` in the
1020
+ agent panel, or put this straight into `settings.json`:
1021
+
1022
+ ```json
1023
+ {
1024
+ "agent_servers": {
1025
+ "deel": { "type": "custom", "command": "npx", "args": ["-y", "deel-local-cli", "acp"], "env": {} }
1026
+ }
1027
+ }
1028
+ ```
1029
+
1030
+ Then pick **deel** in the agent panel. If you have not run `deel setup` yet, deel tells the
1031
+ editor so **the way the spec says to** (ACP's `AuthRequired`), along with the method it sent
1032
+ at connect time — ``Run `deel setup` in a terminal``. That is there so a first run is not a
1033
+ red error message you cannot tell from a bug. Finish it once in a terminal and the editor
1034
+ opens straight into a conversation from then on.
1035
+
980
1036
  **What you get once it is attached:**
981
1037
 
982
1038
  | In the editor | From deel |
@@ -985,7 +1041,7 @@ stdio. It is not a command you type yourself.
985
1041
  | 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 |
986
1042
  | Clickable file links | The **absolute path** of every file touched |
987
1043
  | Approval dialog | deel's safety rails, rendered as the editor's own prompt (`allow once` · `always allow` · `reject`) |
988
- | Mode picker | deel's seven work modes (auto · code · plan · architect · debug · ask · orchestrator) |
1044
+ | Mode picker | deel's eight work modes (auto · code · plan · architect · debug · inspect · ask · orchestrator) |
989
1045
  | Stop button | Reaches the turn mid-flight, even while waiting on the model |
990
1046
  | 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` |
991
1047
 
@@ -1158,7 +1214,7 @@ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder tak
1158
1214
  ## Development
1159
1215
 
1160
1216
  ```bash
1161
- npm test Full suite (7,152 checks; a few are TTY-dependent)
1217
+ npm test Full suite (7,346 checks; a few are TTY-dependent)
1162
1218
  npm run coverage Which lines the tests actually execute
1163
1219
  npm run verify Import + network checks only
1164
1220
  npm run bench Edit success rate
@@ -1201,7 +1257,7 @@ so one run tells you everything.
1201
1257
  | `exitcode` · `doorparity` | 7 · 7 | The printed exit-code table is real · all **four** doors hand out the same thing |
1202
1258
  | `no-bundle` | 21 | Nothing foreign in the published package; test-file hygiene |
1203
1259
  | `edit-bench` | 20 cases | Edit success rate |
1204
- | `mutate` | 11 mutants | **Whether the tests actually guard** — break the line on purpose, check it turns red |
1260
+ | `mutate` | 12 mutants | **Whether the tests actually guard** — break the line on purpose, check it turns red |
1205
1261
 
1206
1262
  > **More** — Coverage · Layout
1207
1263
  >
@@ -1213,6 +1269,7 @@ so one run tells you everything.
1213
1269
 
1214
1270
  | Version | What changed |
1215
1271
  |---|---|
1272
+ | **[1.16.0](docs/en/releases/1.16.md#1160)** | A deep request no longer lands in a shallow mode · gateway cache · `doc2md` · Figma `.fig` |
1216
1273
  | **[1.15.1](docs/en/releases/1.15.md#1151)** | Telling the editor how we get authenticated — ACP Registry listing requirement |
1217
1274
  | **[1.15.0](docs/en/releases/1.15.md#1150)** | Stop doing the same thing twice — nine quietly expensive places that broke the prefix cache |
1218
1275
  | **[1.14.0](docs/en/releases/1.14.md#1140)** | A machine now checks that what we wrote down is true — green does not mean guarded |
package/bin/deel.js CHANGED
@@ -15,6 +15,7 @@ import { runSessions } from '../src/agent/sessionui.js';
15
15
  import { acp } from '../src/acp/serve.js';
16
16
  import { runCompletion } from '../src/completion.js';
17
17
  import { runReset } from '../src/reset.js';
18
+ import { 마크다운, 읽는갈래 } from '../src/tools/doc2md.js';
18
19
  import { 언어잡기, 언어 } from '../src/i18n/index.js';
19
20
  import { load as 설정읽기 } from '../src/config.js';
20
21
 
@@ -112,6 +113,47 @@ function runAudit() {
112
113
  * 운영팀은 감사기록 사양을 보고 수집 규칙을 짠다. zip 을 통째로 만들지 않고
113
114
  * 그 두 장만 필요할 때가 실제로 더 잦다 — 심사 양식에 첨부하는 자리다.
114
115
  */
116
+ /*
117
+ * `deel doc2md <파일> [--out 이름.md]` — 문서를 마크다운으로.
118
+ *
119
+ * 읽는 일은 이미 있는 세 읽개가 그대로 한다(docs.js · xlsx.js · pdf.js).
120
+ * 여기서 새로 들이는 것은 없다 — 의존성 0개는 그대로다.
121
+ *
122
+ * `--out` 이 없으면 표준출력으로 낸다. 파이프에 바로 물리는 자리라
123
+ * 여기서는 say() 를 안 쓴다 (completion 과 같은 규칙이다).
124
+ */
125
+ function runDoc2md(args, flags) {
126
+ const 파일 = args[0];
127
+ if (!파일) {
128
+ say(` ${mark.warn} 바꿀 파일을 주세요: ${c.white('deel doc2md 보고서.pptx')}`);
129
+ say(` ${c.gray('직접 읽는 것:')} ${읽는갈래.join(c.gray(' · '))}`);
130
+ return 1;
131
+ }
132
+ const r = 마크다운(파일);
133
+ if (!r.ok) {
134
+ say('');
135
+ for (const 줄 of String(r.error).split('\n')) say(` ${mark.warn} ${줄}`);
136
+ say('');
137
+ return 1;
138
+ }
139
+ // 못 읽은 쪽·자른 자리는 **반드시** 말한다. 조용히 자르면 받은 사람은
140
+ // 그게 문서 전부인 줄 알고 그 위에 판단을 쌓는다.
141
+ if (flags.out) {
142
+ const 자리 = String(flags.out);
143
+ writeFileSync(자리, r.md, 'utf8');
144
+ say('');
145
+ say(` ${mark.ok} ${c.bold(자리)} ${c.gray(r.summary)}`);
146
+ for (const 줄 of r.말 ?? []) say(` ${c.gray(`· ${줄}`)}`);
147
+ say('');
148
+ return 0;
149
+ }
150
+ // 글은 표준출력, 말은 표준오류로 나눈다. 그래야 `> 보고서.md` 로 받아도
151
+ // 「몇 자에서 잘랐습니다」 가 파일 안에 섞여 들어가지 않는다.
152
+ process.stdout.write(`${r.md}\n`);
153
+ for (const 줄 of r.말 ?? []) process.stderr.write(`${줄}\n`);
154
+ return 0;
155
+ }
156
+
115
157
  function runSbom(flags) {
116
158
  const a = audit();
117
159
  const at = new Date();
@@ -223,7 +265,7 @@ function help() {
223
265
  say('');
224
266
  say(` ${c.gray('--root <폴더>')} 작업 범위. 기본은 지금 폴더`);
225
267
  say(` ${c.gray('--mode <모드>')} auto(기본) / confirm / strict`);
226
- say(` ${c.gray('--work <모드>')} auto(기본·종합) / code / plan / architect / debug / ask / orchestrator`);
268
+ say(` ${c.gray('--work <모드>')} auto(기본·종합) / code / plan / architect / debug / inspect / ask / orchestrator`);
227
269
  say(` ${c.gray('--level <수준>')} 쉬움(기본) / 개발자`);
228
270
  say(` ${c.gray('--ctx <길이>')} 컨텍스트 길이 직접 지정 (655360 · 640k · 128k). 없으면 서버에 맞춤`);
229
271
  say(` ${c.gray('--max-tokens <길이>')} 한 번에 받을 답 길이 상한 (32k). 큰 파일이 잘리면 올린다 — /out 과 같은 값`);
@@ -374,6 +416,8 @@ async function main() {
374
416
  return runPack(flags);
375
417
  case 'audit':
376
418
  return runAudit();
419
+ case 'doc2md':
420
+ return runDoc2md(args, flags);
377
421
  case 'sbom':
378
422
  return runSbom(flags);
379
423
  case 'scan':
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deel-local-cli",
3
- "version": "1.15.1",
3
+ "version": "1.16.0",
4
4
  "description": "A coding-agent CLI that runs on local models and private gateways. Zero dependencies, Node 20+.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/agent/asks.js CHANGED
@@ -93,6 +93,59 @@ const 시킴꼴 = /(줘|주세요|주라|해라|하라|하자|해다오|할래|
93
93
  // 줄머리 표. 번호(1. 2))·글머리(- * ·)·동그라미 숫자.
94
94
  const 줄머리 = /^\s*(?:(\d{1,2})\s*[.)]|[-*·▪️•]|[①-⑮])\s+/;
95
95
 
96
+ /*
97
+ * ── 시킨 것이 아니라 **조건**인 줄 ─────────────────────────────────────
98
+ *
99
+ * 실제로 이렇게 틀렸다. 사람이 친 말은 이랬다 —
100
+ *
101
+ * 이 기능을 분석해서 … 부분을 찾아줘.
102
+ * 각 문제마다 관련 파일과 함수, 발생 조건, 원인을 설명해줘.
103
+ * 코드는 수정하지 마.
104
+ * 최대 5개까지만 찾아줘.
105
+ *
106
+ * 네 줄 중 셋이 시킴꼴이라 **항목 셋**으로 잡혔고, 마지막 줄이 자국에 안
107
+ * 닿아서 이런 말이 나갔다 —
108
+ *
109
+ * 「시킨 것 중 1가지에 아직 손도 안 댔습니다: 3. 최대 5개까지만 찾아줘.
110
+ * 지금 하세요.」
111
+ *
112
+ * 그런데 「최대 5개까지만」 은 **할 일이 아니라 1번에 붙은 조건**이다. 이미
113
+ * 지킨 것을 안 했다고 말한 것이고, 이 파일 맨 위에 「제일 나쁘다」 고 적어
114
+ * 둔 바로 그 짓이다 — 한 번 이러면 그 뒤로 이 검사를 아무도 안 믿는다.
115
+ *
116
+ * ── 어떻게 가르나 ───────────────────────────────────────────────────────
117
+ *
118
+ * 낱말로 가른다. 그 줄에서 **알맹이를 다 빼고 나면 무엇이 남는가.** 수와
119
+ * 한계말과 「찾아줘」 같은 맨 동사밖에 안 남으면, 그 줄은 무엇을 하라는
120
+ * 말이 아니라 얼마나 하라는 말이다.
121
+ *
122
+ * 이렇게 하면 진짜 할 일은 안 걸린다 — 「테스트 최대 10개 만들어줘」 에는
123
+ * '테스트' 가 남아서 조건이 아니다. 가르는 자리를 수가 아니라 **남는 알맹이**
124
+ * 에 두었기 때문이다.
125
+ */
126
+ const 한계말 = new Set([
127
+ '최대', '최소', '많아야', '적어도', '이내', '이하', '이상', '까지', '정도', '넘게',
128
+ '최대한', '이상은', '개', '가지', '건', '줄', '짧게', '간단히', '간결하게', '되도록',
129
+ 'max', 'min', 'most', 'least', 'only', 'up',
130
+ ]);
131
+ // 무엇을 하라는 말이 아니라 그냥 「내놔라」 인 동사들. 이것만 남으면 알맹이가 없다.
132
+ const 맨동사 = new Set([
133
+ '찾아줘', '찾아', '찾기', '골라줘', '골라', '뽑아줘', '뽑아', '적어줘', '적어',
134
+ '알려줘', '보여줘', '제시해', '제시', '내줘', '달라', '말해줘', '써줘',
135
+ ]);
136
+ // 「…하지 마」 는 무슨 일이 있어도 할 일이 아니다. 안 하는 것이 다 한 것이다.
137
+ const 하지마 = /(?:하지|치지|시키지|되지|넣지|쓰지|고치지|바꾸지|건드리지|만지지)\s*(?:는)?\s*(?:마|말)/;
138
+ const 하지마En = /\b(?:do not|don'?t|never|without)\b/i;
139
+
140
+ /** 이 줄이 할 일이 아니라 그 일에 붙은 조건인가. */
141
+ export function 조건줄인가(글) {
142
+ const s = String(글 ?? '');
143
+ if (하지마.test(s) || 하지마En.test(s)) return true;
144
+ const 낱말 = 알맹이낱말(s);
145
+ if (!낱말.length) return false; // 잴 것이 없으면 안 건다 — 빠진것 이 따로 거른다
146
+ return 낱말.every((w) => 한계말.has(w) || 맨동사.has(w) || /^\d/.test(w));
147
+ }
148
+
96
149
  /**
97
150
  * 시킨 것을 항목으로 쪼갠다.
98
151
  *
@@ -100,6 +153,9 @@ const 줄머리 = /^\s*(?:(\d{1,2})\s*[.)]|[-*·▪️•]|[①-⑮])\s+/;
100
153
  * 문장 하나를 쉼표로 더 쪼개지는 않는다. 쪼갤수록 없는 항목이 생기고,
101
154
  * 없는 항목을 "안 했다" 고 말하는 것이 제일 나쁘다.
102
155
  *
156
+ * 조건인 줄은 항목에서 뺀다(조건줄인가). 개수 제한과 금지는 **지켰을 때
157
+ * 자국이 안 남는** 종류라, 그대로 두면 지킨 것이 안 한 것으로 잡힌다.
158
+ *
103
159
  * @returns {Array<{번호:number, 글:string}>}
104
160
  */
105
161
  export function 요청항목들(요청) {
@@ -111,12 +167,12 @@ export function 요청항목들(요청) {
111
167
  const m = 줄머리.exec(줄);
112
168
  if (!m) continue;
113
169
  const 글 = 줄.slice(m[0].length).trim();
114
- if (글) 표붙은것.push(글);
170
+ if (글 && !조건줄인가(글)) 표붙은것.push(글);
115
171
  }
116
172
  if (표붙은것.length >= 2) return 표붙은것.map((글, i) => ({ 번호: i + 1, 글 }));
117
173
 
118
174
  // 2) 표는 없지만 줄마다 시키는 꼴
119
- const 시킨줄 = 줄들.map((l) => l.trim()).filter((l) => l && 시킴꼴.test(l));
175
+ const 시킨줄 = 줄들.map((l) => l.trim()).filter((l) => l && 시킴꼴.test(l) && !조건줄인가(l));
120
176
  if (시킨줄.length >= 2) return 시킨줄.map((글, i) => ({ 번호: i + 1, 글 }));
121
177
 
122
178
  return [];
@@ -33,6 +33,18 @@ const 걸음규칙 = {
33
33
  ask: { 배수: 0.25, 최소: 4, 최대: 12 },
34
34
  plan: { 배수: 0.6, 최소: 8, 최대: 40 },
35
35
  architect: { 배수: 0.7, 최소: 8, 최대: 48 },
36
+ /*
37
+ * 점검은 **묻기보다 훨씬 넉넉해야** 한다.
38
+ *
39
+ * 이 값이 왜 여기 적혀 있는지가 중요하다. 같은 일이 묻기(ask)로 갔을 때
40
+ * 걸음이 열두 번에서 멈췄다 — 배수 0.25 에 최대 12 라, 창이 200k 여도 12 다.
41
+ * 파일 열 개를 훑어야 하는 점검에 열두 걸음이면 절반쯤 보고 답을 낸다.
42
+ * 그런데 화면에는 「다 봤다」 와 구별되는 표시가 아무것도 안 뜬다.
43
+ *
44
+ * 설계보다도 넉넉히 준다. 설계는 몇 파일의 관계를 보면 되지만 점검은
45
+ * **다 뒤져야** 한다 — 안 본 자리에 있는 결함은 없는 것과 같다.
46
+ */
47
+ inspect: { 배수: 1.2, 최소: 16, 최대: 120 },
36
48
  auto: { 배수: 1.5, 최소: 16, 최대: 200 },
37
49
  code: { 배수: 1.5, 최소: 16, 최대: 200 },
38
50
  debug: { 배수: 2, 최소: 16, 최대: 250 },
@@ -101,6 +113,27 @@ export function 웹글자수(ctx) {
101
113
  return 사이(k(ctx) * 400, 4000, 120000);
102
114
  }
103
115
 
116
+ /**
117
+ * 도구 하나가 한 번에 돌려줄 글자 수.
118
+ *
119
+ * ── 여기만 고정 숫자로 남아 있었다 ──────────────────────────────────────
120
+ *
121
+ * 이 파일 맨 위에 「고정한 것 자체가 틀린 것」 이라고 적어 놓고, 정작 도구
122
+ * 결과 상한만 30,000자로 박혀 있었다(tools/index.js 의 MAX_OUT). 그쪽 주석은
123
+ * 「모델에 맞춰 정한다」 고 말하고 있었으므로, 글과 코드가 서로 다른 말을
124
+ * 하는 자리이기도 했다.
125
+ *
126
+ * 8k 짜리 모델에 30,000자면 한 번으로 창이 넘친다. 넘치면 접히고, 접히면
127
+ * 앞엣말을 잊는다 — 화면에는 아무 표시가 없고, 사람 눈에는 그냥 "모델이
128
+ * 멍청해졌다" 로 보인다. 이 파일이 없애려던 고장이 바로 그것이다.
129
+ *
130
+ * 큰 창에서는 여태와 같은 값에 선다(30,000). 그 자리는 문제가 없었고,
131
+ * 문제 없던 자리를 같이 바꾸면 무엇이 좋아졌는지 못 가린다.
132
+ */
133
+ export function 한번에낼글자수(ctx) {
134
+ return 사이(k(ctx) * 160, 2000, 30000);
135
+ }
136
+
104
137
  /**
105
138
  * 하위 작업이 돌 걸음 수.
106
139
  *
@@ -180,6 +180,75 @@ export function 가벼운가(글) {
180
180
  return true;
181
181
  }
182
182
 
183
+ /*
184
+ * ── 반대쪽 문: **깊이 봐야 하는 말** ────────────────────────────────────
185
+ *
186
+ * 위 `가벼운가` 는 강도를 내리는 문이다. 이건 그 문을 **잠그는** 자리다.
187
+ *
188
+ * 왜 필요한가 — 모드가 강도를 낮추는 자리가 있다(modes.js 의 묻기: low).
189
+ * 그 값이 옳은 자리는 "이 함수 뭐야?" 같은 한 줄짜리다. 그런데 무엇이
190
+ * 묻기 모드로 가는지는 낱말이 정한다(route.js). "…설명해줘" 한 마디면
191
+ * 간다. 그래서 이런 것도 같이 갔다 —
192
+ *
193
+ * 「이 프로젝트의 공동작업 기능을 분석해서 동시 편집 시 데이터 손실,
194
+ * 덮어쓰기, 충돌 또는 상태 불일치가 발생할 가능성이 있는 부분을 찾아줘.
195
+ * 실제 코드 근거가 있는 문제만 제시하고 각 문제마다 관련 파일과 함수,
196
+ * 발생 조건, 원인을 설명해줘. 코드는 수정하지 마. 최대 5개까지만 찾아줘.」
197
+ *
198
+ * 파일 열 개를 읽고 경합 조건을 짚어야 하는 일인데, 「짧게 답하라」 는 글과
199
+ * `low` 를 달고 돌았다. 재 보면 이렇다 — 같은 프로젝트, 같은 질문 —
200
+ *
201
+ * 생각 블록: 이쪽 0자 · 깊게 본 쪽 165,000자
202
+ *
203
+ * 값이 5분의 1이면 싼 것이지만, **답이 다르면 싼 게 아니다.**
204
+ *
205
+ * ── 무엇으로 가르나 ─────────────────────────────────────────────────────
206
+ *
207
+ * 시킨 말의 **모양**으로 가른다. 줄이 여럿이면 시킨 것이 여럿이고, 길면
208
+ * 조건이 붙어 있고, 살펴보라는 낱말이 있으면 읽어야 답이 나온다. 셋 중
209
+ * 하나면 충분하다 — 틀리는 방향이 「조금 더 생각한다」 쪽이라 값이 싸다.
210
+ */
211
+ const 살펴보라는말 = /분석|검토|감사|점검|진단|살펴|파헤|훑어|짚어|찾아|가능성|위험|취약|누락|경합|충돌|불일치|원인|근거|비교|추적|audit|review|analy[sz]e|investigat|assess|diagnos|vulnerab|race\s*condition|compare|trace/i;
212
+
213
+ /**
214
+ * 얕게 보면 안 되는 말인가.
215
+ *
216
+ * `가벼운가` 의 반대말이 아니다. 둘 다 아닌 말이 대부분이고, 그때는 아무것도
217
+ * 안 움직인다 — 사람이 정한 천장 그대로 간다.
218
+ */
219
+ export function 무거운가(글) {
220
+ const s = String(글 ?? '').trim();
221
+ if (!s || 인사인가(s)) return false;
222
+ // 줄이 여럿이면 시킨 것이 여럿이다. 빈 줄은 안 센다 — 붙여넣기에 흔하다.
223
+ if (s.split(/\r?\n/).filter((l) => l.trim()).length >= 3) return true;
224
+ // 길면 조건이 붙어 있다. 조건이 붙은 말을 얕게 읽으면 조건부터 떨어진다.
225
+ if (s.length >= 200) return true;
226
+ // 짧아도 살펴보라는 말이면 읽어야 답이 나온다. 한 마디짜리는 뺀다.
227
+ // 스무 자다. 한국어는 빽빽해서 「공동작업 쪽에 경합 조건 있는지 좀 봐줘」 가 스물둘이다 —
228
+ // 마흔으로 잡으면 진짜 일이 가벼운 쪽으로 떨어진다. 틀리는 값이 한쪽은
229
+ // 「조금 더 생각한다」 이고 다른 쪽은 「얕게 답한다」 라, 싼 쪽으로 기운다.
230
+ return s.length >= 20 && 살펴보라는말.test(s);
231
+ }
232
+
233
+ /**
234
+ * 이번 턴의 **천장** — 모드가 정한 값과 사람이 정한 값 중에서 고른다.
235
+ *
236
+ * 모드가 더 세게 보라고 하면 그대로 따른다(설계·디버그·계획·점검). 그건
237
+ * 「이 일은 원래 깊다」 는 말이고, 사람이 손대지 않은 기본값보다 그 말이 낫다.
238
+ *
239
+ * 모드가 **낮출 때**만 한 번 더 본다. 낮추는 것이 맞는 자리는 한 줄짜리
240
+ * 물음이고, 시킨 것이 여럿이거나 살펴보라는 말이면 그 자리가 아니다.
241
+ * 그때는 사람이 정한 값으로 돌아간다 — 모드가 낮추기 전의 자리다.
242
+ */
243
+ export function 천장고르기(모드think, 세션think, 요청 = '') {
244
+ const 세션 = LEVELS.includes(세션think) ? 세션think : 'medium';
245
+ // 끈 것은 모드가 못 켠다. `off` 는 기본값이 아니라 사람이 고른 값이다.
246
+ if (세션 === 'off') return 'off';
247
+ if (!모드think || !LEVELS.includes(모드think)) return 세션;
248
+ if (LEVELS.indexOf(모드think) >= LEVELS.indexOf(세션)) return 모드think;
249
+ return 무거운가(요청) ? 세션 : 모드think;
250
+ }
251
+
183
252
  /**
184
253
  * 이번 턴의 기준 강도.
185
254
  *