deel-local-cli 1.4.3 → 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/README.en.md +997 -996
- package/README.md +988 -987
- package/package.json +60 -60
- package/src/i18n/en.js +1 -0
- package/src/i18n/ko.js +1 -0
- package/src/repl.js +73 -4
- package/src/ui/inputbox.js +557 -460
- package/src/ui/motion.js +312 -5
- package/src/ui/office.js +334 -0
- package/src/ui/screen.js +319 -312
- package/src/ui/status.js +1 -0
- package/src/ui/wrap.js +11 -0
package/package.json
CHANGED
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "deel-local-cli",
|
|
3
|
-
"version": "1.
|
|
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}',
|
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(
|
|
348
|
-
else queue.push(
|
|
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')
|
|
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: '걸음 수를 다 써서 멈췄습니다 — 다 못 했습니다',
|