deel-local-cli 1.2.0 → 1.4.1

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.
Files changed (50) hide show
  1. package/README.en.md +301 -1537
  2. package/README.md +272 -1582
  3. package/bin/deel.js +73 -4
  4. package/package.json +8 -3
  5. package/src/acp/jsonrpc.js +230 -0
  6. package/src/acp/map.js +219 -0
  7. package/src/acp/serve.js +556 -0
  8. package/src/agent/card.js +129 -0
  9. package/src/agent/compact.js +10 -2
  10. package/src/agent/effort.js +5 -0
  11. package/src/agent/evidence.js +186 -0
  12. package/src/agent/grade.js +20 -0
  13. package/src/agent/loop.js +134 -13
  14. package/src/agent/models.js +169 -0
  15. package/src/agent/modes.js +47 -1
  16. package/src/agent/pins.js +140 -0
  17. package/src/agent/preset.js +113 -0
  18. package/src/agent/project.js +10 -4
  19. package/src/agent/session.js +233 -13
  20. package/src/agent/store.js +31 -0
  21. package/src/backend/adapter.js +14 -0
  22. package/src/commands.js +616 -52
  23. package/src/i18n/en.js +265 -0
  24. package/src/i18n/index.js +126 -0
  25. package/src/i18n/ko.js +252 -0
  26. package/src/lsp/client.js +459 -0
  27. package/src/lsp/diag.js +112 -0
  28. package/src/lsp/rpc.js +84 -0
  29. package/src/lsp/servers.js +218 -0
  30. package/src/oneshot.js +19 -0
  31. package/src/pack/sbom.js +218 -0
  32. package/src/pack/selfpack.js +19 -3
  33. package/src/preview/serve.js +19 -2
  34. package/src/repl.js +193 -8
  35. package/src/safety/secrets.js +205 -0
  36. package/src/safety/undo.js +10 -3
  37. package/src/tools/desc.en.js +221 -0
  38. package/src/tools/docs.js +252 -0
  39. package/src/tools/index.js +190 -6
  40. package/src/tools/lsp.js +327 -0
  41. package/src/tools/task.js +30 -2
  42. package/src/ui/ansi.js +45 -0
  43. package/src/ui/approve.js +25 -21
  44. package/src/ui/banner.js +245 -0
  45. package/src/ui/export.js +217 -0
  46. package/src/ui/inputbox.js +37 -7
  47. package/src/ui/intro.js +206 -0
  48. package/src/ui/level.js +11 -5
  49. package/src/ui/notify.js +101 -0
  50. package/src/ui/status.js +181 -35
