deel-local-cli 1.4.2 → 1.5.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/package.json CHANGED
@@ -1,60 +1,60 @@
1
- {
2
- "name": "deel-local-cli",
3
- "version": "1.4.2",
4
- "description": "로컬 모델·사내 게이트웨이 전용 코딩 에이전트 CLI — 외부 의존성 0개 / Zero-dependency coding agent CLI for local LLMs and private gateways",
5
- "type": "module",
6
- "license": "MIT",
7
- "bin": {
8
- "deel": "bin/deel.js"
9
- },
10
- "engines": {
11
- "node": ">=20"
12
- },
13
- "files": [
14
- "bin",
15
- "src",
16
- "README.md",
17
- "README.en.md",
18
- "LICENSE"
19
- ],
20
- "keywords": [
21
- "cli",
22
- "coding-agent",
23
- "local-llm",
24
- "ollama",
25
- "lm-studio",
26
- "openai-compatible",
27
- "offline",
28
- "air-gapped",
29
- "zero-dependency",
30
- "agent",
31
- "acp",
32
- "agent-client-protocol",
33
- "zed",
34
- "korean"
35
- ],
36
- "repository": {
37
- "type": "git",
38
- "url": "git+https://github.com/jysvai/deel-local-cli.git"
39
- },
40
- "bugs": {
41
- "url": "https://github.com/jysvai/deel-local-cli/issues"
42
- },
43
- "homepage": "https://github.com/jysvai/deel-local-cli#readme",
44
- "scripts": {
45
- "start": "node bin/deel.js",
46
- "setup": "node bin/deel.js setup",
47
- "diagnose": "node bin/deel.js diagnose",
48
- "chat": "node bin/deel.js",
49
- "test": "node test/run.mjs",
50
- "docs": "node tools/check-docs.mjs",
51
- "verify": "node test/no-bundle.test.js && node test/network.test.js && node test/web.test.js",
52
- "bench": "node test/edit-bench.js",
53
- "demo": "node test/demo.js",
54
- "check": "node --check bin/deel.js && node --check src/repl.js && node --check src/oneshot.js && node --check src/commands.js && node --check src/agent/loop.js && node --check src/agent/session.js && node --check src/agent/effort.js && node --check src/agent/modes.js && node --check src/agent/route.js && node --check src/agent/models.js && node --check src/agent/grade.js && node --check src/agent/mention.js && node --check src/agent/project.js && node --check src/tools/encoding.js && node --check src/tools/excel-com.js && node --check src/tools/excel.js && node --check src/tools/xlsx.js && node --check src/tools/docs.js && node --check src/agent/compact.js && node --check src/agent/store.js && node --check src/agent/sessionui.js && node --check src/backend/adapter.js && node --check src/backend/ctxsize.js && node --check src/backend/probe.js && node --check src/backend/detect.js && node --check src/backend/http.js && node --check src/backend/scan.js && node --check src/backend/scanui.js && node --check src/tools/index.js && node --check src/tools/desc.en.js && node --check src/tools/lsp.js && node --check src/lsp/rpc.js && node --check src/lsp/servers.js && node --check src/lsp/client.js && node --check src/lsp/diag.js && node --check src/tools/fsutil.js && node --check src/tools/edit-match.js && node --check src/tools/webfetch.js && node --check src/tools/todo.js && node --check src/tools/jobs.js && node --check src/skills/discover.js && node --check src/plugins/manage.js && node --check src/pack/zip.js && node --check src/pack/tar.js && node --check src/pack/selfpack.js && node --check src/safety/guard.js && node --check src/safety/undo.js && node --check src/safety/audit.js && node --check src/safety/network.js && node --check src/setup.js && node --check src/report.js && node --check src/config.js && node --check src/ui/diff.js && node --check src/ui/ansi.js && node --check src/ui/status.js && node --check src/ui/level.js && node --check src/ui/prompt.js && node --check src/ui/spinner.js && node --check src/ui/motion.js && node --check src/ui/notify.js && node --check src/ui/intro.js && node --check src/ui/banner.js && node --check src/ui/export.js && node --check src/i18n/index.js && node --check src/i18n/ko.js && node --check src/i18n/en.js && node --check src/preview/serve.js && node --check src/ui/working.js && node --check src/ui/inputbox.js && node --check src/ui/md.js && node --check src/agent/threads.js && node --check src/agent/evolve.js && node --check src/agent/pins.js && node --check src/agent/card.js && node --check src/agent/preset.js && node --check src/agent/evidence.js && node --check src/acp/jsonrpc.js && node --check src/acp/map.js && node --check src/acp/serve.js && node --check src/pack/sbom.js && node --check src/safety/secrets.js && echo OK",
55
- "coverage": "node test/coverage.mjs",
56
- "prepublishOnly": "npm run check && npm test"
57
- },
58
- "dependencies": {},
59
- "devDependencies": {}
60
- }
1
+ {
2
+ "name": "deel-local-cli",
3
+ "version": "1.5.0",
4
+ "description": "로컬 모델·사내 게이트웨이 전용 코딩 에이전트 CLI — 외부 의존성 0개 / Zero-dependency coding agent CLI for local LLMs and private gateways",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "deel": "bin/deel.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=20"
12
+ },
13
+ "files": [
14
+ "bin",
15
+ "src",
16
+ "README.md",
17
+ "README.en.md",
18
+ "LICENSE"
19
+ ],
20
+ "keywords": [
21
+ "cli",
22
+ "coding-agent",
23
+ "local-llm",
24
+ "ollama",
25
+ "lm-studio",
26
+ "openai-compatible",
27
+ "offline",
28
+ "air-gapped",
29
+ "zero-dependency",
30
+ "agent",
31
+ "acp",
32
+ "agent-client-protocol",
33
+ "zed",
34
+ "korean"
35
+ ],
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/jysvai/deel-local-cli.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/jysvai/deel-local-cli/issues"
42
+ },
43
+ "homepage": "https://github.com/jysvai/deel-local-cli#readme",
44
+ "scripts": {
45
+ "start": "node bin/deel.js",
46
+ "setup": "node bin/deel.js setup",
47
+ "diagnose": "node bin/deel.js diagnose",
48
+ "chat": "node bin/deel.js",
49
+ "test": "node test/run.mjs",
50
+ "docs": "node tools/check-docs.mjs",
51
+ "verify": "node test/no-bundle.test.js && node test/network.test.js && node test/web.test.js",
52
+ "bench": "node test/edit-bench.js",
53
+ "demo": "node test/demo.js",
54
+ "check": "node --check bin/deel.js && node --check src/repl.js && node --check src/oneshot.js && node --check src/commands.js && node --check src/agent/loop.js && node --check src/agent/session.js && node --check src/agent/effort.js && node --check src/agent/modes.js && node --check src/agent/route.js && node --check src/agent/models.js && node --check src/agent/grade.js && node --check src/agent/mention.js && node --check src/agent/project.js && node --check src/tools/encoding.js && node --check src/tools/excel-com.js && node --check src/tools/excel.js && node --check src/tools/xlsx.js && node --check src/tools/docs.js && node --check src/agent/compact.js && node --check src/agent/store.js && node --check src/agent/sessionui.js && node --check src/backend/adapter.js && node --check src/backend/ctxsize.js && node --check src/backend/probe.js && node --check src/backend/detect.js && node --check src/backend/http.js && node --check src/backend/scan.js && node --check src/backend/scanui.js && node --check src/tools/index.js && node --check src/tools/desc.en.js && node --check src/tools/lsp.js && node --check src/lsp/rpc.js && node --check src/lsp/servers.js && node --check src/lsp/client.js && node --check src/lsp/diag.js && node --check src/tools/fsutil.js && node --check src/tools/edit-match.js && node --check src/tools/webfetch.js && node --check src/tools/todo.js && node --check src/tools/jobs.js && node --check src/skills/discover.js && node --check src/plugins/manage.js && node --check src/pack/zip.js && node --check src/pack/tar.js && node --check src/pack/selfpack.js && node --check src/safety/guard.js && node --check src/safety/undo.js && node --check src/safety/audit.js && node --check src/safety/network.js && node --check src/setup.js && node --check src/report.js && node --check src/config.js && node --check src/ui/diff.js && node --check src/ui/ansi.js && node --check src/ui/status.js && node --check src/ui/level.js && node --check src/ui/prompt.js && node --check src/ui/spinner.js && node --check src/ui/motion.js && node --check src/ui/office.js && node --check src/ui/notify.js && node --check src/ui/intro.js && node --check src/ui/banner.js && node --check src/ui/export.js && node --check src/i18n/index.js && node --check src/i18n/ko.js && node --check src/i18n/en.js && node --check src/preview/serve.js && node --check src/ui/working.js && node --check src/ui/inputbox.js && node --check src/ui/md.js && node --check src/agent/threads.js && node --check src/agent/evolve.js && node --check src/agent/pins.js && node --check src/agent/card.js && node --check src/agent/preset.js && node --check src/agent/evidence.js && node --check src/acp/jsonrpc.js && node --check src/acp/map.js && node --check src/acp/serve.js && node --check src/pack/sbom.js && node --check src/safety/secrets.js && echo OK",
55
+ "coverage": "node test/coverage.mjs",
56
+ "prepublishOnly": "npm run check && npm test"
57
+ },
58
+ "dependencies": {},
59
+ "devDependencies": {}
60
+ }
package/src/i18n/en.js CHANGED
@@ -129,6 +129,7 @@ export const en = {
129
129
  'head.shiftTab': 'Shift+Tab',
130
130
  'head.shiftTabHint': ' changes it · ',
131
131
  'head.tabHint': 'Tab completes the / command you are typing',
132
+ 'head.newlineHint': 'New line: Alt+Enter (if your terminal sends it), or end a line with ` then Enter',
132
133
  'head.thisPC': 'found',
133
134
  'head.skills': 'skills {n}',
134
135
  'head.commands': 'commands {n}',
package/src/i18n/ko.js CHANGED
@@ -116,6 +116,7 @@ export const ko = {
116
116
  'head.shiftTab': 'Shift+Tab',
117
117
  'head.shiftTabHint': '으로 바꿉니다 · ',
118
118
  'head.tabHint': 'Tab 은 치던 / 명령을 채웁니다',
119
+ 'head.newlineHint': '줄바꿈: Alt+Enter(터미널이 지원하면) 또는 줄 끝에 ` 를 붙이고 Enter',
119
120
  'head.thisPC': '이 PC',
120
121
  'head.skills': '스킬 {n}',
121
122
  'head.commands': '명령 {n}',
@@ -164,7 +164,7 @@ export function reviewSheet(a, at) {
164
164
  L.push(' 설정 파일: ~/.deel/config.json (또는 환경변수 DEEL_API_KEY)');
165
165
  L.push('');
166
166
 
167
- L.push('3-1. 나가는 길은 갈래이고 서로 섞이지 않습니다');
167
+ L.push('3-1. 나가는 길은 갈래이고 서로 섞이지 않습니다');
168
168
  L.push(줄());
169
169
  L.push(' [A] 모델 게이트웨이 — 소스 코드가 실려 나가는 유일한 길');
170
170
  L.push(' · 주소: deel setup 에서 정한 곳 딱 한 자리');
@@ -181,8 +181,14 @@ export function reviewSheet(a, at) {
181
181
  L.push(' [C] 플러그인 받기 (github) — 사용자가 /plugin install 을 칠 때만');
182
182
  L.push(' · 그 명령이 도는 동안만 열리고 끝나면 닫힙니다.');
183
183
  L.push('');
184
- L.push(' --offline 으로 켜면 [B] [C] 모두 막히고, 컴퓨터 안으로만 다닙니다.');
185
- L.push(' (검증: npm test 안의 network / web 검사 54항목이 이를 확인합니다)');
184
+ L.push(' [D] MCP 서버 자식 프로세스, 남의 프로그램');
185
+ L.push(' · .deel/mcp.json 사람이 직접 적어야만 뜹니다. 기본은 꺼져 있습니다.');
186
+ L.push(' · A·B·C 와 달리 이 서버가 안에서 무슨 소켓을 여는지는 코드로 볼 수');
187
+ L.push(' 없습니다. 그래서 요청을 거르는 대신, --offline 이면 서버 자체를');
188
+ L.push(' 아예 띄우지 않습니다.');
189
+ L.push('');
190
+ L.push(' --offline 으로 켜면 [B]·[C]·[D] 가 모두 막히고, 이 컴퓨터 안으로만 다닙니다.');
191
+ L.push(' (검증: npm test 안의 network / web / mcp 검사 123항목이 이를 확인합니다)');
186
192
  L.push('');
187
193
 
188
194
  L.push('4. 스킬·플러그인');
package/src/repl.js CHANGED
@@ -322,17 +322,49 @@ export async function chatLoop(opts = {}) {
322
322
  let closed = false;
323
323
  const echo = !process.stdin.isTTY; // 파이프·기록용일 때는 입력을 되비춘다
324
324
 
325
+ /*
326
+ * 줄 끝에 ` (백틱) 을 붙이면 "아직 안 끝났다" 는 뜻이다 — 여러 줄을 한
327
+ * 메시지로 묶어 보낼 때 쓴다. 확정된 줄은 여기 쌓아 두다가, ` 없이 끝나는
328
+ * 줄이 오면 그때 \n 으로 이어붙여 한 메시지로 내보낸다.
329
+ *
330
+ * 실제 개행을 상자(rl.line) 안에 그대로 넣지는 않는다 — 접어쓰기() 가 \n 을
331
+ * 못 다뤄서(글자 하나로 세고 폭만 잰다) 그리면 커서 자리와 테두리 줄 수가
332
+ * 어긋난다. 그래서 물리적으로는 늘 한 줄씩만 치고, 이어붙이기는 밖에서 한다.
333
+ *
334
+ * 백슬래시(\) 가 아니라 백틱을 고른 것은 일부러다 — 이 프로그램은 윈도우
335
+ * 경로(`C:\Users\...`)를 늘 다루는데, 그 끝은 흔히 백슬래시로 끝난다.
336
+ * 백슬래시를 표로 쓰면 평범한 경로를 붙여넣기만 해도 계속 이어 치는 것으로
337
+ * 잘못 읽힌다. 백틱은 그럴 일이 없다.
338
+ */
339
+ let 이어쓰기줄들 = null;
340
+
325
341
  rl.on('line', (l) => {
326
342
  if (echo) say(c.gray(l));
343
+ let 보낼것 = l;
327
344
  if (상자쓰나) {
328
345
  if (묻는중 !== null) {
329
346
  // 되묻는 자리: 답을 그 줄에 남긴 채 줄만 넘긴다.
330
347
  process.stdout.write(`\r\x1b[2K${묻는중}${c.white(l)}\n`);
331
348
  } else if (입력기다림) {
349
+ // 백틱 **하나**로 끝날 때만 이어쓰기다. ``` 로 끝나는 줄은 마크다운
350
+ // 코드 울타리라 그대로 둬야 한다 — 코딩 도구에 코드를 붙여넣는 것은
351
+ // 드문 일이 아니고, 거기서 백틱 하나를 떼어먹으면 붙인 코드가 망가진다.
352
+ if (/(^|[^`])`$/.test(l)) {
353
+ const 줄 = l.slice(0, -1);
354
+ const 첫줄 = 이어쓰기줄들 === null;
355
+ (이어쓰기줄들 ??= []).push(줄);
356
+ 화면.입력지움();
357
+ say(`${첫줄 ? ` ${c.hcyan('❯')} ` : ' '}${c.white(줄)}`);
358
+ say(` ${c.gray('… 다음 줄. 그냥 Enter 로 보냅니다')}`);
359
+ return;
360
+ }
332
361
  // 상자를 걷어내고, 사람이 보낸 글을 대화에 남긴다. 안 남기면 스크롤을
333
362
  // 올렸을 때 답만 있고 무엇을 물었는지가 없다.
363
+ const 이었나 = 이어쓰기줄들 !== null;
364
+ 보낼것 = 이었나 ? [...이어쓰기줄들, l].join('\n') : l;
365
+ 이어쓰기줄들 = null;
334
366
  화면.입력지움();
335
- if (l.trim()) say(` ${c.hcyan('❯')} ${c.white(l)}`);
367
+ if (l.trim() || 이었나) say(`${이었나 ? ' ' : ` ${c.hcyan('❯')} `}${c.white(l)}`);
336
368
  } else if (l.trim()) {
337
369
  /*
338
370
  * 일하는 도중에 미리 쳐 둔 것.
@@ -344,8 +376,8 @@ export async function chatLoop(opts = {}) {
344
376
  화면.대기갱신('', queue.length + 1);
345
377
  }
346
378
  }
347
- if (waiter) { const w = waiter; waiter = null; w(l); }
348
- else queue.push(l);
379
+ if (waiter) { const w = waiter; waiter = null; w(보낼것); }
380
+ else queue.push(보낼것);
349
381
  });
350
382
  rl.on('close', () => {
351
383
  closed = true;
@@ -432,7 +464,33 @@ export async function chatLoop(opts = {}) {
432
464
  * 보이고, 그게 제일 못 미더운 화면이다.
433
465
  */
434
466
  if (!상자쓰나) return;
435
- if (key && key.name === 'return') return; // 줄이 끝나는 것은 'line' 이 맡는다
467
+ if (key && key.name === 'return') {
468
+ /*
469
+ * Alt+Enter(맥은 Option+Enter) 만 여기서 잡을 수 있다 — 나머지는 못 잡는다.
470
+ *
471
+ * 평범한 Enter 도, 대부분 터미널에서의 Shift+Enter·Ctrl+Enter 도 여기
472
+ * 닿기 전에 이미 끝나 있다. readline 도 우리와 같은 keypress 를 듣고
473
+ * 있는데(이 리스너보다 먼저 등록됐다), 터미널이 그 조합들을 평범한
474
+ * Enter 와 **똑같은 바이트**(\r, 드물게 \n)로 보내는 한 readline 이
475
+ * 우리보다 먼저 줄을 끝내 버린다 — 그 뒤에 우리가 뭘 해도 늦다.
476
+ * (readline 코드를 흉내 내서 직접 확인했다: \r·\n 은 keypress 가
477
+ * 이 리스너에 닿기도 전에 'line' 이벤트부터 쏜다.)
478
+ *
479
+ * ESC 로 시작하는 조합(Alt/Option+Enter)은 다르다 — readline 이 ESC
480
+ * 뒤에 더 올 것을 기다리는 동안 우리 keypress 가 먼저 온다. 그래서
481
+ * key.meta 로 여기서 골라낼 수 있다. rl.write('\n') 은 못 쓴다 —
482
+ * 그것도 내부적으로 줄을 끝내는 처리를 그대로 타 버린다(직접 확인).
483
+ * 그래서 rl.line·rl.cursor 를 손으로 이어붙인다.
484
+ */
485
+ if (key.meta && 입력기다림 && 묻는중 === null) {
486
+ const 앞 = (rl.line ?? '').slice(0, rl.cursor ?? 0);
487
+ const 뒤 = (rl.line ?? '').slice(rl.cursor ?? 0);
488
+ rl.line = `${앞}\n${뒤}`;
489
+ rl.cursor = 앞.length + 1;
490
+ } else {
491
+ return; // 줄이 끝나는 것은 'line' 이 맡는다
492
+ }
493
+ }
436
494
  if (묻는중 !== null) {
437
495
  const 앞 = 묻는중;
438
496
  setImmediate(() => {
@@ -488,6 +546,7 @@ export async function chatLoop(opts = {}) {
488
546
  * y 를 쳐도 화면에 아무것도 안 나타난다 — 먹은 건지 안 먹은 건지 모른다.
489
547
  */
490
548
  묻는중 = 상자쓰나 ? 앞 : null;
549
+ 이어쓰기줄들 = null; // 되묻는 사이에 걸쳐 있던 미완성 이어쓰기는 버린다
491
550
  try {
492
551
  const a = await nextLine();
493
552
  if (a === null) return o.def ?? '';
@@ -759,6 +818,8 @@ export async function chatLoop(opts = {}) {
759
818
  * 코드가 안 하는 것을 화면이 약속하고 있었던 셈이다.
760
819
  */
761
820
  let 이어갈것 = null; // 이어서 보낼 말 (null 이면 사람 입력을 기다린다)
821
+ // 지금 도는 하위 작업 수. 사무실이 자리를 이만큼 채운다.
822
+ let 도는하위 = 0;
762
823
  let 이어갈모드 = null; // 그때 쓸 작업 모드. 다시 고르지 않는다.
763
824
 
764
825
  /*
@@ -1031,6 +1092,9 @@ export async function chatLoop(opts = {}) {
1031
1092
  // 같이 돈 것은 이름을 다시 적어 준다. 안 그러면 어느 결과인지 모른다.
1032
1093
  if (ev.parallel || ev.showLabel) say(` ${toolLabel(ev.name, ev.args)}`);
1033
1094
  if (ev.name === 'TodoWrite' && ev.result?.todos) {
1095
+ // 사무실 화이트보드도 이걸 본다. 할 일은 세션이 아니라 턴 문맥에
1096
+ // 있어서 상자가 스스로 못 읽는다 — 아는 자리에서 넣어 준다.
1097
+ 화면.할일갱신(ev.result.todos);
1034
1098
  for (const t of ev.result.todos) {
1035
1099
  const 표 = t.state === 'done' ? c.green('☑') : t.state === 'doing' ? c.hyellow('▶') : c.gray('☐');
1036
1100
  const 글 = t.state === 'done' ? c.gray(t.text) : t.state === 'doing' ? c.white(t.text) : c.gray(t.text);
@@ -1129,6 +1193,9 @@ export async function chatLoop(opts = {}) {
1129
1193
  clearThinking();
1130
1194
  if (streamed) { 답비우기(); say(''); streamed = false; }
1131
1195
  화면.일바꿈('하위', clip(ev.목적, 24));
1196
+ // 사무실은 도는 하위 작업 수만큼 자리를 채운다.
1197
+ 도는하위 += 1;
1198
+ 화면.하위갱신(도는하위);
1132
1199
  say('');
1133
1200
  say(` ${c.hmagenta('⌥')} ${c.bold('하위 작업')} ${c.white(clip(ev.목적, 60))}`
1134
1201
  + ` ${c.gray(`· ${보일이름(ev.모드)} · 최대 ${ev.steps}걸음`)}`);
@@ -1150,6 +1217,8 @@ export async function chatLoop(opts = {}) {
1150
1217
  case 'task_done': {
1151
1218
  clearThinking();
1152
1219
  if (streamed) { 답비우기(); say(''); streamed = false; }
1220
+ 도는하위 = Math.max(0, 도는하위 - 1);
1221
+ 화면.하위갱신(도는하위);
1153
1222
  const 끝 = ev.끝 ?? {};
1154
1223
  const 잘됨 = 끝.type === 'done';
1155
1224
  const 왜 = { done: '끝냈습니다', limit: '걸음 수를 다 써서 멈췄습니다 — 다 못 했습니다',