deel-local-cli 1.5.4 → 1.5.6

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
@@ -18,7 +18,7 @@ Zero dependencies · Node 20+ · Exactly one place your source can go
18
18
 
19
19
  [![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)
20
20
  [![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)
21
- [![tests](https://img.shields.io/badge/tests-3%2C614%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
21
+ [![tests](https://img.shields.io/badge/tests-3%2C785%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/en/develop.md)
22
22
 
23
23
  [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
24
24
  [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
@@ -100,7 +100,7 @@ This page is the **summary**. Each section links to the detail behind it.
100
100
  | [Speed and spend](docs/en/tuning.md) | Per-stage effort · the prefix cache · context length |
101
101
  | [Safety and corporate review](docs/en/safety.md) | Undo · working scope · audit log · the review package |
102
102
  | [Configuration](docs/en/config.md) · [Development](docs/en/develop.md) | Env vars · run flags · running the tests · folder layout |
103
- | [Release notes](docs/en/releases.md) | [1.5.4](docs/en/releases.md#154) · [1.5.3](docs/en/releases.md#153) · [1.5.2](docs/en/releases.md#152) · [1.5.1](docs/en/releases.md#151) · [1.5.0](docs/en/releases.md#150) · [1.4.3](docs/en/releases.md#143) · [1.4.2](docs/en/releases.md#142) · [1.4.1](docs/en/releases.md#141) · [1.4.0](docs/en/releases.md#140) · [1.3.0](docs/en/releases.md#130) · [1.2.0](docs/en/releases.md#120) |
103
+ | [Release notes](docs/en/releases.md) | [1.5.6](docs/en/releases.md#156) · [1.5.5](docs/en/releases.md#155) · [1.5.4](docs/en/releases.md#154) · [1.5.3](docs/en/releases.md#153) · [1.5.2](docs/en/releases.md#152) · [1.5.1](docs/en/releases.md#151) · [1.5.0](docs/en/releases.md#150) · [1.4.3](docs/en/releases.md#143) · [1.4.2](docs/en/releases.md#142) · [1.4.1](docs/en/releases.md#141) · [1.4.0](docs/en/releases.md#140) · [1.3.0](docs/en/releases.md#130) · [1.2.0](docs/en/releases.md#120) |
104
104
 
105
105
  ---
106
106
 
@@ -363,7 +363,8 @@ Names follow Claude Code / Codex conventions.
363
363
  | Command | What it does |
364
364
  |---|---|
365
365
  | `/help` | Command list |
366
- | `/lang [ko\|en]` | Screen language. Falls back to Korean for anything not translated yet |
366
+ | `/lang [ko\|en] [prompt lang]` | Screen language. **The prompt language is a separate axis** — `/lang en ko` instructs the model in Korean while answering you in English. Korean screen + English prompt cuts the 8k fixed share by 23% |
367
+ | `/keys` | Press keys to see what your terminal actually sends — for when new lines will not work |
367
368
  | `/bell [on\|off]` | Ring and set the window title when a turn ends, or when deel needs an answer |
368
369
  | `/consult <profile> <question>` | Ask a second model one question. Your current model stays put |
369
370
  | `/export` | This conversation as a **one-page HTML report** — asked, changed, verified. Self-contained, opens on any network |
@@ -496,11 +497,13 @@ Names and arguments match Claude Code, so skills written for that convention wor
496
497
  | `Write` | Write / overwrite a file (**several at once via the `files` array**) |
497
498
  | `Append` | Append to the end of a file — **how large files get written in pieces** |
498
499
  | `Edit` | Replace an exact string (`replace_all`; **several sites at once via the `edits` array**) |
500
+ | `Move` | Move / rename files and folders — **how you restructure** (`moves` array; covered by undo) |
499
501
  | `Glob` | Find files by name pattern |
500
502
  | `Grep` | Regex search file contents |
501
503
  | `Bash` | Run a command (**`background: true` for anything that does not finish**) |
502
504
  | `Skill` | Expand a skill body (shown to the model only when skills exist) |
503
505
  | `WebFetch` | Read a web page (read-only; hidden under `--offline`) |
506
+ | `Ask` | **Ask you back** at a fork — offers the choices, takes a single number |
504
507
  | `Recall` | Search **past conversations** — the model digs up "that thing last time" itself |
505
508
  | `Remember` | One line that outlives the session — known from the start next time |
506
509
  | `TodoWrite` | Checklist — breaks long work into steps and shows progress |
@@ -511,8 +514,8 @@ Names and arguments match Claude Code, so skills written for that convention wor
511
514
  | `Def` | **Where a name is defined** — only shown when a language server is installed |
512
515
  | `Refs` | **Every place a name is used** — only shown when a language server is installed |
513
516
 
514
- Seven tools here are not in Claude Code — `Append`, `Recall`, `Remember`, `Outline`,
515
- `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
517
+ Nine tools here are not in Claude Code — `Append`, `Move`, `Ask`, `Recall`, `Remember`,
518
+ `Outline`, `Verify`, `Task`, `Jobs`. Each tool costs 150-400 tokens of schema on **every request**,
516
519
  so a test stops you every time the list grows (`test/loop.test.js`). The last four earned
517
520
  their cost; here is why.
518
521
 
@@ -929,7 +932,7 @@ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder tak
929
932
  ## Development
930
933
 
931
934
  ```bash
932
- npm test Full suite (3,681 checks)
935
+ npm test Full suite (3,785 checks)
933
936
  npm run coverage Which lines the tests actually execute
934
937
  npm run verify Import + network checks only
935
938
  npm run bench Edit success rate
@@ -981,7 +984,9 @@ so one run tells you everything.
981
984
 
982
985
  | Version | What changed |
983
986
  |---|---|
984
- | **[1.5.4](docs/en/releases.md#154)** | The office fills up and a day passes in it "only one person" was a bug, not a design |
987
+ | **[1.5.6](docs/en/releases.md#156)** | It asks when stuckinstead of writing a question and ending the turn |
988
+ | **[1.5.5](docs/en/releases.md#155)** | A plan fills the room — "only one person" was a bug, not a design |
989
+ | [1.5.4](docs/en/releases.md#154) | A day passes in the room — morning to night, a wall clock, a cold coffee |
985
990
  | [1.5.3](docs/en/releases.md#153) | Three things a Mac user tripped over — newline hint, office stays put, one `/motion` |
986
991
  | [1.5.2](docs/en/releases.md#152) | Everything else the adversarial reviews turned up — where the screen was lying |
987
992
  | [1.5.1](docs/en/releases.md#151) | Two of the things 1.5.0 added shipped broken. This fixes them |
package/README.md CHANGED
@@ -18,7 +18,7 @@
18
18
 
19
19
  [![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)
20
20
  [![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)
21
- [![tests](https://img.shields.io/badge/tests-3%2C614%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/ko/develop.md)
21
+ [![tests](https://img.shields.io/badge/tests-3%2C785%20passing-1a7f37?logo=checkmarx&logoColor=white)](docs/ko/develop.md)
22
22
 
23
23
  [![dependencies](https://img.shields.io/badge/dependencies-0-1a7f37)](https://www.npmjs.com/package/deel-local-cli?activeTab=dependencies)
24
24
  [![ESM](https://img.shields.io/badge/ESM-Node%2020%2B-5FA04E?logo=javascript&logoColor=white)](package.json)
@@ -101,7 +101,7 @@
101
101
  | [속도와 씀씀이](docs/ko/tuning.md) | 단계별 추론 강도 · 프리픽스 캐시 · 컨텍스트 길이 |
102
102
  | [안전망과 사내 반입](docs/ko/safety.md) | 되돌리기 · 작업 범위 · 감사기록 · 심사 서류 |
103
103
  | [설정](docs/ko/config.md) · [개발](docs/ko/develop.md) | 환경변수 · 실행 옵션 · 검사 돌리기 · 폴더 구조 |
104
- | [릴리스 노트](docs/ko/releases.md) | [1.5.4](docs/ko/releases.md#154) · [1.5.3](docs/ko/releases.md#153) · [1.5.2](docs/ko/releases.md#152) · [1.5.1](docs/ko/releases.md#151) · [1.5.0](docs/ko/releases.md#150) · [1.4.3](docs/ko/releases.md#143) · [1.4.2](docs/ko/releases.md#142) · [1.4.1](docs/ko/releases.md#141) · [1.4.0](docs/ko/releases.md#140) · [1.3.0](docs/ko/releases.md#130) · [1.2.0](docs/ko/releases.md#120) |
104
+ | [릴리스 노트](docs/ko/releases.md) | [1.5.6](docs/ko/releases.md#156) · [1.5.5](docs/ko/releases.md#155) · [1.5.4](docs/ko/releases.md#154) · [1.5.3](docs/ko/releases.md#153) · [1.5.2](docs/ko/releases.md#152) · [1.5.1](docs/ko/releases.md#151) · [1.5.0](docs/ko/releases.md#150) · [1.4.3](docs/ko/releases.md#143) · [1.4.2](docs/ko/releases.md#142) · [1.4.1](docs/ko/releases.md#141) · [1.4.0](docs/ko/releases.md#140) · [1.3.0](docs/ko/releases.md#130) · [1.2.0](docs/ko/releases.md#120) |
105
105
 
106
106
  ---
107
107
 
@@ -342,7 +342,8 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
342
342
  |---|---|
343
343
  | `/help` | 명령 목록 |
344
344
  | `/bell [on|off]` | 다 됐을 때·물어볼 때 종소리와 창 제목 |
345
- | `/lang [ko|en]` | 화면 영어권 사람이 때. 모델이 읽는 글도 같이 따라간다(영어 쪽이 1,400토큰 먹는다). 코드는 그대로 |
345
+ | `/lang [ko|en] [시킬말]` | 화면 말. **시킬 말을 따로 줄 수 있다** `/lang ko en` 이면 모델에게는 영어로 시키고 답은 한국어로 받는다(8k 창에서 고정 23% 감소). 코드는 그대로 |
346
+ | `/keys` | 이 터미널이 무슨 키를 보내는지 눌러서 확인 — 줄바꿈이 안 될 때 |
346
347
  | `/consult <프로필> <질문>` | 다른 모델에게 한 번 물어보기 — 지금 쓰는 것은 안 바꿈 |
347
348
  | `/export` | 이 대화를 **보고서 한 장**(HTML)으로 — 시킨 것·바뀐 것·확인한 것. 폐쇄망에서 여는 자기완결 파일 |
348
349
  | `/lsp [on\|off]` | 언어 서버 — 무엇이 깔려 있고 `Def`·`Refs` 를 쓸 수 있는지. `off` 는 고친 뒤 진단만 끔 |
@@ -471,11 +472,13 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
471
472
  | `Write` | 파일 쓰기·덮어쓰기 (**여러 개는 `files` 배열로 한 번에**) |
472
473
  | `Append` | 파일 끝에 이어 붙이기 — **큰 파일을 나눠 쓰는 자리** |
473
474
  | `Edit` | 정확한 문자열 바꾸기 (`replace_all` · **여러 군데는 `edits` 배열로 한 번에**) |
475
+ | `Move` | 파일·폴더 옮기기·이름 바꾸기 — **구조를 바꾸는 자리** (`moves` 배열로 한 번에 · 되돌리기에 잡힘) |
474
476
  | `Glob` | 이름 패턴으로 파일 찾기 |
475
477
  | `Grep` | 내용 정규식 검색 |
476
478
  | `Bash` | 명령 실행 (**끝나지 않는 것은 `background: true`**) |
477
479
  | `Skill` | 스킬 본문 펼쳐 읽기 (스킬이 있을 때만 모델에게 보임) |
478
480
  | `WebFetch` | 웹 페이지 읽기 (읽기 전용 · `--offline` 이면 숨김) |
481
+ | `Ask` | 갈림길에서 **사람에게 되묻기** — 고를 것을 주고 숫자 하나로 받는다 |
479
482
  | `Recall` | **지난 대화**에서 찾기 — "저번에 그거" 를 모델이 스스로 뒤진다 |
480
483
  | `Remember` | 대화가 끝나도 남길 것 한 줄 — 다음에 켤 때 처음부터 안다 |
481
484
  | `TodoWrite` | 할 일 목록 — 긴 일을 쪼개서 어디까지 했는지 화면에 띄움 |
@@ -486,8 +489,8 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
486
489
  | `Def` | 이름이 **어디 정의됐는지** — 언어 서버가 깔려 있을 때만 보입니다 |
487
490
  | `Refs` | 이름을 **어디서 쓰는지** 전부 — 언어 서버가 깔려 있을 때만 보입니다 |
488
491
 
489
- Claude Code 에 없는 것은 일곱입니다 — `Append` · `Recall` · `Remember` ·
490
- `Outline` · `Verify` · `Task` · `Jobs`. 도구 하나가 스키마로 150~400토큰을 먹고
492
+ Claude Code 에 없는 것은 아홉입니다 — `Append` · `Move` · `Ask` · `Recall` ·
493
+ `Remember` · `Outline` · `Verify` · `Task` · `Jobs`. 도구 하나가 스키마로 150~400토큰을 먹고
491
494
  그게 **매 요청마다** 나가므로, 늘릴 때마다 검사에서 한 번 멈추게 해 뒀습니다
492
495
  (`test/loop.test.js`). 뒤의 넷은 그 값을 치르고도 넣을 만해서 넣은 것들이라,
493
496
  아래에 왜인지를 적어 뒀습니다.
@@ -907,7 +910,7 @@ deel sbom --only sbom # SBOM 한 장만
907
910
  ## 개발
908
911
 
909
912
  ```bash
910
- npm test 전체 검증 (3,681항목)
913
+ npm test 전체 검증 (3,785항목)
911
914
  npm run coverage 검사가 소스의 어디를 밟았는지
912
915
  npm run verify 반입·통신 검증만
913
916
  npm run bench 편집 성공률 측정
@@ -972,7 +975,9 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
972
975
 
973
976
  | 판 | 무엇이 바뀌었나 |
974
977
  |---|---|
975
- | **[1.5.4](docs/ko/releases.md#154)** | 사무실에 사람이 차고, 방에 하루가 흐른다 「한 명뿐」은 고정이 아니라 버그였다 |
978
+ | **[1.5.6](docs/ko/releases.md#156)** | 막히면 물어본다글로 묻고 턴을 끝내는 대신에 |
979
+ | **[1.5.5](docs/ko/releases.md#155)** | 계획을 세우면 방이 찬다 — 「한 명뿐」은 고정이 아니라 버그였다 |
980
+ | [1.5.4](docs/ko/releases.md#154) | 방에 하루가 흐른다 — 아침·낮·노을·밤 · 벽시계 · 식은 커피 |
976
981
  | [1.5.3](docs/ko/releases.md#153) | 맥에서 걸린 세 가지 — 줄바꿈 안내 · 사무실이 안 사라짐 · `/motion` 하나로 |
977
982
  | [1.5.2](docs/ko/releases.md#152) | 적대적 리뷰가 남긴 나머지를 전부 정리했다 — 화면이 거짓말하던 자리들 |
978
983
  | [1.5.1](docs/ko/releases.md#151) | 1.5.0 이 넣은 것 두 개가 망가진 채 나갔다. 그것을 고친다 |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deel-local-cli",
3
- "version": "1.5.4",
3
+ "version": "1.5.6",
4
4
  "description": "로컬 모델·사내 게이트웨이 전용 코딩 에이전트 CLI — 외부 의존성 0개 / Zero-dependency coding agent CLI for local LLMs and private gateways",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -15,7 +15,10 @@
15
15
  // 묻기 모드에도 준다: "저번에 이거 어떻게 했더라" 가 딱 묻기 모드의 일이다.
16
16
  // Def·Refs 도 읽기다 — 아무것도 안 바꾼다. 언어 서버가 없는 자리에서는
17
17
  // toolSchemas 가 알아서 빼므로 여기서는 갈래만 정한다.
18
- const 읽기 = ['Read', 'Outline', 'Glob', 'Grep', 'Def', 'Refs', 'WebFetch', 'Skill', 'Recall'];
18
+ // Ask **모든 모드**에 있다(읽기 갈래에 이유가 그것이다). 갈림길은
19
+ // 어느 모드에서나 생기고, 물어볼 길이 없으면 모델은 글로 "알려주세요" 하고
20
+ // 턴을 끝내 버린다 — 그러면 여태 조사한 것이 통째로 버려진다.
21
+ const 읽기 = ['Read', 'Outline', 'Glob', 'Grep', 'Def', 'Refs', 'WebFetch', 'Skill', 'Recall', 'Ask'];
19
22
  // 계획을 적는 도구. 파일을 안 건드리므로 읽기 전용 모드에서도 준다.
20
23
  //
21
24
  // Remember 도 여기 있다. 기억은 사용자의 소스를 안 건드리고 .deel/memory.md
@@ -27,7 +30,9 @@ const 계획 = ['TodoWrite', 'Remember'];
27
30
  // Write 를 주는 자리에는 반드시 같이 준다. 하나만 주면 나눠 쓸 방법이 없어진다.
28
31
  // Jobs 는 Bash 와 짝이다. Bash 를 주는 자리에는 반드시 같이 준다 —
29
32
  // background 로 띄워 놓고 읽을 길이 없으면 띄운 것이 그냥 유령이 된다.
30
- const 쓰기 = ['Write', 'Append', 'Edit', 'Bash', 'Jobs'];
33
+ // Move 구조를 바꾸는 유일한 길이다. 이게 없으면 "폴더를 역할대로 나눠 줘"
34
+ // 에 남는 길이 Bash mv 뿐인데, 그건 매번 승인을 묻고 되돌리기에 안 잡힌다.
35
+ const 쓰기 = ['Write', 'Append', 'Edit', 'Move', 'Bash', 'Jobs'];
31
36
  // 만든 것을 확인하는 도구. 아무것도 안 바꾸지만 **쓰는 모드에만** 준다 —
32
37
  // 안 만든 모드에서 확인할 것이 없고, 도구 정의로 나가는 자리만 먹는다.
33
38
  const 확인 = ['Verify'];
@@ -45,7 +50,7 @@ const 확인 = ['Verify'];
45
50
  */
46
51
  const 쪼개기 = ['Task'];
47
52
 
48
- import { 언어 } from '../i18n/index.js';
53
+ import { 언어, 지시말 } from '../i18n/index.js';
49
54
 
50
55
  export const MODES = {
51
56
  // 처음에는 여기서 시작한다.
@@ -347,7 +352,7 @@ export function 말(id, ctx) {
347
352
  * 아무 지시도 없는 채로 도는데, 그게 화면 빈칸보다 훨씬 나쁘다 —
348
353
  * 모드가 있는 것처럼 보이면서 실제로는 아무 일도 안 한다.
349
354
  */
350
- if (언어() === 'en') {
355
+ if (지시말() === 'en') {
351
356
  if (좁은가 && m.say짧게En) return m.say짧게En;
352
357
  if (m.sayEn) return m.sayEn;
353
358
  }
@@ -8,7 +8,7 @@ import { 매김, 급말, 값 as 급값, 지켜본것 } from './grade.js';
8
8
  import { 지문 } from './project.js';
9
9
  import { 프롬프트토막 as 기억토막 } from './memory.js';
10
10
  import { 못박기 } from './pins.js';
11
- import { 언어 } from '../i18n/index.js';
11
+ import { 언어, 지시말 } from '../i18n/index.js';
12
12
 
13
13
  // 토큰 추정 — 정확한 토크나이저 없이 대략만 센다.
14
14
  // 한글은 글자당 약 1토큰, 영문·코드는 약 4글자당 1토큰으로 본다.
@@ -29,6 +29,9 @@ const BASE_RULES = `너는 deel 다. 사용자의 작업 폴더 안에서 코드
29
29
 
30
30
  - 바로 시작한다. 없는 파일·폴더는 만든다. 그게 시킨 일의 일부다.
31
31
  - 되묻는 것은 **도구로도 못 알아낼 때뿐**이다. 정할 수 있으면 정하고 무엇으로 정했는지 말한다.
32
+ - 그래도 물어야 하면 **Ask 도구로 묻는다.** 글로 "알려주세요" 하고 끝내지 마라 — 그러면 그 턴이
33
+ 끝나서 여태 읽은 것이 다 버려지고, 사람은 아무것도 안 된 화면을 본다. Ask 는 답이 그 자리로
34
+ 돌아와 하던 일이 이어진다. 고를 것을 2~4개 같이 준다.
32
35
  - 여러 파일을 만들고 나눠 담아야 하는 일이면 그렇게 한다. 하나만 건드려 놓고 멈추지 마라.
33
36
  - 다 했으면 확인한다. 돌려 보고 안 되면 고친다. 확인 못 했으면 "확인 못 했다" 고 말한다.
34
37
 
@@ -66,6 +69,7 @@ const BASE_RULES_짧게 = `너는 deel 다. 사용자의 작업 폴더에서 코
66
69
  시킨 일을 끝까지 해낸다. 계획만 내고 멈추지 마라.
67
70
  - 바로 시작한다. 없는 파일·폴더는 만든다.
68
71
  - 도구로 알아낼 수 있으면 되묻지 말고 정한다. 무엇으로 정했는지는 말한다.
72
+ - 그래도 물어야 하면 Ask 도구로 묻는다. 글로 묻고 끝내면 턴이 끝나 여태 읽은 것이 버려진다.
69
73
  - 파일이 여럿이면 다 만든다. 하나만 하고 멈추지 마라.
70
74
  - 끝내기 전에 Verify 로 확인한다. 확인 못 했으면 "확인 못 했다" 고 말한다.
71
75
 
@@ -101,6 +105,9 @@ const BASE_RULES_EN = `You are deel, a tool that reads and edits code inside the
101
105
 
102
106
  - Start now. Create missing files and folders — that is part of the job.
103
107
  - Ask back **only when no tool can tell you**. If you can decide, decide, and say what you decided it from.
108
+ - When you truly must ask, **ask with the Ask tool.** Do not write "let me know what you want" and stop — that ends
109
+ the turn, everything you read is thrown away, and the user sees a screen where nothing happened. An Ask answer
110
+ comes straight back to where you are, so the work carries on. Give 2–4 options to pick from.
104
111
  - If the job needs several files, make them all. Do not touch one and stop.
105
112
  - When you are done, check. Run it, and fix it if it fails. If you could not check, say "I could not verify this."
106
113
 
@@ -131,6 +138,7 @@ const BASE_RULES_짧게_EN = `You are deel. You read and edit code in the user's
131
138
  Finish the job. Do not stop at a plan.
132
139
  - Start now. Create missing files and folders.
133
140
  - If a tool can tell you, decide instead of asking. Say what you decided it from.
141
+ - If you must ask, use the Ask tool. Asking in prose ends the turn and throws away what you read.
134
142
  - If there are several files, make them all. Do not do one and stop.
135
143
  - Verify before you finish. If you could not verify, say so.
136
144
 
@@ -150,8 +158,22 @@ Finish the job. Do not stop at a plan.
150
158
  */
151
159
  function 기본규칙(ctx) {
152
160
  const 짧게 = Number(ctx) > 0 && Number(ctx) < 24000;
153
- if (언어() === 'en') return 짧게 ? BASE_RULES_짧게_EN : BASE_RULES_EN;
154
- return 짧게 ? BASE_RULES_짧게 : BASE_RULES;
161
+ /*
162
+ * 시키는 말은 지시말() 정한다 — 화면 말과 다른 축이다(i18n/index.js).
163
+ *
164
+ * 그래서 "영어로 시키고 한국어로 받기" 가 된다. 규칙 글은 영어판을 쓰되,
165
+ * **답하는 말**만 다시 못 박는다. 안 박으면 영어 규칙 안의
166
+ * "Answer the user in English" 가 그대로 먹어서, 한국 사람이 영어 답을
167
+ * 받는다 — 값을 아끼려다 읽을 수 없는 답을 받는 셈이다.
168
+ */
169
+ const 시키는말 = 지시말();
170
+ const 글 = 시키는말 === 'en'
171
+ ? (짧게 ? BASE_RULES_짧게_EN : BASE_RULES_EN)
172
+ : (짧게 ? BASE_RULES_짧게 : BASE_RULES);
173
+ if (시키는말 === 언어()) return 글;
174
+ return `${글}\n\n${언어() === 'ko'
175
+ ? '**답은 한국어로 해라.** 위 규칙이 영어로 적혀 있어도 사용자에게 하는 말은 한국어다.'
176
+ : '**Answer in English.** The rules above are in Korean, but what you say to the user is English.'}`;
155
177
  }
156
178
 
157
179
  export class Session {
package/src/commands.js CHANGED
@@ -8,7 +8,7 @@ import { allowEndpoint } from './safety/network.js';
8
8
  import { pick } from './ui/prompt.js';
9
9
  import { load, save, resolveKey, upsert } from './config.js';
10
10
  import { 종, 알릴만한초 } from './ui/notify.js';
11
- import { 말, 언어, 언어정하기, 언어고르기, 옮긴만큼 } from './i18n/index.js';
11
+ import { 말, 언어, 언어정하기, 언어고르기, 옮긴만큼, 지시말, 지시말정하기, 지시말따로정했나 } from './i18n/index.js';
12
12
  import { 프로필찾기, 쓸수있나, 연결만들기, 알릴말, 목록보기 } from './agent/models.js';
13
13
  import { allowTemporarily } from './safety/network.js';
14
14
  import { chat } from './backend/adapter.js';
@@ -110,6 +110,7 @@ const 명령들 = {
110
110
  memory: { arg: true },
111
111
  level: { arg: true },
112
112
  bell: { arg: true },
113
+ keys: { arg: false },
113
114
  motion: { arg: true },
114
115
  consult: { arg: true },
115
116
  lang: { arg: true },
@@ -312,16 +313,34 @@ export async function handle(line, session, ctx) {
312
313
  }
313
314
 
314
315
  case 'lang': {
315
- const 값 = String(arg ?? '').trim();
316
+ /*
317
+ * 두 축이다 — 화면 말, 그리고 **모델에게 시키는 말**.
318
+ *
319
+ * /lang en 둘 다 영어
320
+ * /lang ko en 화면은 한국어, 시키는 말만 영어 ← "영어로 시키고 한국어로 받기"
321
+ * /lang ko auto 다시 하나로 (시키는 말이 화면 말을 따라간다)
322
+ *
323
+ * 굳이 나눈 이유는 값이다. 한글은 글자당 약 1토큰이고 영문은 약 3.6자당
324
+ * 1토큰이라(session.js 의 estimateTokens), 시키는 말만 영어로 돌려도
325
+ * 고정 몫이 눈에 띄게 준다. 작은 창일수록 그 차이가 크다.
326
+ * 답은 그대로 한국어로 온다 — 기본규칙() 이 그 한 줄을 따로 못 박는다.
327
+ */
328
+ const 쪼갠것 = String(arg ?? '').trim().split(/\s+/).filter(Boolean);
329
+ const 값 = 쪼갠것[0] ?? '';
330
+ const 시킬말 = 쪼갠것[1] ?? null;
316
331
  const 이름 = (l) => 말(`lang.${l}`);
317
332
  if (!값) {
318
333
  const p = 옮긴만큼();
319
334
  say('');
320
335
  say(` ${c.gray(말('lang.now', { 이름: 이름(언어()) }))}`);
336
+ say(` ${c.gray(말('lang.promptNow', {
337
+ 이름: 지시말따로정했나() ? 이름(지시말()) : 말('lang.follows'),
338
+ }))}`);
321
339
  say(` ${c.gray(말('lang.progress', { 언어: 언어(), 옮김: p.옮김, 전체: p.전체 }))}`);
322
340
  if (p.남음) say(` ${c.gray(말('lang.partial'))}`);
323
341
  say(` ${c.gray(말('lang.codeStays'))}`);
324
342
  say(` ${c.gray(말('lang.howto'))} ${c.cyan('/lang ko')} ${c.gray('·')} ${c.cyan('/lang en')}`);
343
+ say(` ${c.gray(말('lang.splitHowto'))} ${c.cyan('/lang ko en')}`);
325
344
  say(` ${c.gray(말('lang.envHint'))}`);
326
345
  say('');
327
346
  return { handled: true };
@@ -331,13 +350,27 @@ export async function handle(line, session, ctx) {
331
350
  say('');
332
351
  return { handled: true };
333
352
  }
353
+ // 시킬 말을 같이 줬으면 그것부터 본다 — 화면 말만 바꿔 놓고 거절하면
354
+ // 사람이 시킨 것의 절반만 먹은 채로 끝난다.
355
+ if (시킬말 !== null && !지시말정하기(시킬말)) {
356
+ say(` ${c.gray(말('lang.unknown'))}`);
357
+ say('');
358
+ return { handled: true };
359
+ }
334
360
  언어정하기(값);
335
361
  const cfg = load();
336
362
  cfg.lang = 언어();
363
+ // 따로 안 정했으면 설정에서도 지운다. 남겨 두면 다음에 켤 때
364
+ // 「화면 말을 따라간다」 가 아니라 옛 값이 되살아난다.
365
+ if (지시말따로정했나()) cfg.promptLang = 지시말();
366
+ else delete cfg.promptLang;
337
367
  try { save(cfg); } catch { /* 못 남겨도 이번 세션에는 먹는다 */ }
338
368
  const p = 옮긴만큼();
339
369
  say('');
340
370
  say(` ${mark.ok} ${말('lang.changed', { 이름: 이름(언어()) })}`);
371
+ if (지시말() !== 언어()) {
372
+ say(` ${c.gray(말('lang.promptChanged', { 이름: 이름(지시말()), 답: 이름(언어()) }))}`);
373
+ }
341
374
  say(` ${c.gray(말('lang.progress', { 언어: 언어(), 옮김: p.옮김, 전체: p.전체 }))}`);
342
375
  if (p.남음) say(` ${c.gray(말('lang.partial'))}`);
343
376
  say('');
@@ -421,6 +454,24 @@ export async function handle(line, session, ctx) {
421
454
  return { handled: true };
422
455
  }
423
456
 
457
+ /*
458
+ * 이 터미널이 무슨 바이트를 보내나 (repl.js 의 키확인).
459
+ *
460
+ * 줄바꿈이 안 된다는 말은 여러 판째 나왔는데, 그때마다 문서에 "터미널
461
+ * 설정을 바꾸세요" 라고만 적었다. 그건 고친 것이 아니다 — 사람은 제
462
+ * 터미널에서 무엇이 오는지 볼 길이 없었다. 여기서는 짐작 대신 눌러 본다.
463
+ */
464
+ case 'keys': {
465
+ if (typeof ctx?.키확인 !== 'function') {
466
+ say('');
467
+ say(` ${c.gray('키 확인은 상자 화면에서만 됩니다 (--no-tui 나 파이프에서는 못 씁니다).')}`);
468
+ say('');
469
+ return { handled: true };
470
+ }
471
+ await ctx.키확인();
472
+ return { handled: true };
473
+ }
474
+
424
475
  case 'bell': {
425
476
  const cfg = load();
426
477
  const 켜는말 = ['on', '켬', '켜', '켜기', 'y', 'yes', 'ㅇ'];
package/src/i18n/en.js CHANGED
@@ -80,12 +80,13 @@ export const en = {
80
80
  'cmd.memory.arg': '[forget <n>|clear|<text to remember>]',
81
81
  'cmd.level.desc': 'user level (beginner/developer)',
82
82
  'cmd.level.arg': '[level]',
83
+ 'cmd.keys.desc': 'press keys to see what your terminal actually sends — when new lines will not work',
83
84
  'cmd.bell.desc': 'bell and window title when a turn ends or deel needs you (on/off)',
84
85
  'cmd.bell.arg': '[on|off]',
85
86
  'cmd.motion.desc': 'what moves while deel works — knight, animals, office',
86
87
  'cmd.motion.arg': '[knight|animal|office|default|off]',
87
88
  'cmd.lang.desc': 'screen language — 한국어 / English',
88
- 'cmd.lang.arg': '[ko|en]',
89
+ 'cmd.lang.arg': '[ko|en] [prompt lang]',
89
90
  'cmd.init.desc': 'create a DEEL.md rules file',
90
91
  'cmd.exit.desc': 'quit',
91
92
  'cmd.quit.desc': 'quit',
@@ -132,6 +133,7 @@ export const en = {
132
133
  'head.shiftTabHint': ' changes it · ',
133
134
  'head.tabHint': 'Tab completes the / command you are typing',
134
135
  'head.newlineHint': 'New line: end a line with space + ` then Enter — works everywhere (Alt+Enter only if your terminal sends it)',
136
+ 'head.star': 'Enjoying this tool? Star us on GitHub —',
135
137
  'head.thisPC': 'found',
136
138
  'head.skills': 'skills {n}',
137
139
  'head.commands': 'commands {n}',
@@ -208,7 +210,11 @@ export const en = {
208
210
  'lang.unknown': 'Please say ko or en.',
209
211
  'lang.progress': '{언어}: {옮김} of {전체} strings translated. The rest come through in Korean.',
210
212
  'lang.partial': 'Not everything is translated yet — untranslated lines show Korean rather than a blank.',
211
- 'lang.codeStays': 'What the model reads follows too English costs about 1,400 fewer tokens per request. Code, tool names and argument names stay as they are.',
213
+ 'lang.codeStays': 'Unless you set it separately, what the model reads follows the screen. Code, tool names and argument names stay as they are.',
214
+ 'lang.promptNow': 'The model is instructed in {이름}.',
215
+ 'lang.follows': 'the same as the screen',
216
+ 'lang.splitHowto': 'To be instructed in English but answered in Korean:',
217
+ 'lang.promptChanged': 'Instructions in {이름}, answers in {답} — 23% off the fixed share on an 8k window.',
212
218
  'lang.envHint': 'To start in English every time, set DEEL_LANG=en',
213
219
 
214
220
  'cmd.lsp.desc': 'language servers — what is installed, and whether Def/Refs are available',
@@ -273,6 +279,13 @@ export const en = {
273
279
  'level.developer.name': 'developer',
274
280
  'level.developer.hint': 'everything by hand',
275
281
 
282
+ // ── Ask box (the Ask tool) ────────────────────────────────────────────
283
+ 'ask.title': 'A question for you',
284
+ 'ask.pickHint': 'Pick a number, or type your own answer',
285
+ 'ask.freeHint': 'Type your answer',
286
+ 'ask.pickPrompt': '1-{끝}, or type your own',
287
+ 'ask.freePrompt': 'Answer',
288
+
276
289
  // ── Common ────────────────────────────────────────────────────────────
277
290
  'common.unknownCommand': 'Unknown command',
278
291
  'common.seeHelp': 'Run /help for the list',
package/src/i18n/index.js CHANGED
@@ -67,9 +67,45 @@ export function 언어정하기(값) {
67
67
  */
68
68
  export function 언어잡기({ env = process.env, cfg = null } = {}) {
69
69
  지금 = 언어고르기(env.DEEL_LANG) ?? 언어고르기(cfg?.lang) ?? 기본언어;
70
+ 지시 = 언어고르기(env.DEEL_PROMPT_LANG) ?? 언어고르기(cfg?.promptLang) ?? null;
70
71
  return 지금;
71
72
  }
72
73
 
74
+ /*
75
+ * ── 시키는 말과 보는 말은 다른 축이다 ──────────────────────────────────
76
+ *
77
+ * 여태 하나였다. /lang en 을 켜면 화면도, **모델에게 주는 글도**, 모델이
78
+ * 답하는 말도 한꺼번에 영어가 됐다. 그래서 "영어로 시키고 한국어로 받기" 가
79
+ * 아예 안 됐는데, 그게 실제로 쓸모가 있다:
80
+ *
81
+ * · 값이 눅다. 한글은 글자당 약 1토큰, 영문은 약 3.6자당 1토큰이다
82
+ * (session.js 의 estimateTokens). 8k 창에서 고정 몫이 눈에 띄게 준다.
83
+ * · 작은 모델이 영어 지시를 더 잘 따른다. 학습에 그쪽이 훨씬 많았다.
84
+ *
85
+ * 그런데 **읽는 사람은 한국 사람**이다. 그래서 둘을 갈랐다. 화면 말은
86
+ * 언어() 가, 모델에게 주는 글은 지시말() 이 정한다.
87
+ *
88
+ * null 이면 "따로 안 정했다" 는 뜻이고, 그때는 화면 말을 따라간다.
89
+ * 여태 쓰던 사람에게 아무것도 안 바뀌게 하려고 그렇게 뒀다.
90
+ */
91
+ let 지시 = null;
92
+
93
+ /** 모델에게 주는 글의 말. 따로 안 정했으면 화면 말을 따른다. */
94
+ export function 지시말() { return 지시 ?? 지금; }
95
+
96
+ /** 지시말을 따로 정한다. 빈 값·auto 면 다시 화면 말을 따라간다. */
97
+ export function 지시말정하기(값) {
98
+ const v = String(값 ?? '').trim().toLowerCase();
99
+ if (!v || v === 'auto' || v === '따라') { 지시 = null; return true; }
100
+ const 고른것 = 언어고르기(v);
101
+ if (!고른것) return false;
102
+ 지시 = 고른것;
103
+ return true;
104
+ }
105
+
106
+ /** 따로 정해 뒀나. 화면에 "따라감" 과 "영어로 못 박음" 을 갈라 보이려고 쓴다. */
107
+ export function 지시말따로정했나() { return 지시 !== null; }
108
+
73
109
  /*
74
110
  * 조사는 앞 글자의 받침을 보고 갈린다.
75
111
  *
package/src/i18n/ko.js CHANGED
@@ -67,12 +67,13 @@ export const ko = {
67
67
  'cmd.memory.arg': '[forget <번호>|clear|<적을 말>]',
68
68
  'cmd.level.desc': '사용자 수준 (쉬움/개발자)',
69
69
  'cmd.level.arg': '[수준]',
70
+ 'cmd.keys.desc': '이 터미널이 무슨 키를 보내는지 눌러서 확인 — 줄바꿈이 안 될 때',
70
71
  'cmd.bell.desc': '다 됐을 때·물어볼 때 종소리와 창 제목 (on/off)',
71
72
  'cmd.bell.arg': '[on|off]',
72
73
  'cmd.motion.desc': '일하는 동안 뭐가 도나 — 기사·동물·사무실',
73
74
  'cmd.motion.arg': '[기사|동물|사무실|기본|끔]',
74
75
  'cmd.lang.desc': '화면 말 — 한국어 / English',
75
- 'cmd.lang.arg': '[ko|en]',
76
+ 'cmd.lang.arg': '[ko|en] [시킬말]',
76
77
  'cmd.init.desc': 'DEEL.md 규칙 파일 만들기',
77
78
  'cmd.exit.desc': '끝내기',
78
79
  'cmd.quit.desc': '끝내기',
@@ -119,6 +120,7 @@ export const ko = {
119
120
  'head.shiftTabHint': '으로 바꿉니다 · ',
120
121
  'head.tabHint': 'Tab 은 치던 / 명령을 채웁니다',
121
122
  'head.newlineHint': '줄바꿈: 줄 끝에 빈칸+` 를 붙이고 Enter — 어디서나 됩니다 (Alt+Enter 는 터미널이 보내 줄 때만)',
123
+ 'head.star': '쓸 만하면 별 하나 주세요 —',
122
124
  'head.thisPC': '이 PC',
123
125
  'head.skills': '스킬 {n}',
124
126
  'head.commands': '명령 {n}',
@@ -195,8 +197,12 @@ export const ko = {
195
197
  'lang.unknown': 'ko 나 en 으로 적어 주세요.',
196
198
  'lang.progress': '{언어}: {옮김}/{전체} 마디가 옮겨져 있습니다. 나머지는 한국어로 나옵니다.',
197
199
  'lang.partial': '아직 다 안 옮겼습니다 — 빈칸을 내는 대신 한국어를 그대로 냅니다.',
198
- 'lang.codeStays': '모델이 읽는 글도 같이 따라갑니다 영어 쪽이 매 요청 1,400토큰쯤 덜 먹습니다. 코드와 도구·인자 이름은 그대로입니다.',
199
- 'lang.envHint': ' 때부터 영어로 쓰려면 DEEL_LANG=en',
200
+ 'lang.codeStays': '따로 안 정하면 모델이 읽는 글도 화면 말을 따라갑니다. 코드와 도구·인자 이름은 그대로입니다.',
201
+ 'lang.promptNow': '모델에게 시키는 말은 {이름}.',
202
+ 'lang.follows': '화면 말을 따라갑니다',
203
+ 'lang.splitHowto': '영어로 시키고 한국어로 받으려면',
204
+ 'lang.promptChanged': '시키는 말은 {이름}, 답은 {답:으로} 받습니다 — 8k 창에서 고정 몫이 23% 줄어듭니다.',
205
+ 'lang.envHint': '켤 때부터 영어로 쓰려면 DEEL_LANG=en (시키는 말만 영어로는 DEEL_PROMPT_LANG=en)',
200
206
 
201
207
  'cmd.lsp.desc': '언어 서버 — 무엇이 깔려 있고 Def·Refs 를 쓸 수 있는지',
202
208
  'cmd.lsp.arg': '[on|off]',
@@ -260,6 +266,13 @@ export const ko = {
260
266
  'level.developer.name': '개발자',
261
267
  'level.developer.hint': '전부 직접 만짐',
262
268
 
269
+ // ── 되묻기 상자 (Ask 도구) ────────────────────────────────────────────
270
+ 'ask.title': '물어봅니다',
271
+ 'ask.pickHint': '숫자로 고르거나, 그냥 쳐서 답하세요',
272
+ 'ask.freeHint': '답을 쳐 주세요',
273
+ 'ask.pickPrompt': '1-{끝} 또는 직접 입력',
274
+ 'ask.freePrompt': '답',
275
+
263
276
  // ── 자주 나오는 것 ────────────────────────────────────────────────────
264
277
  'common.unknownCommand': '모르는 명령입니다',
265
278
  'common.seeHelp': '명령 목록은 /help',