deel-local-cli 1.20.12 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +96 -77
- package/README.md +119 -80
- package/bin/deel.js +398 -32
- package/package.json +2 -1
- package/src/acp/jsonrpc.js +87 -7
- package/src/acp/map.js +62 -13
- package/src/acp/serve.js +338 -30
- package/src/agent/agents.js +110 -12
- package/src/agent/askcheck.js +50 -6
- package/src/agent/asks.js +59 -5
- package/src/agent/budget.js +9 -2
- package/src/agent/card.js +8 -2
- package/src/agent/commit.js +168 -16
- package/src/agent/compact.js +93 -21
- package/src/agent/{/354/213/240/353/242/260/353/217/204.js → confidence.js} +1 -1
- package/src/agent/effort.js +85 -15
- package/src/agent/evidence.js +63 -7
- package/src/agent/evolve.js +132 -24
- package/src/agent/grade.js +12 -3
- package/src/agent/loop.js +408 -81
- package/src/agent/memory.js +176 -29
- package/src/agent/mention.js +22 -5
- package/src/agent/models.js +31 -7
- package/src/agent/modes.js +25 -10
- package/src/agent/outschema.js +381 -56
- package/src/agent/pins.js +33 -2
- package/src/agent/project.js +73 -8
- package/src/agent/recall.js +40 -9
- package/src/agent/{/354/204/261/355/225/234/352/270/260/353/241/235.js → recordshape.js} +19 -8
- package/src/agent/review.js +143 -13
- package/src/agent/route.js +506 -32
- package/src/agent/salvage.js +2 -1
- package/src/agent/session.js +457 -31
- package/src/agent/store.js +232 -22
- package/src/agent/threads.js +52 -3
- package/src/backend/adapter.js +508 -85
- package/src/backend/azure.js +12 -1
- package/src/backend/cachemark.js +31 -5
- package/src/backend/clientcert.js +23 -1
- package/src/backend/ctxsize.js +56 -9
- package/src/backend/detect.js +252 -19
- package/src/backend/http.js +383 -42
- package/src/backend/learn.js +101 -11
- package/src/backend/mcp.js +333 -24
- package/src/backend/price.js +61 -13
- package/src/backend/probe.js +188 -36
- package/src/backend/proxy.js +108 -14
- package/src/backend/quota.js +226 -30
- package/src/backend/retry.js +46 -3
- package/src/backend/scan.js +47 -10
- package/src/backend/scanui.js +57 -7
- package/src/backend/toolfit.js +204 -22
- package/src/backend/vision.js +14 -1
- package/src/backend/wire.js +58 -10
- package/src/cmdnames.js +16 -0
- package/src/commands/common.js +70 -0
- package/src/commands/extend.js +300 -0
- package/src/commands/model.js +852 -0
- package/src/commands/view.js +328 -0
- package/src/commands/work.js +823 -0
- package/src/commands.js +193 -2082
- package/src/completion.js +45 -3
- package/src/config.js +452 -39
- package/src/configexplain.js +125 -14
- package/src/doctor.js +164 -26
- package/src/i18n/en.js +22 -0
- package/src/i18n/index.js +31 -1
- package/src/i18n/ja.js +25 -1
- package/src/i18n/ko.js +25 -2
- package/src/i18n/zh.js +25 -1
- package/src/lsp/client.js +111 -9
- package/src/lsp/servers.js +86 -22
- package/src/oneshot.js +325 -65
- package/src/pack/sbom.js +28 -5
- package/src/pack/selfpack.js +54 -5
- package/src/pack/sheet.en.js +24 -3
- package/src/pack/tar.js +45 -8
- package/src/pack/zip.js +103 -8
- package/src/plugins/manage.js +165 -14
- package/src/preview/serve.js +239 -23
- package/src/providers/bedrock.js +30 -5
- package/src/providers/gemini.js +8 -0
- package/src/providers/index.js +17 -1
- package/src/repl.js +254 -61
- package/src/report.js +7 -2
- package/src/reset.js +151 -25
- package/src/safety/audit.js +108 -8
- package/src/safety/authcmd.js +75 -5
- package/src/safety/guard.js +507 -15
- package/src/safety/hooks.js +172 -20
- package/src/safety/keystore.js +178 -18
- package/src/safety/network.js +209 -6
- package/src/safety/policy.js +377 -36
- package/src/safety/runmode.js +9 -2
- package/src/safety/secrets.js +243 -17
- package/src/safety/shellenv.js +64 -3
- package/src/safety/trust.js +298 -32
- package/src/safety/undo.js +409 -17
- package/src/setup.js +175 -17
- package/src/skills/discover.js +122 -19
- package/src/stats.js +57 -16
- package/src/tools/{/355/231/225/354/235/270/353/262/225.js → checkmethods.js} +26 -1
- package/src/tools/clipboard.js +53 -20
- package/src/tools/convert.js +118 -37
- package/src/tools/desc.en.js +48 -5
- package/src/tools/doc2md.js +11 -1
- package/src/tools/docs.js +94 -18
- package/src/tools/edit-match.js +212 -13
- package/src/tools/encoding.js +525 -28
- package/src/tools/excel-com.js +63 -15
- package/src/tools/excel.js +12 -7
- package/src/tools/fastgrep.js +444 -36
- package/src/tools/fig.js +40 -9
- package/src/tools/fsutil.js +235 -21
- package/src/tools/hwpxwrite.js +80 -19
- package/src/tools/ignore.js +55 -7
- package/src/tools/index.js +1029 -145
- package/src/tools/jobs.js +325 -50
- package/src/tools/kiwi.js +50 -5
- package/src/tools/lsp.js +164 -32
- package/src/tools/outline.js +52 -12
- package/src/tools/pdf.js +366 -56
- package/src/tools/shell.js +12 -4
- package/src/tools/spawn.js +203 -18
- package/src/tools/todo.js +73 -4
- package/src/tools/verify.js +312 -43
- package/src/tools/webfetch.js +184 -25
- package/src/tools/xlsx.js +232 -31
- package/src/ui/ansi.js +209 -15
- package/src/ui/banner.js +2 -1
- package/src/ui/complete.js +46 -4
- package/src/ui/diff.js +4 -2
- package/src/ui/export.js +91 -17
- package/src/ui/inputbox.js +58 -18
- package/src/ui/intro.js +9 -2
- package/src/ui/level.js +32 -7
- package/src/ui/md.js +101 -15
- package/src/ui/motion.js +5 -2
- package/src/ui/notify.js +7 -1
- package/src/ui/office.js +37 -5
- package/src/ui/pastechip.js +3 -2
- package/src/ui/pick.js +85 -12
- package/src/ui/prompt.js +176 -13
- package/src/ui/screen.js +2 -1
- package/src/ui/spinner.js +6 -1
- package/src/ui/status.js +80 -12
- package/src/ui/wrap.js +23 -37
- /package/src/agent/{/353/213/250/352/263/204.js" → phase.js} +0 -0
- /package/src/skills/builtin/{/352/271/212/354/235/264/354/236/210/352/262/214-/353/247/214/353/223/244/352/270/260/SKILL.md" → build-deep/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/260/250/352/267/274/354/260/250/352/267/274-/353/224/224/353/262/204/352/271/205/SKILL.md" → debug-step-by-step/SKILL.md} +0 -0
- /package/src/skills/builtin/{/353/201/235/352/271/214/354/247/200-/355/225/230/352/270/260/SKILL.md" → finish-all/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/212/244/354/212/244/353/241/234-/352/262/200/355/206/240/SKILL.md" → self-review/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/275/224/353/223/234-/354/244/204/354/235/264/352/270/260/SKILL.md" → simplify-code/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/260/224/353/237/254/353/263/264/352/270/260/SKILL.md" → spike/SKILL.md} +0 -0
- /package/src/skills/builtin/{/352/262/200/354/202/254-/353/250/274/354/240/200/SKILL.md" → test-first/SKILL.md} +0 -0
package/src/ui/md.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
//
|
|
28
28
|
// 실제로는 제목·목록·코드펜스가 다 짧은 줄이라 거의 다 그려진다. 안 그려지는
|
|
29
29
|
// 것은 긴 산문뿐인데, 산문은 원래 꾸밀 것이 없다.
|
|
30
|
-
import { c, width } from './ansi.js';
|
|
30
|
+
import { c, width, 화면글거르기 } from './ansi.js';
|
|
31
31
|
import { 접어쓰기 } from './wrap.js';
|
|
32
32
|
|
|
33
33
|
const 굵게 = (s) => c.bold(s);
|
|
@@ -171,7 +171,11 @@ export function 한줄(줄, 상태 = { 펜스: false }, 폭 = 80) {
|
|
|
171
171
|
// 가름줄(`|---|---|`). 그대로 두면 화면에 작대기가 그대로 뜬다.
|
|
172
172
|
// 바로 앞 머리줄의 칸 너비를 기억해 뒀다가 그 자리에 맞춰 긋는다 —
|
|
173
173
|
// 그래야 세로줄과 가로줄이 만나서 표처럼 보인다.
|
|
174
|
-
|
|
174
|
+
//
|
|
175
|
+
// 하이픈을 하나는 **꼭** 요구한다. 안 그러면 빈칸과 세로줄뿐인 줄(`| | |`)까지
|
|
176
|
+
// 걸려서, 내용이 빈 데이터 행이 가로선으로 그려진다 — 그 행의 글이 아니라 행 자체가
|
|
177
|
+
// 사라진 것으로 보인다 (2.0.0 8회차).
|
|
178
|
+
if (/^\s*\|[\s:|-]*-[\s:|-]*\|\s*$/.test(s)) {
|
|
175
179
|
const 너비 = 상태.표너비 ?? s.split('|').map(() => 3);
|
|
176
180
|
return { 글: c.gray(너비.map((w) => '─'.repeat(w)).join('┼')), 상태 };
|
|
177
181
|
}
|
|
@@ -227,15 +231,36 @@ const 가름찾기 = (행들) =>
|
|
|
227
231
|
* 세로줄이다. 둘 다 칸을 가르면 안 되는 자리다.
|
|
228
232
|
*/
|
|
229
233
|
function 칸가르기(줄) {
|
|
230
|
-
|
|
234
|
+
// 끝의 바깥 세로줄만 뗀다 — `\|` 로 끝나면 칸 안의 세로줄이다 (2.0.0 6회차 마크6: `2 \` 만 남았다).
|
|
235
|
+
//
|
|
236
|
+
// 「앞이 백슬래시면 무조건 안 뗀다」 로 두면 반대로 샌다. 백슬래시가 **짝수** 개면
|
|
237
|
+
// 그것들은 서로를 벗긴 것이라 마지막 세로줄은 여전히 바깥 것이다 — `C:\\|` 의 닫는
|
|
238
|
+
// 세로줄을 못 떼서 칸에 `C:\|` 가 남았다 (2.0.0 8회차). 짝을 먼저 먹고 판단한다.
|
|
239
|
+
const s = String(줄 ?? '').replace(/\r+$/, '').trim().replace(/^\|/, '').replace(/(^|[^\\])((?:\\\\)*)\|$/, '$1$2');
|
|
231
240
|
const 칸들 = [];
|
|
232
241
|
let 지금 = '';
|
|
233
|
-
|
|
242
|
+
/*
|
|
243
|
+
* 인라인 코드는 **같은 길이의 백틱 줄**로 닫힌다(``` ``a | b`` ```). 백틱 하나마다 「코드 안」 을
|
|
244
|
+
* 뒤집었더니 겹백틱 코드 안의 `|` 에서 칸이 갈려 2칸 표가 3칸이 됐다 (2.0.0 6회차 마크6).
|
|
245
|
+
* 0 이면 코드 밖, 아니면 연 백틱 줄의 길이.
|
|
246
|
+
*/
|
|
247
|
+
let 코드길이 = 0;
|
|
234
248
|
for (let i = 0; i < s.length; i += 1) {
|
|
235
249
|
const ch = s[i];
|
|
250
|
+
// `\\` 는 백슬래시 한 자다. 안 벗기면 위에서 짝수로 세어 세로줄을 뗀 칸이
|
|
251
|
+
// 화면에는 `C:\\` 로 뜬다 — 뗀 만큼 글이 딴 것이 된다 (2.0.0 8회차).
|
|
252
|
+
if (ch === '\\' && s[i + 1] === '\\') { 지금 += '\\'; i += 1; continue; }
|
|
236
253
|
if (ch === '\\' && s[i + 1] === '|') { 지금 += '|'; i += 1; continue; }
|
|
237
|
-
if (ch === '`') {
|
|
238
|
-
|
|
254
|
+
if (ch === '`') {
|
|
255
|
+
let n = 1;
|
|
256
|
+
while (s[i + n] === '`') n += 1;
|
|
257
|
+
if (코드길이 === 0) 코드길이 = n;
|
|
258
|
+
else if (n === 코드길이) 코드길이 = 0;
|
|
259
|
+
지금 += s.slice(i, i + n);
|
|
260
|
+
i += n - 1;
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
if (ch === '|' && 코드길이 === 0) { 칸들.push(지금); 지금 = ''; continue; }
|
|
239
264
|
지금 += ch;
|
|
240
265
|
}
|
|
241
266
|
칸들.push(지금);
|
|
@@ -362,6 +387,9 @@ export class 마크다운 {
|
|
|
362
387
|
// 바닥을 둬서, 좁은 창에서도 짧은 줄은 제 모양으로 나가게 한다.
|
|
363
388
|
this.폭 = Math.max(20, Number(폭) || 0);
|
|
364
389
|
this.담긴것 = '';
|
|
390
|
+
// 토막 끝에서 끊긴 제어 순서의 앞머리(`ESC [1`). 다음 토막과 붙여서 통째로 뗀다 — 토막마다 따로
|
|
391
|
+
// 떼면 ESC 만 빠지고 `[1A[2K` 가 글자로 화면에 남는다.
|
|
392
|
+
this.날토막 = '';
|
|
365
393
|
this.상태 = { 펜스: false };
|
|
366
394
|
// 이 줄은 이미 날것으로 흘려보내기 시작했다 — 뒤늦게 꾸미면 앞뒤가 안 맞는다.
|
|
367
395
|
this.날것중 = false;
|
|
@@ -434,17 +462,38 @@ export class 마크다운 {
|
|
|
434
462
|
return 나갈것;
|
|
435
463
|
}
|
|
436
464
|
|
|
437
|
-
// 표가 끝났다.
|
|
465
|
+
// 표가 끝났다. 비운 **뒤에** 지운다 — 끝() 이 적어 둔 것과 같은 까닭이다.
|
|
466
|
+
// 먼저 지우면 상한에 끊긴 표의 뒷동강이 머리줄과 칸 너비를 잃어, 산문이 한 줄
|
|
467
|
+
// 오는 것만으로 표 아래쪽이 테두리도 정렬도 없는 딴 모양으로 나간다.
|
|
468
|
+
const 앞 = this.#표비우기();
|
|
469
|
+
// 다음 표는 제 폭을 처음부터 다시 잰다.
|
|
438
470
|
this.이어갈머리 = null;
|
|
439
471
|
this.표너비 = null;
|
|
440
|
-
const 앞 = this.#표비우기();
|
|
441
472
|
const r = 한줄(줄, this.상태, this.폭);
|
|
442
473
|
this.상태 = r.상태;
|
|
443
474
|
return [...앞, r.글];
|
|
444
475
|
}
|
|
445
476
|
|
|
446
477
|
넣기(토막) {
|
|
447
|
-
|
|
478
|
+
// 모델 답은 바깥 글이다 — 섞여 온 터미널 제어 순서는 여기서 뗀다(ansi.js 화면글거르기 머리말).
|
|
479
|
+
// 그리기() 도 이 길을 지나므로 한 자리에서 막힌다.
|
|
480
|
+
let 날것 = this.날토막 + String(토막 ?? '');
|
|
481
|
+
this.날토막 = '';
|
|
482
|
+
let 끝ESC = Math.max(날것.lastIndexOf('\x1b'), 날것.lastIndexOf('\x9b'));
|
|
483
|
+
// `ESC ] … ESC` 로 끝나면 마침 글자(ESC \) 가 반만 왔다. 그 ESC 만 붙들면 앞 몸통(링크 주소)이
|
|
484
|
+
// 끝 안 난 순서로 걸러져 글자로 샌다 — 순서 머리부터 붙든다.
|
|
485
|
+
if (끝ESC > 0 && 날것.slice(끝ESC) === '\x1b') {
|
|
486
|
+
const 앞ESC = 날것.lastIndexOf('\x1b', 끝ESC - 1);
|
|
487
|
+
if (앞ESC >= 0 && /^\x1b(?:\][^\x07\x1b\n]*|[PX^_][^\x1b\n]*)$/.test(날것.slice(앞ESC, 끝ESC))) 끝ESC = 앞ESC;
|
|
488
|
+
}
|
|
489
|
+
// 끝난 순서가 아니면(마침 글자 없음) 붙들어 둔다. 너무 길면 순서가 아니라 보고 그냥 뗀다.
|
|
490
|
+
// 8비트 CSI(0x9b) 와 ESC 두 글자 꼴의 가운데 글자(`ESC (`)도 같다 — 안 붙들면 `2J` · `0` 이 글자로 남는다.
|
|
491
|
+
if (끝ESC >= 0 && 날것.length - 끝ESC < 256
|
|
492
|
+
&& /^(?:\x1b(?:\[[0-?]*[ -/]*|\][^\x07\x1b\n]*\x1b?|[PX^_][^\x1b\n]*\x1b?|[ -/]*)?|\x9b[0-?]*[ -/]*)$/.test(날것.slice(끝ESC))) {
|
|
493
|
+
this.날토막 = 날것.slice(끝ESC);
|
|
494
|
+
날것 = 날것.slice(0, 끝ESC);
|
|
495
|
+
}
|
|
496
|
+
this.담긴것 += 화면글거르기(날것);
|
|
448
497
|
const 나갈것 = [];
|
|
449
498
|
|
|
450
499
|
for (;;) {
|
|
@@ -491,19 +540,56 @@ export class 마크다운 {
|
|
|
491
540
|
return 나갈것;
|
|
492
541
|
}
|
|
493
542
|
|
|
543
|
+
/*
|
|
544
|
+
* ── 여기서 **코드 울타리 안인지**까지 지운다 ────────────────────────────
|
|
545
|
+
*
|
|
546
|
+
* 이 파일 위쪽이 한 번 적어 뒀다 — 「울타리를 열기만 하면 그 뒤로 오는 답
|
|
547
|
+
* 전체가 코드로 그려진다. 제목도 굵기도 다 사라지고, 화면은 멀쩡해 보인다」.
|
|
548
|
+
* 그 일이 실제로 나던 자리가 여기다.
|
|
549
|
+
*
|
|
550
|
+
* 끝() 은 담긴 반 줄과 붙들고 있던 표는 비우면서 `상태.펜스` 만 켜 둔 채로
|
|
551
|
+
* 돌아갔다. 부르는 쪽(repl.js)은 마크다운 하나를 한 턴 내내 쓰고, 도구를
|
|
552
|
+
* 부를 때마다 여기를 거친다. 그래서 도구 부르기 직전에 ```js 한 줄이
|
|
553
|
+
* 나갔으면 **그 턴의 남은 답이 통째로** 회색 세로줄 붙은 날글로 그려졌다.
|
|
554
|
+
* 잘려서 다시 받는 자리(retry)는 더 나쁘다 — 새로 받은 답 전체가 그 꼴이 된다.
|
|
555
|
+
*
|
|
556
|
+
* 화면에 글자는 다 있다. 그래서 아무도 고장으로 안 읽고, 「모델이 오늘
|
|
557
|
+
* 이상하게 답한다」 로 읽힌다. 흘려보내던 상태는 흘려보내기가 끝나는
|
|
558
|
+
* **이 자리에서 다 지운다.**
|
|
559
|
+
*/
|
|
560
|
+
/** 그리던 것을 다 잊는다. 다음 글은 처음부터 읽는다. */
|
|
561
|
+
처음으로() {
|
|
562
|
+
this.담긴것 = '';
|
|
563
|
+
this.날토막 = '';
|
|
564
|
+
this.상태 = { 펜스: false };
|
|
565
|
+
this.날것중 = false;
|
|
566
|
+
this.표줄들 = [];
|
|
567
|
+
this.이어갈머리 = null;
|
|
568
|
+
this.표너비 = null;
|
|
569
|
+
}
|
|
570
|
+
|
|
494
571
|
/** 답이 끝났다. 남은 것을 내놓는다. */
|
|
495
572
|
끝() {
|
|
573
|
+
const 나갈것 = [];
|
|
574
|
+
// 끝내 마침 글자가 안 온 순서 앞머리는 글자가 아니다 — 떼고 버린다. 거르개에 넣었더니
|
|
575
|
+
// ESC·`[` 만 떼이고 `ESC [1` 의 `1` 이 글자로 남았다 (2.0.0 6회차 마크6).
|
|
576
|
+
this.날토막 = ''; // 붙들던 앞머리는 버린다
|
|
496
577
|
// 담긴 것은 없는데 날것으로 나가던 중이면 줄만 끝내 준다.
|
|
497
578
|
if (!this.담긴것) {
|
|
498
|
-
|
|
579
|
+
if (this.날것중) 나갈것.push({ 이어붙임: '', 끝났나: true });
|
|
499
580
|
this.날것중 = false;
|
|
500
581
|
// 표로 끝나는 답이 흔하다(마지막이 정리 표). 여기서 안 비우면 통째로 사라진다.
|
|
501
|
-
|
|
582
|
+
나갈것.push(...this.#표비우기());
|
|
583
|
+
} else {
|
|
584
|
+
const 남은것 = this.#그리기(this.담긴것);
|
|
585
|
+
this.담긴것 = '';
|
|
586
|
+
this.날것중 = false;
|
|
587
|
+
나갈것.push(...남은것, ...this.#표비우기());
|
|
502
588
|
}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
this
|
|
506
|
-
return
|
|
589
|
+
// 비운 **뒤에** 지운다. 먼저 지우면 위 두 줄이 붙들고 있던 표를 옛 방식으로
|
|
590
|
+
// 내보낼 때 쓸 상태를 잃는다 — 글이 사라지는 것이 제일 나쁘다.
|
|
591
|
+
this.처음으로();
|
|
592
|
+
return 나갈것;
|
|
507
593
|
}
|
|
508
594
|
}
|
|
509
595
|
|
package/src/ui/motion.js
CHANGED
|
@@ -402,7 +402,7 @@ const 동물그림들 = {
|
|
|
402
402
|
['.#..#.......', '.####.......', '######......', '............'],
|
|
403
403
|
],
|
|
404
404
|
|
|
405
|
-
// 웹 — 새
|
|
405
|
+
// 웹 — 새 두 마리. 앞엣것은 제자리에서 날갯짓하고, 뒤엣것이 오른쪽으로 날아간다.
|
|
406
406
|
웹: [
|
|
407
407
|
['..#.........', '.###..#.....', '..#..###....', '......#.....'],
|
|
408
408
|
['.###........', '..#...###...', '......#.....', '............'],
|
|
@@ -496,7 +496,10 @@ export const 테마들 = {
|
|
|
496
496
|
*
|
|
497
497
|
* DEEL_MOTION=기사 · knight · 동물 · animal 로 고른다. 영어 이름도 받는 것은
|
|
498
498
|
* 한글을 못 치는 자리(CI 설정 파일·도커 환경변수)가 있기 때문이다.
|
|
499
|
-
* 모르는 이름이면 조용히
|
|
499
|
+
* 모르는 이름이면 조용히 **없는 셈 친다** — 오타 하나로 프로그램이 안 뜨면 안 된다.
|
|
500
|
+
* 없는 셈 치니 그 아래가 이어서 이긴다: 설정 파일에 고른 것이 있으면 그것, 그것도
|
|
501
|
+
* 없을 때라야 기본이다. `DEEL_MOTION=cat` 은 기본이 아니라 설정대로 돈다는 뜻이라,
|
|
502
|
+
* `/motion` 은 「환경변수가 이깁니다」 를 말하기 전에 `환경그림()` 으로 되물어야 한다.
|
|
500
503
|
*/
|
|
501
504
|
const 이름표 = {
|
|
502
505
|
기사: '기사', knight: '기사', 동물: '동물', animal: '동물', animals: '동물',
|
package/src/ui/notify.js
CHANGED
|
@@ -64,7 +64,13 @@ export function 창제목(글) {
|
|
|
64
64
|
return 쓰기(`\x1b]2;${s}\x1b\\\x1b]1;${s}\x1b\\`);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
/*
|
|
68
|
+
* 제목을 터미널에 도로 맡긴다 — 남의 터미널을 우리 이름으로 두고 나가지 않는다.
|
|
69
|
+
*
|
|
70
|
+
* 켤 때 있던 제목을 그대로 되살리지는 못한다. 그것을 알려면 터미널에 물어야 하는데
|
|
71
|
+
* (OSC 21), 답하는 터미널이 얼마 없고 안 답하면 그 답을 기다리다 멈춘다. 빈 제목을
|
|
72
|
+
* 보내면 터미널·셸이 제 규칙대로(대개 셸 프롬프트가 다시 정한다) 채운다.
|
|
73
|
+
*/
|
|
68
74
|
export function 제목되돌리기() {
|
|
69
75
|
return 쓰기('\x1b]2;\x1b\\\x1b]1;\x1b\\');
|
|
70
76
|
}
|
package/src/ui/office.js
CHANGED
|
@@ -56,9 +56,21 @@ function 색충분한가() {
|
|
|
56
56
|
|
|
57
57
|
// 윈도우는 Node 20 이 Win10 이상을 요구하고, 거기 콘솔은 256색을 한다.
|
|
58
58
|
if (process.platform === 'win32') return true;
|
|
59
|
-
|
|
59
|
+
/*
|
|
60
|
+
* 아래 둘은 윈도우 콘솔이 심는 값인데, 정작 **윈도우에서는 한 번도 안 돈다** —
|
|
61
|
+
* 바로 위 한 줄이 이미 참으로 답하고 나가기 때문이다. 그러니 이 두 줄이 사는
|
|
62
|
+
* 자리는 윈도우가 아니라 **윈도우가 아닌 판**, 곧 WSL 이다. 윈도우 터미널이
|
|
63
|
+
* WSLENV 로 WT_SESSION 을 리눅스 쪽에 건너보내므로(WSLENV=WT_SESSION:…),
|
|
64
|
+
* 거기서는 platform 이 linux 인 채로 이 값만 서 있다.
|
|
65
|
+
*
|
|
66
|
+
* 죽은 줄로 보고 지우면 그 자리에서 사무실이 안 켜진다. 자리를 위로 옮겨도
|
|
67
|
+
* 답은 한 글자도 안 바뀐다(어차피 둘 다 참이다). 그래서 코드는 그대로 두고
|
|
68
|
+
* 주석만 사실에 맞춘다 — 「윈도우 터미널」 이라고만 적혀 있어서 윈도우에서
|
|
69
|
+
* 도는 줄로 읽혔다.
|
|
70
|
+
*/
|
|
71
|
+
if (process.env.WT_SESSION) return true; // 윈도우 터미널 — WSL 쪽에서 돈다
|
|
60
72
|
if (process.env.TERM_PROGRAM) return true; // iTerm · VS Code · Apple Terminal
|
|
61
|
-
if (process.env.ConEmuANSI === 'ON') return true;
|
|
73
|
+
if (process.env.ConEmuANSI === 'ON') return true; // ConEmu — 마찬가지로 윈도우 밖에서만
|
|
62
74
|
|
|
63
75
|
return false;
|
|
64
76
|
}
|
|
@@ -375,13 +387,20 @@ export function 사무실줄들(상태, 폭) {
|
|
|
375
387
|
// 한 바퀴에 1분. 7.5초마다 한 칸씩 — 도는 것이 보이면서도 매 판 다시
|
|
376
388
|
// 그릴 만큼 부산하지는 않다.
|
|
377
389
|
const 칸 = Math.floor((걸린초 % 60) / 7.5) % 8;
|
|
378
|
-
|
|
390
|
+
/*
|
|
391
|
+
* 세로로는 **한 칸만** 뻗는다. 시계판 속은 가로 다섯 · 세로 셋뿐이라,
|
|
392
|
+
* 세로로 둘을 가면 바늘 끝이 판을 넘어 테두리 줄에 얹힌다. 그 테두리도
|
|
393
|
+
* 바늘도 C.검정 이라 12시·6시 두 자리는 **안 찍은 것과 똑같은 그림**이
|
|
394
|
+
* 됐다 — 여덟 자리 중 둘이 서로 구별이 안 됐고, 0초 판과 30초 판이 한
|
|
395
|
+
* 글자도 안 달랐다. 바늘은 판 안에서만 돈다.
|
|
396
|
+
*/
|
|
397
|
+
const [dx, dy] = [[0, -1], [1, -1], [2, 0], [1, 1], [0, 1], [-1, 1], [-2, 0], [-1, -1]][칸];
|
|
379
398
|
p.찍기(cx + 3, 2, C.검정);
|
|
380
399
|
p.찍기(cx + 3 + dx, 2 + dy, C.검정);
|
|
381
400
|
}
|
|
382
401
|
}
|
|
383
402
|
|
|
384
|
-
// 서류함 12×
|
|
403
|
+
// 서류함 12×10 — 읽은 파일. 서랍 하나가 일곱쯤 (테두리 한 줄 + 서랍 셋 × 세 줄).
|
|
385
404
|
{
|
|
386
405
|
const 찬칸 = Math.min(3, Math.ceil(읽은파일 / 7));
|
|
387
406
|
const 그림 = ['CCCCCCCCCCCC'];
|
|
@@ -680,7 +699,20 @@ export function 상태만들기(session, 일감, 틱 = 0, 할일들 = [], 도는
|
|
|
680
699
|
* 꺼 둔다 — 다섯이 동시에 돌고 있다고 말하면 그건 거짓말이라서다.
|
|
681
700
|
* 켜진 화면이 지금 도는 것, 꺼진 화면이 아직 차례가 안 온 것이다.
|
|
682
701
|
*/
|
|
683
|
-
|
|
702
|
+
/*
|
|
703
|
+
* 도는 중(doing)인 일은 빼야 한다. 그 일은 바로 위에서 **켜진 화면**으로 이미 앉혔다.
|
|
704
|
+
* 또 앉히면 한 사람이 둘이 되고, 바로 이 문단이 못 박은 「꺼진 화면은 아직 차례가 안 온 것」
|
|
705
|
+
* 이 거짓이 된다 (2.0.0 6회차 사무실6ck-f OF4). 화이트보드 쪽지 셈(아래 할일)은 그대로 —
|
|
706
|
+
* 거기서는 도는 일도 아직 남은 일이다.
|
|
707
|
+
*
|
|
708
|
+
* 다만 그건 **앉힌 사람이 있을 때** 하는 말이다. 일감이 없으면 위 고리가 통째로
|
|
709
|
+
* 안 돌아 켜진 화면이 하나도 안 생기는데, 여기서만 doing 을 빼고 있었다. 그러면
|
|
710
|
+
* 하던 일이 방에서 **증발한다** — 화이트보드에는 쪽지가 붙어 있는데 방은 텅 비어,
|
|
711
|
+
* 같은 화면이 두 말을 한다. 뺄 사람이 없으면 빼지 않는다.
|
|
712
|
+
*/
|
|
713
|
+
const 아직안잡은일 = 할일들
|
|
714
|
+
.filter((t) => t.state !== 'done' && !(일감 && t.state === 'doing')).length;
|
|
715
|
+
for (let i = 0; i < Math.max(0, 아직안잡은일); i++) {
|
|
684
716
|
일꾼들.push({ 갈래: '기다림' });
|
|
685
717
|
}
|
|
686
718
|
|
package/src/ui/pastechip.js
CHANGED
|
@@ -37,8 +37,9 @@ export function 크기말(바이트) {
|
|
|
37
37
|
/**
|
|
38
38
|
* 이 덩이가 상자에 안 들어가나.
|
|
39
39
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
40
|
+
* 개행이 하나라도 있으면 줄 수를 안 따지고 접는다 (아래 까닭). 개행이 없는
|
|
41
|
+
* 한 덩이는 들어가면 접지 않는다 — 상자에 보이는 편이 낫다. 그 들어가는지를
|
|
42
|
+
* 줄 수만 세서 보면 안 된다. 한 줄이어도 폭보다 길면 상자 안에서 여러 줄로
|
|
42
43
|
* 접히기 때문이다. 그래서 상자가 쓰는 접어쓰기 로 똑같이 세어 본다.
|
|
43
44
|
*/
|
|
44
45
|
export function 접을까(글, { 폭 = 80, 최대 = 안쪽최대 } = {}) {
|
package/src/ui/pick.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
// 2 - 파일은 나눠서 해줘
|
|
19
19
|
// 1,3 (둘 다)
|
|
20
20
|
// 1 3 둘 다 하되 순서는 반대로
|
|
21
|
+
// 2번 파일은 나눠서 (한국 사람은 번호를 「2번」 이라고 친다)
|
|
21
22
|
//
|
|
22
23
|
// ── 어디서 멈추나 ───────────────────────────────────────────────────────
|
|
23
24
|
//
|
|
@@ -26,15 +27,34 @@
|
|
|
26
27
|
// 사람이 고른 적 없는 것을 골랐다고 하는 셈이라, 안 받느니만 못하다.
|
|
27
28
|
// 그래서 수 바로 뒤에 글자가 붙어 있으면(3개) 수로 안 읽는다.
|
|
28
29
|
//
|
|
30
|
+
// 「번」 하나만 그 선 안으로 들인다. 「2번」 을 못 읽으면 가장 흔한 답이 고른 것 없는 글이
|
|
31
|
+
// 된다. 「번」 뒤에도 끝이나 구분표가 와야 한다 — 「2번째로」 · 「2번으로 해줘」 는 여태처럼
|
|
32
|
+
// 사람 말로 둔다(골랐다고 우기는 쪽이 더 나쁘다).
|
|
33
|
+
//
|
|
29
34
|
// 목록에 없는 수도 안 읽는다. 선택지가 둘인데 「5분만 기다려」 라고 치면
|
|
30
35
|
// 그건 고른 것이 아니라 하는 말이다.
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
/*
|
|
38
|
+
* 수 뒤에 올 수 있는 것 — 끝, 빈칸, 그리고 흔한 구분표. `1.5` 는 수가 아니다.
|
|
39
|
+
*
|
|
40
|
+
* 「번」 앞의 빈칸도 받는다. 「1 번」 이라고 치면 「번」 이 하고 싶은 말로 남아 모델에게
|
|
41
|
+
* 「가 — 번」 이 갔다 (2.0.0 6회차 고르기6cg PK1).
|
|
42
|
+
*
|
|
43
|
+
* 붙임표도 구분표다. 머리말이 적어 둔 「2 - 나눠서」 는 읽으면서 빈칸 없는 「2- 나눠서」 는
|
|
44
|
+
* 아무것도 못 골랐다. 다만 뒤가 숫자면 안 읽는다 — 「2-3」 은 범위로 친 말이지 둘을 고른 것이
|
|
45
|
+
* 아니다 (PK2).
|
|
46
|
+
*
|
|
47
|
+
* 「번」 뒤도 마찬가지다. 전방탐색에 붙임표가 빠져 있어서 「2번- 나눠서」 만 아무것도 못 골랐다 —
|
|
48
|
+
* 「2- 나눠서」 는 읽는데 「번」 을 하나 더 친 사람만 고른 것이 사라진다 (8회차 PK4).
|
|
49
|
+
*/
|
|
50
|
+
const 수뒤 = /^(\d{1,3})(?:\s*번(?=[\s,,、·.)::\]\-–—]|$))?(?:\s*[.)::\]](?!\d)|\s*[,,、·]|\s*[-–—](?!\d)|\s|$)/;
|
|
34
51
|
|
|
35
52
|
/** 수와 말 사이에 사람이 흔히 넣는 것들. 말 앞머리에서 떼어 낸다. */
|
|
36
53
|
const 사이표 = /^[\s,,、·::\-–—>]+/;
|
|
37
54
|
|
|
55
|
+
/** 전각 숫자(0–9)를 보통 숫자로. 일본어·중국어 입력기는 번호를 이렇게 낸다. */
|
|
56
|
+
const 숫자펴기 = (s) => String(s ?? '').replace(/[0-9]/g, (d) => String.fromCharCode(d.charCodeAt(0) - 0xfee0));
|
|
57
|
+
|
|
38
58
|
/**
|
|
39
59
|
* 답 한 줄을 「고른 번호들」과 「덧붙인 말」로 가른다.
|
|
40
60
|
*
|
|
@@ -49,7 +69,7 @@ export function 고른것풀기(답, 고를것 = []) {
|
|
|
49
69
|
if (!원) return { 번호들: [], 덧말: '', 글: '' };
|
|
50
70
|
|
|
51
71
|
const 번호들 = [];
|
|
52
|
-
let 남은 =
|
|
72
|
+
let 남은 = 숫자펴기(원);
|
|
53
73
|
for (;;) {
|
|
54
74
|
const m = 수뒤.exec(남은);
|
|
55
75
|
if (!m) break;
|
|
@@ -70,15 +90,63 @@ export function 고른것풀기(답, 고를것 = []) {
|
|
|
70
90
|
return { 번호들, 덧말, 글 };
|
|
71
91
|
}
|
|
72
92
|
|
|
93
|
+
/*
|
|
94
|
+
* 계획 상자 첫 낱말을 표와 견주기 전에 다듬는다.
|
|
95
|
+
*
|
|
96
|
+
* 글자 그대로 견줬더니 「ok.」 「네.」 「yes!」 가 표에 없는 글이 되어 「그 말대로 다시 짜라」 로
|
|
97
|
+
* 갔다. 자음을 두 번 친 「ㅇㅇ」 「ㄴㄴ」 도 그랬다 — 그러라고 했는데 계획이 또 나오고, 그만하라고
|
|
98
|
+
* 했는데 또 나온다.
|
|
99
|
+
*
|
|
100
|
+
* 떼는 것은 끝의 마침표·느낌표·물결뿐이다. **물음표는 안 뗀다** — 「네?」 「ok?」 는 되묻는
|
|
101
|
+
* 말이지 그러라는 말이 아니다. 애매하면 여태처럼 「다시」 로 두는 편이 낫다(계획을 한 번 더
|
|
102
|
+
* 보는 것이, 안 시킨 일을 하는 것보다 싸다).
|
|
103
|
+
*/
|
|
104
|
+
function 낱말다듬기(낱말) {
|
|
105
|
+
return String(낱말 ?? '').toLowerCase()
|
|
106
|
+
.replace(/[.!~。!]+$/u, '')
|
|
107
|
+
.replace(/^(ㅇ|ㄴ|ㄱ)\1+$/u, '$1');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* ── 멈추라는 말 · 진행을 뒤집는 말 (Gemini 화면4) ──────────────────────────
|
|
112
|
+
*
|
|
113
|
+
* 첫 낱말만 표와 견줬다. 그래서 두 자리가 틀렸다.
|
|
114
|
+
* · 「그만」 「멈춰」 「stop」 「cancel」 이 멈춤 표에 없어서 「그 말대로 다시 짜라」 로 갔다 —
|
|
115
|
+
* 그만하라는데 계획이 또 나온다.
|
|
116
|
+
* · 첫 낱말이 진행 쪽이면 뒤는 「진행하되 한마디」 로 얹었다. 「ok 말고」 「진행 하지 마」
|
|
117
|
+
* 「응 취소」 「y 아니 멈춰」 가 전부 **계획 실행**으로 갔다 — 안 시킨 일을 한다.
|
|
118
|
+
*
|
|
119
|
+
* 뒤에 부정·멈춤 말이 붙은 진행은 뜻이 둘로 읽힌다. 멈춤으로 못 박지도 않는다(「y 테스트는 하지
|
|
120
|
+
* 말고」 는 진행하되 빼라는 말일 수 있다). 여태 원칙대로 「다시」 — 계획을 한 번 더 보는 것이
|
|
121
|
+
* 안 시킨 일을 하는 것보다 싸다. 영어 낱말은 낱말 경계로만 본다 — 「note」 의 not 에 안 걸리게.
|
|
122
|
+
*/
|
|
123
|
+
const 멈춤낱말 = ['n', 'no', 'nope', 'ㄴ', '아니', '아니요', '아뇨', '취소', '그만', '그만해', '멈춰', '중단', '종료', '중지', '포기', 'stop', 'cancel', 'abort', 'quit'];
|
|
124
|
+
const 뒤집는말 = /말고|하지\s*마|하지\s*말|안\s*돼|안\s*되|멈춰|멈추|그만|취소|중단|종료|포기|중지|아니|아뇨|잠깐|(?:^|[^a-z])(?:no|not|nope|don['’]?t|abort|quit|halt|stop|cancel|wait)(?![a-z])/i;
|
|
125
|
+
/*
|
|
126
|
+
* 멈추라는 말을 **뒤집는** 말. 「취소 말고」 「그만 말고」 는 멈추라는 말이 아니다.
|
|
127
|
+
*
|
|
128
|
+
* 위 뒤집는말 을 그대로 쓰면 안 된다 — 거기에는 멈춤 말이 다 들어 있어서 「no 그만해」 처럼
|
|
129
|
+
* 두 번 멈추라는 답까지 「다시」 로 가고, 그만하라는데 계획이 또 나온다. 그래서 여기서는
|
|
130
|
+
* 부정하는 말만 본다. 진행 쪽에 이미 있는 원칙(애매하면 다시)을 멈춤 쪽에도 맞춘 것이다
|
|
131
|
+
* (2.0.0 6회차 고르기6cg PK3).
|
|
132
|
+
*/
|
|
133
|
+
const 부정말 = /말고|하지\s*마|하지\s*말|안\s*돼|안\s*되/;
|
|
134
|
+
/*
|
|
135
|
+
* 그러라는 말. 「진행해」 「좋아」 「ㄱㄱ」 「okay」 가 표에 없어서 「그 말대로 다시 짜라」 로 갔다 —
|
|
136
|
+
* 그러라고 했는데 계획이 또 나온다(Gemini 화면5). 멈추라는 말만큼 비싸지는 않지만 한 번 더 도는 값을 치른다.
|
|
137
|
+
*/
|
|
138
|
+
const 진행낱말 = ['y', 'yes', 'ㅇ', '응', '네', '예', '진행', '진행해', '진행해줘', '좋아', '좋아요', '좋습니다', '그래', '그래요',
|
|
139
|
+
'ok', 'okay', 'sure', 'go', 'ㄱ', '고', '고고'];
|
|
140
|
+
|
|
73
141
|
/**
|
|
74
142
|
* 계획 상자의 답을 읽는다.
|
|
75
143
|
*
|
|
76
144
|
* 갈래는 셋(진행·그만·다시)이고, 들어오는 모양은 다섯이다.
|
|
77
|
-
* · 그냥 진행 — ⏎ · y · yes · ㅇ · 진행
|
|
145
|
+
* · 그냥 진행 — ⏎ · y · yes · ㅇ · ㅇㅇ · 응 · 진행 (끝의 . ! 는 떼고 본다)
|
|
78
146
|
* · 진행하되 한마디 — `y 검사도 같이 넣어줘`
|
|
79
|
-
* · 그만 — n · no · ㄴ
|
|
80
|
-
* · 그 밖의 글 — 그 말대로 다시 짜라 (여태 그대로)
|
|
81
|
-
* · `3 이건 빼자` — 3단계에 대한 말로 붙여서 다시 짜라
|
|
147
|
+
* · 그만 — n · no · ㄴ · ㄴㄴ · 그만 · 멈춰 · 취소 · stop · cancel
|
|
148
|
+
* · 그 밖의 글 — 그 말대로 다시 짜라 (여태 그대로). 진행 낱말 뒤에 뒤집는 말이 붙어도(「ok 말고」) 여기다
|
|
149
|
+
* · `3 이건 빼자` — 3단계에 대한 말로 붙여서 다시 짜라 (`3번 이건 빼자` 도)
|
|
82
150
|
*
|
|
83
151
|
* ⏎ 하나로 진행하는 것은 안 건드린다. 계획을 읽고 「그래」 하는 데
|
|
84
152
|
* 두 글자를 더 치게 하면, 그 두 글자 때문에 안 읽고 넘기게 된다.
|
|
@@ -91,22 +159,27 @@ export function 계획답풀기(답, 단계수 = 0) {
|
|
|
91
159
|
const 원 = String(답 ?? '').trim();
|
|
92
160
|
if (!원) return { 갈래: '진행', 덧말: '', 단계: null };
|
|
93
161
|
|
|
94
|
-
const 첫낱말 = 원.split(/\s+/)[0]
|
|
162
|
+
const 첫낱말 = 낱말다듬기(원.split(/\s+/)[0]);
|
|
95
163
|
const 나머지 = 원.slice(원.split(/\s+/)[0].length).replace(사이표, '').trim();
|
|
96
164
|
|
|
97
|
-
if (
|
|
165
|
+
if (멈춤낱말.includes(첫낱말)) {
|
|
166
|
+
// 「취소 말고」 「그만 말고」 — 첫 낱말은 멈춤인데 뒤가 뒤집는다. 진행 쪽과 같이 「다시」 로 둔다.
|
|
167
|
+
if (부정말.test(나머지)) return { 갈래: '다시', 덧말: 원, 단계: null };
|
|
98
168
|
return { 갈래: '그만', 덧말: '', 단계: null };
|
|
99
169
|
}
|
|
100
|
-
if (
|
|
170
|
+
if (진행낱말.includes(첫낱말)) {
|
|
171
|
+
// 「ok 말고」 「응 취소」 — 첫 낱말은 진행인데 뒤가 뒤집는다. 애매하면 다시 (위 뒤집는말 머리말).
|
|
172
|
+
if (뒤집는말.test(나머지)) return { 갈래: '다시', 덧말: 원, 단계: null };
|
|
101
173
|
return { 갈래: '진행', 덧말: 나머지, 단계: null };
|
|
102
174
|
}
|
|
103
175
|
|
|
104
176
|
// 「3 이건 빼자」 — 그 단계에 대고 하는 말이다.
|
|
105
|
-
const
|
|
177
|
+
const 펴진 = 숫자펴기(원);
|
|
178
|
+
const m = 수뒤.exec(펴진);
|
|
106
179
|
if (m) {
|
|
107
180
|
const n = Number(m[1]);
|
|
108
181
|
if (Number.isInteger(n) && n >= 1 && n <= 단계수) {
|
|
109
|
-
const 말 =
|
|
182
|
+
const 말 = 펴진.slice(m[0].length).replace(사이표, '').trim();
|
|
110
183
|
if (말) return { 갈래: '다시', 덧말: 말, 단계: n };
|
|
111
184
|
}
|
|
112
185
|
}
|