package/src/i18n/en.js ADDED
@@ -0,0 +1,265 @@
1
+ /**
2
+ * English.
3
+ *
4
+ * ── What this is, and what it is not ────────────────────────────────────
5
+ *
6
+ * deel is a Korean-language tool. The code stays Korean — functions and
7
+ * variables are named in Korean and that is deliberate. Only what shows up
8
+ * on screen is translated, and only so that someone who opens the repo can
9
+ * type `/help` and understand what they are looking at.
10
+ *
11
+ * Keys missing here fall back to Korean rather than to a blank. A blank is
12
+ * read as a bug; Korean is read as "not translated yet", which is the truth.
13
+ * `/lang` prints how much is done — see 옮긴만큼() in ./index.js.
14
+ *
15
+ * ── Wording rules ──────────────────────────────────────────────────────
16
+ *
17
+ * Keep the same directness as the Korean. The Korean text says what happens
18
+ * and what it costs you, not what the feature is called. Translate that,
19
+ * not the noun.
20
+ *
21
+ * Do not translate: command names (they are already English), `DEEL.md`,
22
+ * `MCP`, `ACP`, and the tool names (Read/Write/Bash/Verify…). Those are
23
+ * identifiers, and renaming them on screen would make the docs wrong.
24
+ */
25
+ export const en = {
26
+ // ── Slash commands ────────────────────────────────────────────────────
27
+ 'cmd.help.desc': 'list commands',
28
+ 'cmd.clear.desc': 'clear the conversation',
29
+ 'cmd.context.desc': 'show context usage',
30
+ 'cmd.ctx.desc': 'context length — re-measure for this model, or set it yourself',
31
+ 'cmd.ctx.arg': '[auto|number|640k]',
32
+ 'cmd.out.desc': 'cap on one reply — raise this if large files come back cut off',
33
+ 'cmd.out.arg': '[number|32k|auto]',
34
+ 'cmd.grade.desc': 'model class — how much it can do on its own. Not the same axis as window size',
35
+ 'cmd.grade.arg': '[small|medium|large|auto]',
36
+ 'cmd.compact.desc': 'shrink older turns',
37
+ 'cmd.model.desc': 'switch connection/model · `card` shows what this model has done here',
38
+ 'cmd.model.arg': '[name|list|models|card]',
39
+ 'cmd.think.desc': 'reasoning effort (off/low/medium/high/max)',
40
+ 'cmd.think.arg': '<level>',
41
+ 'cmd.mode.desc': 'approval policy — how much it asks first (auto/confirm/strict)',
42
+ 'cmd.mode.arg': '<mode>',
43
+ 'cmd.work.desc': 'work mode — what kind of job this is (picked for you in auto)',
44
+ 'cmd.work.arg': '[mode]',
45
+ 'cmd.auto.desc': 'work mode → auto (moves by itself with the request. Not /mode)',
46
+ 'cmd.code.desc': 'work mode → code (edits and builds)',
47
+ 'cmd.plan.desc': 'work mode → plan (plan first, no edits)',
48
+ 'cmd.architect.desc': 'work mode → architect (shapes the structure)',
49
+ 'cmd.debug.desc': 'work mode → debug (finds the cause)',
50
+ 'cmd.ask.desc': 'work mode → ask (explains only)',
51
+ 'cmd.orchestrator.desc': 'work mode → orchestrator (splits a big job up)',
52
+ 'cmd.undo.desc': 'undo the last turn — files and the conversation together',
53
+ 'cmd.undo.arg': '[turns]',
54
+ 'cmd.diff.desc': 'files changed this session, and what changed in them',
55
+ 'cmd.diff.arg': '[file]',
56
+ 'cmd.preview.desc': 'serve what you built, right here — nothing to deploy',
57
+ 'cmd.preview.arg': '[folder|off]',
58
+ 'cmd.tools.desc': 'show the tools available',
59
+ 'cmd.skills.desc': 'browse, search, and load skills',
60
+ 'cmd.skills.arg': '[search|all|off]',
61
+ 'cmd.plugin.desc': 'plugins — list, install, remove, bundle for air-gapped import',
62
+ 'cmd.plugin.arg': '[install|remove|pack]',
63
+ 'cmd.cost.desc': 'usage for this session',
64
+ 'cmd.status.desc': 'connection status',
65
+ 'cmd.scan.desc': 'scan this machine for local model servers',
66
+ 'cmd.scan.arg': '[save]',
67
+ 'cmd.thread.desc': 'threads — take a side track. Connection and undo history stay shared',
68
+ 'cmd.thread.arg': '[new|fork|close|number|name]',
69
+ 'cmd.learned.desc': 'what deel picked up on its own — commands that work here, this model’s habits',
70
+ 'cmd.learned.arg': '[clear]',
71
+ 'cmd.pin.desc': 'pin a line — survives folding and summarising',
72
+ 'cmd.pin.arg': '[text|clear number|clear all]',
73
+ 'cmd.evidence.desc': 'evidence — what changed and what proves it. Says what is unproven too',
74
+ 'cmd.evidence.arg': '[file]',
75
+ 'cmd.sessions.desc': 'past sessions in this folder',
76
+ 'cmd.recall.desc': 'search past sessions — by what was said, not by title',
77
+ 'cmd.recall.arg': '<text>',
78
+ 'cmd.mcp.desc': 'show connected MCP tool servers',
79
+ 'cmd.memory.desc': 'what survives the session — view and clear',
80
+ 'cmd.memory.arg': '[forget <n>|clear|<text to remember>]',
81
+ 'cmd.level.desc': 'user level (beginner/developer)',
82
+ 'cmd.level.arg': '[level]',
83
+ 'cmd.bell.desc': 'bell and window title when a turn ends or deel needs you (on/off)',
84
+ 'cmd.bell.arg': '[on|off]',
85
+ 'cmd.lang.desc': 'screen language — 한국어 / English',
86
+ 'cmd.lang.arg': '[ko|en]',
87
+ 'cmd.init.desc': 'create a DEEL.md rules file',
88
+ 'cmd.exit.desc': 'quit',
89
+ 'cmd.quit.desc': 'quit',
90
+
91
+ 'cmd.consult.desc': 'ask a second model one question — your current model stays put',
92
+ 'cmd.consult.arg': '[profile <question>]',
93
+ 'consult.title': 'models configured on this machine',
94
+ 'consult.noProfiles': 'No profiles configured. Run /scan to look for local servers.',
95
+ 'consult.current': '(in use)',
96
+ 'consult.usage': 'Usage: /consult <profile> <question> e.g. /consult small what does this regex do?',
97
+ 'consult.questionOnly': 'Only the question is sent — this conversation does not go with it. Put the background you need into the question.',
98
+ 'consult.candidates': 'These match:',
99
+ 'consult.have': 'Available:',
100
+ 'consult.needQuestion': 'Add what you want to ask {이름}. e.g. /consult {이름} what does this error mean?',
101
+ 'consult.goesOut': '— this question leaves your machine',
102
+ 'consult.asking': 'asking {모델}…',
103
+ 'consult.system': 'You are a model another coding agent is consulting. Answer briefly and precisely. Say so when you do not know. You are being asked a question, not told to edit files.',
104
+ 'consult.failed': 'Could not ask — {왜}',
105
+ 'consult.empty': 'The reply came back empty. That model may not answer without tools.',
106
+ 'consult.added': 'Added that answer to the conversation, labelled with who said it.',
107
+ // ── Startup header ────────────────────────────────────────────────────
108
+ 'head.spec.ollama': 'Ollama API',
109
+ 'head.spec.openai': 'OpenAI-compatible API',
110
+ 'head.model': 'model',
111
+ 'head.tokens': 'tokens',
112
+ 'head.grade': 'class',
113
+ 'head.guess': 'guessed',
114
+ 'head.send': 'sends to',
115
+ 'head.inside': 'this machine',
116
+ 'head.outside': 'outside',
117
+ 'head.offlineLock': '[offline lock]',
118
+ 'head.nowhereElse': '← and nowhere else',
119
+ 'head.glyphHint': 'The {안} at the start of the status bar means this. It becomes {밖} when traffic leaves.',
120
+ 'head.conn': 'link',
121
+ 'head.streaming': 'streaming',
122
+ 'head.noStreaming': 'no streaming',
123
+ 'head.tools': 'tools',
124
+ 'head.toolsUnknown': 'tools unconfirmed',
125
+ 'head.thinkCtl': 'effort control',
126
+ 'head.noThinkCtl': 'no effort control',
127
+ 'head.folder': 'folder',
128
+ 'head.approve': 'approval',
129
+ 'head.shiftTab': 'Shift+Tab',
130
+ 'head.shiftTabHint': ' changes it · ',
131
+ 'head.tabHint': 'Tab completes the / command you are typing',
132
+ 'head.thisPC': 'found',
133
+ 'head.skills': 'skills {n}',
134
+ 'head.commands': 'commands {n}',
135
+ 'head.plugins': 'plugins {n}',
136
+
137
+ 'grade.small': 'small',
138
+ 'grade.medium': 'medium',
139
+ 'grade.large': 'large',
140
+ 'head.gradeSuffix': ' · {급} {급이름}{짐작}',
141
+
142
+ // ── Status bar segments ───────────────────────────────────────────────
143
+ 'seg.dir': 'working folder (the ⌂/↗ in front says where your source goes)',
144
+ 'seg.thread': 'thread',
145
+ 'seg.model': 'model name',
146
+ 'seg.grade': 'model class (how much it does on its own)',
147
+ 'seg.ctx': 'context used (ticks = where folding and summarising kick in)',
148
+ 'seg.work': 'work mode',
149
+ 'seg.think': 'reasoning effort and split',
150
+ 'seg.mode': 'approval policy',
151
+ 'seg.edits': 'files changed this session',
152
+ 'seg.verify': 'what was actually verified',
153
+ 'seg.undoable': 'turns you can still undo',
154
+ 'seg.tok': 'tokens in/out',
155
+ 'seg.tools': 'tool calls',
156
+ 'seg.skills': 'skills found on this machine',
157
+ 'seg.time': 'time since start',
158
+
159
+ // ── Startup motion ────────────────────────────────────────────────────
160
+ 'intro.inside': 'stays on this machine',
161
+ 'intro.outside': 'leaves this machine',
162
+
163
+ // ── Undo ──────────────────────────────────────────────────────────────
164
+ 'undo.nothing': 'Nothing to undo.',
165
+ 'undo.done': 'Rolled back {turns} turn(s) · {files} file(s) restored.',
166
+ 'undo.alsoTalk': 'Rolled back {n} message(s) too — if the model still thinks the reverted code is there, it keeps building on it.',
167
+ 'undo.repaired': '({n} orphaned tool call(s) cleaned up as well)',
168
+ 'undo.saidWas': 'You had asked:',
169
+ 'undo.talkKept': 'The conversation is left as is — that turn had already been folded away.',
170
+ 'undo.talkKeptWhy': 'The model may still think the reverted work exists, so mention it in your next message.',
171
+
172
+ // ── Bell ──────────────────────────────────────────────────────────────
173
+ 'bell.stateOn': 'on',
174
+ 'bell.stateOff': 'off',
175
+ 'bell.now': 'The bell is {상태}.',
176
+ 'bell.when': 'It rings once when a turn takes longer than {초}s, and whenever deel needs an answer from you.',
177
+ 'bell.title': 'The window title shows the state too — you can tell from the tab alone whether it is still working.',
178
+ 'bell.howto': 'To change it',
179
+ 'bell.notATty': '(Not a terminal right now, so no bell and no title — that would corrupt the pipe.)',
180
+ 'bell.needOnOff': 'Please say on or off.',
181
+ 'bell.turnedOn': 'Bell on.',
182
+ 'bell.turnedOff': 'Bell off.',
183
+ 'bell.justRang': 'That was one ring. If you heard nothing, check your terminal’s bell setting.',
184
+ 'bell.appliesNow': 'Applies to this session right away.',
185
+
186
+ // ── Screen language ───────────────────────────────────────────────────
187
+ 'lang.now': 'Screen language is {이름}.',
188
+ 'lang.ko': '한국어 (Korean)',
189
+ 'lang.en': 'English',
190
+ 'lang.howto': 'To change it',
191
+ 'lang.changed': 'Screen language is now {이름}.',
192
+ 'lang.unknown': 'Please say ko or en.',
193
+ 'lang.progress': '{언어}: {옮김} of {전체} strings translated. The rest come through in Korean.',
194
+ 'lang.partial': 'Not everything is translated yet — untranslated lines show Korean rather than a blank.',
195
+ '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.',
196
+ 'lang.envHint': 'To start in English every time, set DEEL_LANG=en',
197
+
198
+ 'cmd.lsp.desc': 'language servers — what is installed, and whether Def/Refs are available',
199
+ 'cmd.lsp.arg': '[on|off]',
200
+ 'lsp.found': '{수} language server(s) on this machine',
201
+ 'lsp.none': 'No language server on this machine. Falling back to Grep and Outline.',
202
+ 'lsp.thisFolder': 'Language of this folder:',
203
+ 'lsp.noneHere': 'none usable',
204
+ 'lsp.fileCount': '{수} files',
205
+ 'lsp.tools': 'Tools:',
206
+ 'lsp.toolsHidden': 'hidden (no server here)',
207
+ 'lsp.diag': 'Diagnostics after an edit:',
208
+ 'lsp.stateOn': 'on',
209
+ 'lsp.stateOff': 'off',
210
+ 'lsp.ready': 'ready',
211
+ 'lsp.starting': 'starting',
212
+ 'lsp.diagOn': 'Diagnostics after an edit are on.',
213
+ 'lsp.diagOff': 'Diagnostics after an edit are off. Def and Refs still work.',
214
+ 'lsp.installHint': 'Install one and Def / Refs appear:',
215
+ 'lsp.neverInstalls': 'deel installs nothing. Those are commands for you to run yourself.',
216
+ 'lsp.howto': 'Turn post-edit diagnostics on or off:',
217
+
218
+ 'cmd.export.desc': 'this conversation as a one-page HTML report — asked, changed, verified',
219
+ 'cmd.export.arg': '[filename]',
220
+ 'export.untitled': 'Untitled conversation',
221
+ 'export.model': 'Model',
222
+ 'export.calls': '{수} tool calls',
223
+ 'export.conversation': 'What was asked and answered',
224
+ 'export.you': 'You',
225
+ 'export.nothing': 'Nothing was said.',
226
+ 'export.changed': 'Changed files',
227
+ 'export.file': 'File',
228
+ 'export.times': 'Times',
229
+ 'export.noChanges': 'No files were changed.',
230
+ 'export.evidence': 'What was run',
231
+ 'export.nothingRan': 'No commands were run.',
232
+ 'export.unproven': 'Not proven',
233
+ 'export.allProven': 'None — every change has a later check behind it.',
234
+ 'export.footer': 'Report generated by deel. No outbound links — opens on any network.',
235
+ 'export.saved': 'Report saved:',
236
+ 'export.openHint': 'Double-click to open. Attach it to a review or report as-is.',
237
+ 'export.failed': 'Could not save the report. Check folder permissions.',
238
+
239
+
240
+ // ── Help, approval policy, user level ─────────────────────────────────
241
+ 'help.titleCommon': 'commands (the ones you will use)',
242
+ 'help.title': 'commands',
243
+ 'help.moreHidden': '{n} more exist — type them and they run just the same.',
244
+ 'help.showAll': 'To see all of them',
245
+ 'help.restGoesToModel': 'Anything else goes to the model. Ctrl+C on an empty line quits.',
246
+ 'approve.auto.name': 'auto-approve',
247
+ 'approve.auto.short': 'auto',
248
+ 'approve.auto.line': 'edits without asking. /undo is the safety net',
249
+ 'approve.confirm.name': 'risky only',
250
+ 'approve.confirm.short': 'risky',
251
+ 'approve.confirm.line': 'asks before commands it cannot take back. Files are edited without asking',
252
+ 'approve.strict.name': 'ask always',
253
+ 'approve.strict.short': 'always',
254
+ 'approve.strict.line': 'asks before every file edit and every command',
255
+ 'level.beginner.name': 'beginner',
256
+ 'level.beginner.hint': 'start on the recommended settings',
257
+ 'level.developer.name': 'developer',
258
+ 'level.developer.hint': 'everything by hand',
259
+
260
+ // ── Common ────────────────────────────────────────────────────────────
261
+ 'common.unknownCommand': 'Unknown command',
262
+ 'common.seeHelp': 'Run /help for the list',
263
+ 'common.yes': 'yes',
264
+ 'common.no': 'no',
265
+ };
@@ -0,0 +1,126 @@
1
+ /**
2
+ * 화면 말 고르기 — 한국어 / 영어.
3
+ *
4
+ * ── 왜 이제 와서 ────────────────────────────────────────────────────────
5
+ *
6
+ * deel 은 한국어로 쓴 도구다. 함수 이름도 변수 이름도 한글이고, 그게 이
7
+ * 저장소의 뜻이다. 그건 안 바꾼다.
8
+ *
9
+ * 바꾸는 것은 **화면에 나가는 말**뿐이다. GitHub 에 올려 두고 보니, 처음
10
+ * 켠 사람이 `/help` 한 번 치고 나가 버린다. 명령 이름은 이미 영어인데
11
+ * (help·model·undo…) 그 옆의 설명이 전부 한글이라, 무엇을 하는 명령인지
12
+ * 알 길이 없어서다. 코드는 한국어로 두고 화면만 갈아 끼운다.
13
+ *
14
+ * ── 지키는 선 ───────────────────────────────────────────────────────────
15
+ *
16
+ * 1) **빈칸을 절대 안 낸다.** 영어 말이 없으면 한국어를 그대로 낸다.
17
+ * 반쯤 번역된 화면에서 제일 나쁜 것이 '아무것도 안 적힌 자리' 다 —
18
+ * 사람은 그걸 고장으로 읽지, 번역이 덜 됐다고 읽지 않는다.
19
+ * 2) **없는 열쇠도 안 죽는다.** 열쇠를 그대로 돌려준다. 화면이 좀 이상해도
20
+ * 프로그램은 돈다. 화면 말 하나 때문에 대화가 끊기면 안 된다.
21
+ * 3) **얼마나 됐는지 숨기지 않는다.** 검사가 안 옮긴 열쇠를 세어 적는다.
22
+ * 다 된 척하는 것보다 "298개 중 47개는 아직 한국어" 가 낫다.
23
+ * 4) 기본은 **한국어**다. 시스템 로캘을 안 본다 — 윈도우·WSL 에서
24
+ * LANG=en_US 인 한국 사람이 많아서, 그걸 보면 멀쩡히 쓰던 사람 화면이
25
+ * 어느 날 영어로 바뀐다. 영어는 **고른 사람만** 본다.
26
+ */
27
+ import { ko } from './ko.js';
28
+ import { en } from './en.js';
29
+
30
+ export const 언어들 = ['ko', 'en'];
31
+ export const 기본언어 = 'ko';
32
+
33
+ const 표 = { ko, en };
34
+
35
+ let 지금 = 기본언어;
36
+
37
+ /** 넘어온 값을 아는 언어로. 모르면 null. */
38
+ export function 언어고르기(값) {
39
+ const v = String(값 ?? '').trim().toLowerCase();
40
+ if (!v) return null;
41
+ if (v === 'ko' || v === 'kr' || v === 'korean' || v === '한국어' || v === '한글') return 'ko';
42
+ if (v === 'en' || v === 'eng' || v === 'english' || v === '영어') return 'en';
43
+ // ko_KR.UTF-8 · en-US 같은 것도 받는다.
44
+ const 앞 = v.split(/[-_.]/)[0];
45
+ return 언어들.includes(앞) ? 앞 : null;
46
+ }
47
+
48
+ /** 지금 언어. */
49
+ export function 언어() { return 지금; }
50
+
51
+ /**
52
+ * 언어를 정한다. 모르는 값이면 안 바꾸고 false.
53
+ * @returns {boolean} 바꿨나
54
+ */
55
+ export function 언어정하기(값) {
56
+ const v = 언어고르기(값);
57
+ if (!v) return false;
58
+ 지금 = v;
59
+ return true;
60
+ }
61
+
62
+ /**
63
+ * 켤 때 한 번. 환경변수 → 설정 순으로 본다.
64
+ *
65
+ * 환경변수가 이긴다. 한 번만 영어로 켜 보고 싶은 사람이 설정을 건드리지
66
+ * 않고 `DEEL_LANG=en deel` 로 할 수 있어야 한다.
67
+ */
68
+ export function 언어잡기({ env = process.env, cfg = null } = {}) {
69
+ 지금 = 언어고르기(env.DEEL_LANG) ?? 언어고르기(cfg?.lang) ?? 기본언어;
70
+ return 지금;
71
+ }
72
+
73
+ /**
74
+ * 말 한 마디.
75
+ *
76
+ * @param {string} 열쇠
77
+ * @param {Record<string, string|number>} [채움] `{이름}` 자리에 끼워 넣을 것
78
+ * @returns {string}
79
+ */
80
+ export function 말(열쇠, 채움 = null) {
81
+ const k = String(열쇠 ?? '');
82
+ // 있는 것 → 한국어 → 열쇠. 어느 자리에서도 빈칸이 안 나오게 세 겹으로 받친다.
83
+ const 글 = 표[지금]?.[k] ?? ko[k] ?? k;
84
+ if (!채움) return 글;
85
+ /*
86
+ * 자리 이름에 \w 를 쓰면 안 된다.
87
+ *
88
+ * 한글은 \w 가 아니다. `{안}` 은 영영 안 맞아서 화면에 중괄호째로 찍힌다 —
89
+ * 이 저장소에서 같은 함정을 이미 한 번 밟았다 — commands.js 의 배분 낱말 경계.
90
+ * 자리 이름은 한글로 짓는 편이 읽기 좋으니, 규칙을 글자 종류가 아니라
91
+ * **중괄호와 빈칸이 아닌 것**으로 잡는다.
92
+ *
93
+ * 안 준 자리는 그대로 둔다. 지워 버리면 "0개 턴" 처럼 틀린 말이 되고,
94
+ * 틀린 숫자는 안 적힌 것보다 나쁘다.
95
+ */
96
+ return String(글).replace(/\{([^{}\s]+)\}/g, (통째, 이름) =>
97
+ (Object.prototype.hasOwnProperty.call(채움, 이름) ? String(채움[이름]) : 통째));
98
+ }
99
+
100
+ /** 이 열쇠가 지금 언어로 옮겨져 있나. 검사와 /lang 이 본다. */
101
+ export function 옮겨졌나(열쇠, 언어이름 = 지금) {
102
+ return Object.prototype.hasOwnProperty.call(표[언어이름] ?? {}, String(열쇠));
103
+ }
104
+
105
+ /**
106
+ * 얼마나 옮겨졌나.
107
+ *
108
+ * 숨기지 않으려고 둔다. 다 된 척하는 화면보다 "298개 중 47개는 아직
109
+ * 한국어입니다" 가 낫다 — 그래야 도와줄 사람이 어디를 도울지 안다.
110
+ */
111
+ export function 옮긴만큼(언어이름 = 지금) {
112
+ const 전부 = Object.keys(ko);
113
+ const 있는것 = 전부.filter((k) => 옮겨졌나(k, 언어이름));
114
+ return {
115
+ 언어: 언어이름,
116
+ 전체: 전부.length,
117
+ 옮김: 있는것.length,
118
+ 남음: 전부.length - 있는것.length,
119
+ 안옮긴열쇠: 전부.filter((k) => !옮겨졌나(k, 언어이름)),
120
+ };
121
+ }
122
+
123
+ /** 한국어 쪽에 없는 열쇠가 영어 쪽에 있으면 그건 죽은 말이다. 검사가 본다. */
124
+ export function 짝없는열쇠(언어이름) {
125
+ return Object.keys(표[언어이름] ?? {}).filter((k) => !Object.prototype.hasOwnProperty.call(ko, k));
126
+ }
package/src/i18n/ko.js ADDED
@@ -0,0 +1,252 @@
1
+ /**
2
+ * 한국어 — 이쪽이 원본이다.
3
+ *
4
+ * 여기 없는 열쇠는 어느 언어에도 없다. en.js 는 여기 있는 것만 옮긴다.
5
+ * 열쇠를 새로 만들 때는 **반드시 여기부터** 적는다 — 반대로 하면 영어에만
6
+ * 있고 한국어에는 없는 말이 생기고, 그건 한국 사람 화면에 열쇠 이름이
7
+ * 그대로 찍히는 것으로 나타난다.
8
+ *
9
+ * 열쇠 이름은 `자리.무엇` 꼴이다. `cmd.*` 는 슬래시 명령, `head.*` 는 켤 때
10
+ * 머리말, `seg.*` 는 상태줄 조각 이름, 그 밖은 그 명령의 화면 글이다.
11
+ */
12
+ export const ko = {
13
+ // ── 슬래시 명령 ───────────────────────────────────────────────────────
14
+ 'cmd.help.desc': '명령 목록',
15
+ 'cmd.clear.desc': '대화 비우기',
16
+ 'cmd.context.desc': '컨텍스트 사용량 보기',
17
+ 'cmd.ctx.desc': '컨텍스트 길이 — 모델에 맞춰 다시 재거나 직접 지정',
18
+ 'cmd.ctx.arg': '[auto|숫자|640k]',
19
+ 'cmd.out.desc': '한 번에 받을 답 길이 상한 — 큰 파일이 잘리면 여기를 올린다',
20
+ 'cmd.out.arg': '[숫자|32k|auto]',
21
+ 'cmd.grade.desc': '모델 급 — 얼마나 알아서 하나. 창 크기와는 다른 축',
22
+ 'cmd.grade.arg': '[작음|보통|큼|auto]',
23
+ 'cmd.compact.desc': '오래된 대화 줄이기',
24
+ 'cmd.model.desc': '연결·모델 바꾸기 · `카드` 면 이 모델을 겪어 본 결과',
25
+ 'cmd.model.arg': '[이름|list|models|카드]',
26
+ 'cmd.think.desc': '추론 강도 (off/low/medium/high/max)',
27
+ 'cmd.think.arg': '<수준>',
28
+ 'cmd.mode.desc': '승인 정책 — 얼마나 물어보나 (auto/confirm/strict)',
29
+ 'cmd.mode.arg': '<모드>',
30
+ 'cmd.work.desc': '작업 모드 — 무슨 일을 하는 중인가 (종합이면 저절로)',
31
+ 'cmd.work.arg': '[모드]',
32
+ 'cmd.auto.desc': '작업 모드 → 종합 (요청에 따라 저절로 옮겨 감. /mode 와 다름)',
33
+ 'cmd.code.desc': '작업 모드 → 코드 (고치고 만든다)',
34
+ 'cmd.plan.desc': '작업 모드 → 계획 (먼저 계획만)',
35
+ 'cmd.architect.desc': '작업 모드 → 설계 (구조를 짠다)',
36
+ 'cmd.debug.desc': '작업 모드 → 디버그 (원인을 찾는다)',
37
+ 'cmd.ask.desc': '작업 모드 → 묻기 (설명만)',
38
+ 'cmd.orchestrator.desc': '작업 모드 → 총괄 (큰 일을 쪼개서)',
39
+ 'cmd.undo.desc': '직전 작업 되돌리기 — 파일과 대화를 같이',
40
+ 'cmd.undo.arg': '[턴수]',
41
+ 'cmd.diff.desc': '이번 대화에서 바뀐 파일 · 바뀐 자리 보기',
42
+ 'cmd.diff.arg': '[파일]',
43
+ 'cmd.preview.desc': '만든 웹을 이 자리에서 띄워 본다 — 올릴 필요 없음',
44
+ 'cmd.preview.arg': '[폴더|off]',
45
+ 'cmd.tools.desc': '쓸 수 있는 도구 보기',
46
+ 'cmd.skills.desc': '스킬 보기·검색·골라 올리기',
47
+ 'cmd.skills.arg': '[검색어|all|off]',
48
+ 'cmd.plugin.desc': '플러그인 목록·설치·삭제·반입묶음',
49
+ 'cmd.plugin.arg': '[install|remove|pack]',
50
+ 'cmd.cost.desc': '이번 세션 사용량',
51
+ 'cmd.status.desc': '연결 상태',
52
+ 'cmd.scan.desc': '이 PC 의 로컬 모델 서버 훑기',
53
+ 'cmd.scan.arg': '[save]',
54
+ 'cmd.thread.desc': '대화 갈래 — 곁가지를 딴 자리에서. 연결·되돌리기는 같이 씀',
55
+ 'cmd.thread.arg': '[new|fork|close|번호|이름]',
56
+ 'cmd.learned.desc': '쓰면서 저절로 알게 된 것 — 되는 명령·이 모델의 버릇',
57
+ 'cmd.learned.arg': '[지우기|clear]',
58
+ 'cmd.pin.desc': '못 박기 — 접거나 요약해도 안 지워지는 말',
59
+ 'cmd.pin.arg': '[말|clear 번호|clear all]',
60
+ 'cmd.evidence.desc': '증거 — 무엇을 바꿨고 무엇이 그걸 증명하나. 안 된 것도 적는다',
61
+ 'cmd.evidence.arg': '[파일]',
62
+ 'cmd.sessions.desc': '이 폴더의 지난 대화 목록',
63
+ 'cmd.recall.desc': '지난 대화에서 찾기 — 목록 말고 내용으로',
64
+ 'cmd.recall.arg': '<찾을 말>',
65
+ 'cmd.mcp.desc': '밖에서 붙인 도구(MCP) 서버 보기',
66
+ 'cmd.memory.desc': '대화가 끝나도 남는 기억 — 보기·지우기',
67
+ 'cmd.memory.arg': '[forget <번호>|clear|<적을 말>]',
68
+ 'cmd.level.desc': '사용자 수준 (쉬움/개발자)',
69
+ 'cmd.level.arg': '[수준]',
70
+ 'cmd.bell.desc': '다 됐을 때·물어볼 때 종소리와 창 제목 (on/off)',
71
+ 'cmd.bell.arg': '[on|off]',
72
+ 'cmd.lang.desc': '화면 말 — 한국어 / English',
73
+ 'cmd.lang.arg': '[ko|en]',
74
+ 'cmd.init.desc': 'DEEL.md 규칙 파일 만들기',
75
+ 'cmd.exit.desc': '끝내기',
76
+ 'cmd.quit.desc': '끝내기',
77
+
78
+ 'cmd.consult.desc': '다른 모델에게 한 번 물어보기 — 지금 쓰는 것은 안 바꿈',
79
+ 'cmd.consult.arg': '[프로필 <질문>]',
80
+ 'consult.title': '이 PC 에 적어 둔 모델들',
81
+ 'consult.noProfiles': '설정에 프로필이 하나도 없습니다. /scan 으로 이 PC 를 훑어 보세요.',
82
+ 'consult.current': '(지금 쓰는 것)',
83
+ 'consult.usage': '쓰기: /consult <프로필> <질문> 예) /consult small 이 정규식이 무슨 뜻이야?',
84
+ 'consult.questionOnly': '친 질문만 보냅니다 — 지금 대화는 안 딸려 갑니다. 필요한 배경은 질문에 적어 주세요.',
85
+ 'consult.candidates': '이런 것들이 걸립니다:',
86
+ 'consult.have': '있는 것:',
87
+ 'consult.needQuestion': '{이름} 에게 무엇을 물어볼지 같이 적어 주세요. 예) /consult {이름} 이 오류가 무슨 뜻이야?',
88
+ 'consult.goesOut': '— 이 질문은 바깥으로 나갑니다',
89
+ 'consult.asking': '{모델} 에게 물어보는 중…',
90
+ 'consult.system': '너는 다른 코딩 에이전트가 옆에서 물어보는 모델이다. 짧고 정확하게 답해라. 모르면 모른다고 해라. 파일을 고치라는 말이 아니라 묻는 말이다.',
91
+ 'consult.failed': '못 물어봤습니다 — {왜}',
92
+ 'consult.empty': '빈 답이 왔습니다. 그 모델이 도구 없이 답을 못 내는 것일 수 있습니다.',
93
+ 'consult.added': '이 답을 대화에도 넣었습니다 — 누가 한 말인지 표를 달아서.',
94
+ // ── 켤 때 머리말 ──────────────────────────────────────────────────────
95
+ 'head.spec.ollama': 'Ollama 규격',
96
+ 'head.spec.openai': 'OpenAI 호환 규격',
97
+ 'head.model': '모델',
98
+ 'head.tokens': '토큰',
99
+ 'head.grade': '급',
100
+ 'head.guess': '짐작',
101
+ 'head.send': '보냄',
102
+ 'head.inside': '이 컴퓨터 안',
103
+ 'head.outside': '바깥',
104
+ 'head.offlineLock': '[오프라인 잠금]',
105
+ 'head.nowhereElse': '← 여기 말고는 어디로도 안 갑니다',
106
+ 'head.glyphHint': '상태줄 맨 앞 {안} 가 이 뜻입니다. 바깥으로 가면 {밖} 로 바뀝니다.',
107
+ 'head.conn': '연결',
108
+ 'head.streaming': '스트리밍',
109
+ 'head.noStreaming': '스트리밍 없음',
110
+ 'head.tools': '도구',
111
+ 'head.toolsUnknown': '도구 미확인',
112
+ 'head.thinkCtl': '추론 조절',
113
+ 'head.noThinkCtl': '추론 조절 없음',
114
+ 'head.folder': '폴더',
115
+ 'head.approve': '승인',
116
+ 'head.shiftTab': 'Shift+Tab',
117
+ 'head.shiftTabHint': '으로 바꿉니다 · ',
118
+ 'head.tabHint': 'Tab 은 치던 / 명령을 채웁니다',
119
+ 'head.thisPC': '이 PC',
120
+ 'head.skills': '스킬 {n}',
121
+ 'head.commands': '명령 {n}',
122
+ 'head.plugins': '플러그인 {n}',
123
+
124
+ 'grade.small': '작음',
125
+ 'grade.medium': '보통',
126
+ 'grade.large': '큼',
127
+ 'head.gradeSuffix': ' · {급} {급이름}{짐작}',
128
+
129
+ // ── 상태줄 조각 이름 (/status 와 설정이 본다) ─────────────────────────
130
+ 'seg.dir': '작업 폴더 (앞의 ⌂/↗ 는 소스가 어디로 나가나)',
131
+ 'seg.thread': '대화 갈래',
132
+ 'seg.model': '모델 이름',
133
+ 'seg.grade': '모델 급 (얼마나 알아서 하나)',
134
+ 'seg.ctx': '컨텍스트 사용량 (눈금 = 접기·요약 자리)',
135
+ 'seg.work': '작업 모드',
136
+ 'seg.think': '추론 강도·배분',
137
+ 'seg.mode': '승인 방식',
138
+ 'seg.edits': '이번 대화에서 바뀐 파일',
139
+ 'seg.verify': '확인한 것 (Verify)',
140
+ 'seg.undoable': '되돌릴 수 있는 턴',
141
+ 'seg.tok': '주고받은 토큰',
142
+ 'seg.tools': '도구 호출 수',
143
+ 'seg.skills': '이 PC 에서 찾은 스킬',
144
+ 'seg.time': '켠 지 얼마나',
145
+
146
+ // ── 켤 때 도는 글자 모션 ──────────────────────────────────────────────
147
+ 'intro.inside': '이 컴퓨터 안에서만',
148
+ 'intro.outside': '바깥으로 나갑니다',
149
+
150
+ // ── 되돌리기 ──────────────────────────────────────────────────────────
151
+ 'undo.nothing': '되돌릴 것이 없습니다.',
152
+ 'undo.done': '{turns}개 턴, 파일 {files}개를 되돌렸습니다.',
153
+ 'undo.alsoTalk': '대화도 {n}개 걷어냈습니다 — 모델이 되돌린 코드를 아직 있는 줄 알면 그 위에 이어 일합니다.',
154
+ 'undo.repaired': '(짝 없는 도구 호출 {n}개도 같이 정리)',
155
+ 'undo.saidWas': '시켰던 말:',
156
+ 'undo.talkKept': '대화는 그대로 둡니다 — 그 턴의 말이 이미 접혀 없어졌습니다.',
157
+ 'undo.talkKeptWhy': '모델이 되돌린 것을 아직 있는 줄 알 수 있으니, 이어 시킬 때 한 번 짚어 주세요.',
158
+
159
+ // ── 종소리 ────────────────────────────────────────────────────────────
160
+ 'bell.stateOn': '켜져',
161
+ 'bell.stateOff': '꺼져',
162
+ 'bell.now': '종소리는 지금 {상태} 있습니다.',
163
+ 'bell.when': '{초}초 넘게 걸린 턴이 끝났을 때, 그리고 무언가 물어볼 때 한 번 울립니다.',
164
+ 'bell.title': '창 제목에도 상태가 뜹니다 — 탭 이름만 봐도 도는 중인지 알 수 있게.',
165
+ 'bell.howto': '바꾸려면',
166
+ 'bell.notATty': '(지금은 터미널이 아니라 소리도 제목도 안 나갑니다 — 파이프를 깨지 않으려고 그렇습니다.)',
167
+ 'bell.needOnOff': 'on 이나 off 로 적어 주세요.',
168
+ 'bell.turnedOn': '종소리를 켰습니다.',
169
+ 'bell.turnedOff': '종소리를 껐습니다.',
170
+ 'bell.justRang': '방금 한 번 울렸습니다. 안 들렸으면 터미널 설정에서 소리를 확인해 주세요.',
171
+ 'bell.appliesNow': '이 세션에도 바로 먹습니다.',
172
+
173
+ // ── 화면 말 (/lang) ───────────────────────────────────────────────────
174
+ 'lang.now': '화면 말은 지금 {이름} 입니다.',
175
+ 'lang.ko': '한국어',
176
+ 'lang.en': 'English (영어)',
177
+ 'lang.howto': '바꾸려면',
178
+ 'lang.changed': '화면 말을 {이름} 으로 바꿨습니다.',
179
+ 'lang.unknown': 'ko 나 en 으로 적어 주세요.',
180
+ 'lang.progress': '{언어}: {옮김}/{전체} 마디가 옮겨져 있습니다. 나머지는 한국어로 나옵니다.',
181
+ 'lang.partial': '아직 다 안 옮겼습니다 — 빈칸을 내는 대신 한국어를 그대로 냅니다.',
182
+ 'lang.codeStays': '모델이 읽는 글도 같이 따라갑니다 — 영어 쪽이 매 요청 1,400토큰쯤 덜 먹습니다. 코드와 도구·인자 이름은 그대로입니다.',
183
+ 'lang.envHint': '켤 때부터 영어로 쓰려면 DEEL_LANG=en',
184
+
185
+ 'cmd.lsp.desc': '언어 서버 — 무엇이 깔려 있고 Def·Refs 를 쓸 수 있는지',
186
+ 'cmd.lsp.arg': '[on|off]',
187
+ 'lsp.found': '이 PC 에 있는 언어 서버 {수}개',
188
+ 'lsp.none': '이 PC 에는 언어 서버가 없습니다. Grep · Outline 으로 갑니다.',
189
+ 'lsp.thisFolder': '이 폴더의 언어:',
190
+ 'lsp.noneHere': '쓸 수 있는 것 없음',
191
+ 'lsp.fileCount': '{수}개 파일',
192
+ 'lsp.tools': '도구:',
193
+ 'lsp.toolsHidden': '안 보여 줍니다 (서버가 없어서)',
194
+ 'lsp.diag': '고친 뒤 진단:',
195
+ 'lsp.stateOn': '켜짐',
196
+ 'lsp.stateOff': '꺼짐',
197
+ 'lsp.ready': '준비됨',
198
+ 'lsp.starting': '켜는 중',
199
+ 'lsp.diagOn': '고친 뒤 진단을 켰습니다.',
200
+ 'lsp.diagOff': '고친 뒤 진단을 껐습니다. Def · Refs 는 그대로 씁니다.',
201
+ 'lsp.installHint': '깔면 Def · Refs 가 생깁니다:',
202
+ 'lsp.neverInstalls': 'deel 은 아무것도 깔지 않습니다. 위 명령은 사람이 직접 칠 것입니다.',
203
+ 'lsp.howto': '고친 뒤 진단 켜고 끄기:',
204
+
205
+ 'cmd.export.desc': '이 대화를 보고서 한 장(HTML)으로 — 시킨 것·바뀐 것·확인한 것',
206
+ 'cmd.export.arg': '[파일이름]',
207
+ 'export.untitled': '제목 없는 대화',
208
+ 'export.model': '모델',
209
+ 'export.calls': '도구 {수}회',
210
+ 'export.conversation': '오간 말',
211
+ 'export.you': '나',
212
+ 'export.nothing': '오간 말이 없습니다.',
213
+ 'export.changed': '바뀐 파일',
214
+ 'export.file': '파일',
215
+ 'export.times': '몇 번',
216
+ 'export.noChanges': '바뀐 파일이 없습니다.',
217
+ 'export.evidence': '돌려 본 것',
218
+ 'export.nothingRan': '돌린 명령이 없습니다.',
219
+ 'export.unproven': '증명 안 된 것',
220
+ 'export.allProven': '없습니다 — 바꾼 것마다 뒤에 돌린 확인이 있습니다.',
221
+ 'export.footer': 'deel 이 만든 보고서입니다. 바깥으로 나가는 주소가 없어 어느 망에서든 열립니다.',
222
+ 'export.saved': '보고서를 남겼습니다:',
223
+ 'export.openHint': '더블클릭으로 열립니다. 그대로 결재·보고에 첨부하면 됩니다.',
224
+ 'export.failed': '보고서를 못 남겼습니다. 폴더 권한을 확인해 주세요.',
225
+
226
+
227
+ // ── 도움말 · 승인 방식 · 수준 ─────────────────────────────────────────
228
+ 'help.titleCommon': '명령 (자주 쓰는 것)',
229
+ 'help.title': '명령',
230
+ 'help.moreHidden': '이 밖에 {n}개가 더 있습니다 — 직접 입력하면 그대로 실행됩니다.',
231
+ 'help.showAll': '전부 보려면',
232
+ 'help.restGoesToModel': '그 밖의 입력은 모델에게 보냅니다. 빈 줄에서 Ctrl+C 로 끝냅니다.',
233
+ 'approve.auto.name': '자동 승인',
234
+ 'approve.auto.short': '자동',
235
+ 'approve.auto.line': '안 묻고 고칩니다. 되돌리기(/undo)가 안전망입니다',
236
+ 'approve.confirm.name': '위험만 확인',
237
+ 'approve.confirm.short': '위험만',
238
+ 'approve.confirm.line': '되돌릴 수 없는 명령만 물어봅니다. 파일은 안 묻고 고칩니다',
239
+ 'approve.strict.name': '모두 확인',
240
+ 'approve.strict.short': '모두',
241
+ 'approve.strict.line': '파일을 고치거나 명령을 돌리기 전에 매번 물어봅니다',
242
+ 'level.beginner.name': '쉬움',
243
+ 'level.beginner.hint': '권장값으로 바로 시작',
244
+ 'level.developer.name': '개발자',
245
+ 'level.developer.hint': '전부 직접 만짐',
246
+
247
+ // ── 자주 나오는 것 ────────────────────────────────────────────────────
248
+ 'common.unknownCommand': '모르는 명령입니다',
249
+ 'common.seeHelp': '명령 목록은 /help',
250
+ 'common.yes': '네',
251
+ 'common.no': '아니요',
252
+ };