deel-local-cli 1.5.2 → 1.5.3
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 +5 -3
- package/README.md +5 -3
- package/package.json +1 -1
- package/src/commands.js +99 -0
- package/src/i18n/en.js +16 -1
- package/src/i18n/index.js +52 -3
- package/src/i18n/ko.js +18 -3
- package/src/repl.js +5 -1
- package/src/ui/motion.js +20 -1
- package/src/ui/office.js +15 -2
- package/src/ui/screen.js +17 -0
package/README.en.md
CHANGED
|
@@ -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.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.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
|
|
|
@@ -388,6 +388,7 @@ Names follow Claude Code / Codex conventions.
|
|
|
388
388
|
| `/auto` | Hand the wheel back — it picks the mode from what you type |
|
|
389
389
|
| `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | Switch work mode directly (pins it) |
|
|
390
390
|
| `/level [level]` | How much to show (`쉬움` simple · `개발자` developer) |
|
|
391
|
+
| `/motion [plain\|knight\|animal\|office\|off]` | What animates while it works — takes effect at once, and is saved |
|
|
391
392
|
| `/undo [turns]` | Revert file changes |
|
|
392
393
|
| `/diff [file]` | Files changed this session, and the changed lines |
|
|
393
394
|
| `/preview [folder\|file\|off]` | Serve what you built, right here — a browser opens with it |
|
|
@@ -928,7 +929,7 @@ Stored in `~/.deel/config.json`. A `.deel/config.json` in the project folder tak
|
|
|
928
929
|
## Development
|
|
929
930
|
|
|
930
931
|
```bash
|
|
931
|
-
npm test Full suite (
|
|
932
|
+
npm test Full suite (3,667 checks)
|
|
932
933
|
npm run coverage Which lines the tests actually execute
|
|
933
934
|
npm run verify Import + network checks only
|
|
934
935
|
npm run bench Edit success rate
|
|
@@ -980,7 +981,8 @@ so one run tells you everything.
|
|
|
980
981
|
|
|
981
982
|
| Version | What changed |
|
|
982
983
|
|---|---|
|
|
983
|
-
| **[1.5.
|
|
984
|
+
| **[1.5.3](docs/en/releases.md#153)** | Three things a Mac user tripped over — newline hint, office stays put, one `/motion` |
|
|
985
|
+
| [1.5.2](docs/en/releases.md#152) | Everything else the adversarial reviews turned up — where the screen was lying |
|
|
984
986
|
| [1.5.1](docs/en/releases.md#151) | Two of the things 1.5.0 added shipped broken. This fixes them |
|
|
985
987
|
| [1.5.0](docs/en/releases.md#150) | Line breaks exist now, the screen got fun, and it came out lighter than before |
|
|
986
988
|
| [1.4.3](docs/en/releases.md#143) | The README explains what's different, and the review report gets its missing line |
|
package/README.md
CHANGED
|
@@ -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.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.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
|
|
|
@@ -366,6 +366,7 @@ LM Studio 는 `/api/v0/models`, llama.cpp 는 `/props`. 못 알아보면 `(추
|
|
|
366
366
|
| `/auto` | 다시 맡기기 — 말을 보고 알맞은 모드로 저절로 옮겨 갑니다 |
|
|
367
367
|
| `/code` `/plan` `/architect` `/debug` `/ask` `/orchestrator` | 작업 모드 바로 바꾸기 (그때부터 고정) |
|
|
368
368
|
| `/level [수준]` | 화면에 무엇을 내놓을지 (`쉬움` · `개발자`) |
|
|
369
|
+
| `/motion [기본\|기사\|동물\|사무실\|끔]` | 일하는 동안 뭐가 도나 — 그 자리에서 바뀌고 설정에 남습니다 |
|
|
369
370
|
| `/undo [턴수]` | 파일 변경 되돌리기 |
|
|
370
371
|
| `/diff [파일]` | 이번 대화에서 바뀐 파일 · 바뀐 자리 보기 |
|
|
371
372
|
| `/preview [폴더\|파일\|off]` | 만든 웹을 이 자리에서 띄워 보기 — 브라우저가 같이 열립니다 |
|
|
@@ -906,7 +907,7 @@ deel sbom --only sbom # SBOM 한 장만
|
|
|
906
907
|
## 개발
|
|
907
908
|
|
|
908
909
|
```bash
|
|
909
|
-
npm test 전체 검증 (
|
|
910
|
+
npm test 전체 검증 (3,667항목)
|
|
910
911
|
npm run coverage 검사가 소스의 어디를 밟았는지
|
|
911
912
|
npm run verify 반입·통신 검증만
|
|
912
913
|
npm run bench 편집 성공률 측정
|
|
@@ -971,7 +972,8 @@ zip 은 진짜 `unzip` 으로, tar 는 진짜 `tar` 가 만든 것을 읽혀 교
|
|
|
971
972
|
|
|
972
973
|
| 판 | 무엇이 바뀌었나 |
|
|
973
974
|
|---|---|
|
|
974
|
-
| **[1.5.
|
|
975
|
+
| **[1.5.3](docs/ko/releases.md#153)** | 맥에서 걸린 세 가지 — 줄바꿈 안내 · 사무실이 안 사라짐 · `/motion` 하나로 |
|
|
976
|
+
| [1.5.2](docs/ko/releases.md#152) | 적대적 리뷰가 남긴 나머지를 전부 정리했다 — 화면이 거짓말하던 자리들 |
|
|
975
977
|
| [1.5.1](docs/ko/releases.md#151) | 1.5.0 이 넣은 것 두 개가 망가진 채 나갔다. 그것을 고친다 |
|
|
976
978
|
| [1.5.0](docs/ko/releases.md#150) | 줄바꿈이 생겼고, 화면이 재미있어졌고, 그러면서 오히려 가벼워졌다 |
|
|
977
979
|
| [1.4.3](docs/ko/releases.md#143) | README 가 왜 다른지 말하고, 심사서의 빠진 한 줄을 채운다 |
|
package/package.json
CHANGED
package/src/commands.js
CHANGED
|
@@ -19,6 +19,27 @@ import { 보고서적기 } from './ui/export.js';
|
|
|
19
19
|
import { loadCommand, discover } from './skills/discover.js';
|
|
20
20
|
import { install, list, remove, pack } from './plugins/manage.js';
|
|
21
21
|
import { spin } from './ui/spinner.js';
|
|
22
|
+
import { 그림고르기설정, 끔설정 } from './ui/motion.js';
|
|
23
|
+
import { 사무실설정, 최소높이, 최소폭 } from './ui/office.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 설정에 적힌 그림을 화면 쪽에 물린다.
|
|
27
|
+
*
|
|
28
|
+
* 켤 때(repl·oneshot)와 `/motion` 으로 바꿀 때가 **같은 길을 타야** 한다.
|
|
29
|
+
* 갈라 두면 명령으로 바꾼 것이 다음에 켤 때 안 살아나거나 그 반대가 된다.
|
|
30
|
+
*
|
|
31
|
+
* 사무실은 그림 하나가 아니라 화면 아래를 통째로 쓰는 것이라 따로 켠다.
|
|
32
|
+
* '사무실' 을 골랐으면 상자 안 그림은 조용한 돌림표로 돌아간다 — 둘 다
|
|
33
|
+
* 같은 것을 말하므로 나란히 두면 같은 소리를 두 번 하는 셈이다.
|
|
34
|
+
*/
|
|
35
|
+
export function 적용하기(고른것) {
|
|
36
|
+
const 값 = String(고른것 ?? '기본');
|
|
37
|
+
사무실설정(값 === '사무실');
|
|
38
|
+
그림고르기설정(값 === '사무실' ? '기본' : 값);
|
|
39
|
+
// 환경변수(DEEL_NO_MOTION)는 안 건드린다 — 사람이 직접 넣어 둔 것을 명령이
|
|
40
|
+
// 지워 버리면, 껐다고 믿는 자리에서 그림이 다시 돈다. 설정용 값은 따로 둔다.
|
|
41
|
+
끔설정(값 === '끔');
|
|
42
|
+
}
|
|
22
43
|
import { PROFILES, LEVELS as THINK_LEVELS, normalizeProfile, table as effortTable } from './agent/effort.js';
|
|
23
44
|
import { scanLocal, toProfiles } from './backend/scan.js';
|
|
24
45
|
import { list as listSessions } from './agent/store.js';
|
|
@@ -89,6 +110,7 @@ const 명령들 = {
|
|
|
89
110
|
memory: { arg: true },
|
|
90
111
|
level: { arg: true },
|
|
91
112
|
bell: { arg: true },
|
|
113
|
+
motion: { arg: true },
|
|
92
114
|
consult: { arg: true },
|
|
93
115
|
lang: { arg: true },
|
|
94
116
|
lsp: { arg: true },
|
|
@@ -431,6 +453,83 @@ export async function handle(line, session, ctx) {
|
|
|
431
453
|
say('');
|
|
432
454
|
return { handled: true };
|
|
433
455
|
}
|
|
456
|
+
/*
|
|
457
|
+
* 일하는 동안 뭐가 도나.
|
|
458
|
+
*
|
|
459
|
+
* 예전에는 DEEL_MOTION 과 DEEL_OFFICE 두 환경변수였다. 켜 보려면 터미널을
|
|
460
|
+
* 껐다 켜야 했고, 이름도 둘을 따로 외워야 했다 — 재미로 넣은 것을 켜는 데
|
|
461
|
+
* 그만한 품이 들면 아무도 안 켠다. 명령 하나로 합치고 그 자리에서 바뀐다.
|
|
462
|
+
*/
|
|
463
|
+
case 'motion': {
|
|
464
|
+
const cfg = load();
|
|
465
|
+
const 고를것 = [
|
|
466
|
+
{ 값: '기본', 별명: ['기본', 'default', 'plain', '돌림표'], 설명: 말('motion.plain') },
|
|
467
|
+
{ 값: '기사', 별명: ['기사', 'knight'], 설명: 말('motion.knight') },
|
|
468
|
+
{ 값: '동물', 별명: ['동물', 'animal', 'animals'], 설명: 말('motion.animal') },
|
|
469
|
+
{ 값: '사무실', 별명: ['사무실', 'office'], 설명: 말('motion.office') },
|
|
470
|
+
{ 값: '끔', 별명: ['끔', 'off', '꺼', '끄기', 'none'], 설명: 말('motion.off') },
|
|
471
|
+
];
|
|
472
|
+
const 지금값 = cfg.motion ?? '기본';
|
|
473
|
+
const 값 = String(arg ?? '').trim().toLowerCase();
|
|
474
|
+
|
|
475
|
+
if (!값) {
|
|
476
|
+
say('');
|
|
477
|
+
say(` ${c.gray(말('motion.now'))} ${c.hcyan(지금값)}`);
|
|
478
|
+
say('');
|
|
479
|
+
for (const 것 of 고를것) {
|
|
480
|
+
const 표 = 것.값 === 지금값 ? c.hgreen('●') : c.gray('○');
|
|
481
|
+
// padEnd 가 아니라 pad — 한글은 한 글자가 두 칸이라
|
|
482
|
+
// 글자 수로 채우면 '사무실' 줄만 두 칸 밀린다.
|
|
483
|
+
say(` ${표} ${c.cyan(pad(`/motion ${것.값}`, 18))} ${c.gray(것.설명)}`);
|
|
484
|
+
}
|
|
485
|
+
say('');
|
|
486
|
+
// 사무실은 화면이 좁거나 낮으면 안 뜬다. 켜 놓고 안 보이면 고장으로
|
|
487
|
+
// 보이므로, 지금 이 터미널이 되는지를 여기서 미리 말해 준다.
|
|
488
|
+
const 줄 = process.stdout.rows ?? 0;
|
|
489
|
+
const 칸 = process.stdout.columns ?? 0;
|
|
490
|
+
if (줄 && 칸 && (줄 < 최소높이 || 칸 < 최소폭)) {
|
|
491
|
+
say(` ${c.yellow(mark.warn)} ${c.gray(말('motion.tooSmall', { 줄, 칸, 최소줄: 최소높이, 최소칸: 최소폭 }))}`);
|
|
492
|
+
say('');
|
|
493
|
+
}
|
|
494
|
+
if (process.env.DEEL_MOTION || process.env.DEEL_OFFICE) {
|
|
495
|
+
say(` ${c.gray(말('motion.envWins'))}`);
|
|
496
|
+
say('');
|
|
497
|
+
}
|
|
498
|
+
return { handled: true };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
const 고른것 = 고를것.find((것) => 것.별명.includes(값));
|
|
502
|
+
if (!고른것) {
|
|
503
|
+
say('');
|
|
504
|
+
say(` ${c.gray(말('motion.unknown', { 값: arg }))}`);
|
|
505
|
+
say(` ${c.gray(말('motion.pickOne'))} ${고를것.map((것) => c.cyan(것.값)).join(c.gray(' · '))}`);
|
|
506
|
+
say('');
|
|
507
|
+
return { handled: true };
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
cfg.motion = 고른것.값;
|
|
511
|
+
try { save(cfg); } catch { /* 못 남겨도 이번 세션에는 먹는다 */ }
|
|
512
|
+
적용하기(cfg.motion);
|
|
513
|
+
|
|
514
|
+
say('');
|
|
515
|
+
say(` ${mark.ok} ${말('motion.set', { 것: 고른것.값 })} ${c.gray('— ' + 고른것.설명)}`);
|
|
516
|
+
// 사무실을 골랐는데 이 터미널이 작으면 아무 일도 안 일어난 것처럼 보인다.
|
|
517
|
+
// 켰다고 말해 놓고 안 보이면 그게 고장이다. 그래서 그 자리에서 말한다.
|
|
518
|
+
if (고른것.값 === '사무실') {
|
|
519
|
+
const 줄 = process.stdout.rows ?? 0;
|
|
520
|
+
const 칸 = process.stdout.columns ?? 0;
|
|
521
|
+
if (줄 && 칸 && (줄 < 최소높이 || 칸 < 최소폭)) {
|
|
522
|
+
say(` ${c.yellow(mark.warn)} ${c.gray(말('motion.tooSmall', { 줄, 칸, 최소줄: 최소높이, 최소칸: 최소폭 }))}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
if (process.env.DEEL_MOTION || process.env.DEEL_OFFICE) {
|
|
526
|
+
say(` ${c.yellow(mark.warn)} ${c.gray(말('motion.envWins'))}`);
|
|
527
|
+
}
|
|
528
|
+
say(` ${c.gray(말('motion.appliesNow'))}`);
|
|
529
|
+
say('');
|
|
530
|
+
return { handled: true };
|
|
531
|
+
}
|
|
532
|
+
|
|
434
533
|
case 'exit':
|
|
435
534
|
case 'quit': return { handled: true, exit: true };
|
|
436
535
|
|
package/src/i18n/en.js
CHANGED
|
@@ -82,6 +82,8 @@ export const en = {
|
|
|
82
82
|
'cmd.level.arg': '[level]',
|
|
83
83
|
'cmd.bell.desc': 'bell and window title when a turn ends or deel needs you (on/off)',
|
|
84
84
|
'cmd.bell.arg': '[on|off]',
|
|
85
|
+
'cmd.motion.desc': 'what moves while deel works — knight, animals, office',
|
|
86
|
+
'cmd.motion.arg': '[knight|animal|office|default|off]',
|
|
85
87
|
'cmd.lang.desc': 'screen language — 한국어 / English',
|
|
86
88
|
'cmd.lang.arg': '[ko|en]',
|
|
87
89
|
'cmd.init.desc': 'create a DEEL.md rules file',
|
|
@@ -129,7 +131,7 @@ export const en = {
|
|
|
129
131
|
'head.shiftTab': 'Shift+Tab',
|
|
130
132
|
'head.shiftTabHint': ' changes it · ',
|
|
131
133
|
'head.tabHint': 'Tab completes the / command you are typing',
|
|
132
|
-
'head.newlineHint': 'New line:
|
|
134
|
+
'head.newlineHint': 'New line: end a line with space + ` then Enter — works everywhere (Alt+Enter only if your terminal sends it)',
|
|
133
135
|
'head.thisPC': 'found',
|
|
134
136
|
'head.skills': 'skills {n}',
|
|
135
137
|
'head.commands': 'commands {n}',
|
|
@@ -184,6 +186,19 @@ export const en = {
|
|
|
184
186
|
'bell.justRang': 'That was one ring. If you heard nothing, check your terminal’s bell setting.',
|
|
185
187
|
'bell.appliesNow': 'Applies to this session right away.',
|
|
186
188
|
|
|
189
|
+
'motion.now': 'Now:',
|
|
190
|
+
'motion.plain': 'a one-cell spinner. Quiet',
|
|
191
|
+
'motion.knight': 'a knight swings a sword. Sometimes gets flattened',
|
|
192
|
+
'motion.animal': 'pixel animals wander about',
|
|
193
|
+
'motion.office': 'twelve rows of office above the prompt — the work as a room',
|
|
194
|
+
'motion.off': 'no drawing at all (for screen-reader users)',
|
|
195
|
+
'motion.set': 'Switched to {것}.',
|
|
196
|
+
'motion.appliesNow': 'Visible from the next frame. Saved to your config too.',
|
|
197
|
+
'motion.unknown': '"{값}" is not one of the drawings.',
|
|
198
|
+
'motion.pickOne': 'Pick one of:',
|
|
199
|
+
'motion.envWins': 'DEEL_MOTION / DEEL_OFFICE is set, and the environment wins.',
|
|
200
|
+
'motion.tooSmall': 'This terminal is {줄} x {칸} — the office needs at least {최소줄} rows and {최소칸} columns.',
|
|
201
|
+
|
|
187
202
|
// ── Screen language ───────────────────────────────────────────────────
|
|
188
203
|
'lang.now': 'Screen language is {이름}.',
|
|
189
204
|
'lang.ko': '한국어 (Korean)',
|
package/src/i18n/index.js
CHANGED
|
@@ -70,11 +70,54 @@ export function 언어잡기({ env = process.env, cfg = null } = {}) {
|
|
|
70
70
|
return 지금;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
/*
|
|
74
|
+
* 조사는 앞 글자의 받침을 보고 갈린다.
|
|
75
|
+
*
|
|
76
|
+
* 끼워 넣는 값이 그때그때 달라지는 자리에서 조사를 글에 박아 두면
|
|
77
|
+
* "기사 으로 바꿨습니다" 같은 것이 나온다. 안 터지고, 검사도 안 잡고,
|
|
78
|
+
* 화면에서만 어색하다 — 그래서 오래 남는다.
|
|
79
|
+
*
|
|
80
|
+
* `{것:으로}` 처럼 자리 이름 뒤에 조사를 적으면 값을 보고 골라 붙인다.
|
|
81
|
+
* 영어 글에는 안 적으면 그만이라 `{것}` 그대로 둬도 된다.
|
|
82
|
+
*
|
|
83
|
+
* 값과 조사 사이에 뭔가 끼어야 할 때 — 따옴표가 대표적이다 — 이름 앞에
|
|
84
|
+
* `~` 를 붙이면 **조사만** 나온다: `"{값}"{~값:은는}` → `"없는거"는`.
|
|
85
|
+
*/
|
|
86
|
+
const 조사표 = {
|
|
87
|
+
은는: ['은', '는'],
|
|
88
|
+
이가: ['이', '가'],
|
|
89
|
+
을를: ['을', '를'],
|
|
90
|
+
과와: ['과', '와'],
|
|
91
|
+
으로: ['으로', '로'],
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* 이 값 뒤에 붙일 조사.
|
|
96
|
+
*
|
|
97
|
+
* 한글이 아닌 것으로 끝나면(영어 낱말·기호) **받침 없는 쪽**을 씁니다.
|
|
98
|
+
* 한국어 화면에 들어오는 값은 거의 한글이라 여기까지 오는 일이 드물고,
|
|
99
|
+
* 여기서 더 따지면 규칙만 늘고 맞는 자리는 안 늘어납니다.
|
|
100
|
+
*/
|
|
101
|
+
export function 조사붙이기(값, 갈래) {
|
|
102
|
+
const 짝 = 조사표[갈래];
|
|
103
|
+
if (!짝) return '';
|
|
104
|
+
// 색 입힌 값이 그대로 들어온다(`\x1b[92m⌂\x1b[0m`). 벗기지 않으면 마지막
|
|
105
|
+
// 글자가 언제나 리셋 문자열의 'm' 이라, 무슨 값이 와도 답이 하나로 굳는다.
|
|
106
|
+
const 끝 = String(값 ?? '').replace(/\x1b\[[0-9;]*m/g, '').trimEnd().slice(-1);
|
|
107
|
+
const cp = 끝.codePointAt(0);
|
|
108
|
+
if (cp === undefined || cp < 0xac00 || cp > 0xd7a3) return 짝[1];
|
|
109
|
+
const 받침 = (cp - 0xac00) % 28;
|
|
110
|
+
if (받침 === 0) return 짝[1];
|
|
111
|
+
// `으로` 만 다르다. ㄹ(받침 8) 뒤에서는 받침이 없는 것처럼 '로' 가 붙는다.
|
|
112
|
+
if (갈래 === '으로' && 받침 === 8) return 짝[1];
|
|
113
|
+
return 짝[0];
|
|
114
|
+
}
|
|
115
|
+
|
|
73
116
|
/**
|
|
74
117
|
* 말 한 마디.
|
|
75
118
|
*
|
|
76
119
|
* @param {string} 열쇠
|
|
77
|
-
* @param {Record<string, string|number>} [채움] `{이름}` 자리에 끼워 넣을 것
|
|
120
|
+
* @param {Record<string, string|number>} [채움] `{이름}` · `{이름:조사}` 자리에 끼워 넣을 것
|
|
78
121
|
* @returns {string}
|
|
79
122
|
*/
|
|
80
123
|
export function 말(열쇠, 채움 = null) {
|
|
@@ -93,8 +136,14 @@ export function 말(열쇠, 채움 = null) {
|
|
|
93
136
|
* 안 준 자리는 그대로 둔다. 지워 버리면 "0개 턴" 처럼 틀린 말이 되고,
|
|
94
137
|
* 틀린 숫자는 안 적힌 것보다 나쁘다.
|
|
95
138
|
*/
|
|
96
|
-
return String(글).replace(/\{([^{}\s]+)\}/g, (통째,
|
|
97
|
-
|
|
139
|
+
return String(글).replace(/\{([^{}\s:]+)(?::([^{}\s]+))?\}/g, (통째, 이름, 갈래) => {
|
|
140
|
+
const 조사만 = 이름.startsWith('~');
|
|
141
|
+
const 진짜이름 = 조사만 ? 이름.slice(1) : 이름;
|
|
142
|
+
if (!Object.prototype.hasOwnProperty.call(채움, 진짜이름)) return 통째;
|
|
143
|
+
const 값 = String(채움[진짜이름]);
|
|
144
|
+
if (조사만) return 갈래 ? 조사붙이기(값, 갈래) : '';
|
|
145
|
+
return 갈래 ? 값 + 조사붙이기(값, 갈래) : 값;
|
|
146
|
+
});
|
|
98
147
|
}
|
|
99
148
|
|
|
100
149
|
/** 이 열쇠가 지금 언어로 옮겨져 있나. 검사와 /lang 이 본다. */
|
package/src/i18n/ko.js
CHANGED
|
@@ -69,6 +69,8 @@ export const ko = {
|
|
|
69
69
|
'cmd.level.arg': '[수준]',
|
|
70
70
|
'cmd.bell.desc': '다 됐을 때·물어볼 때 종소리와 창 제목 (on/off)',
|
|
71
71
|
'cmd.bell.arg': '[on|off]',
|
|
72
|
+
'cmd.motion.desc': '일하는 동안 뭐가 도나 — 기사·동물·사무실',
|
|
73
|
+
'cmd.motion.arg': '[기사|동물|사무실|기본|끔]',
|
|
72
74
|
'cmd.lang.desc': '화면 말 — 한국어 / English',
|
|
73
75
|
'cmd.lang.arg': '[ko|en]',
|
|
74
76
|
'cmd.init.desc': 'DEEL.md 규칙 파일 만들기',
|
|
@@ -103,7 +105,7 @@ export const ko = {
|
|
|
103
105
|
'head.outside': '바깥',
|
|
104
106
|
'head.offlineLock': '[오프라인 잠금]',
|
|
105
107
|
'head.nowhereElse': '← 여기 말고는 어디로도 안 갑니다',
|
|
106
|
-
'head.glyphHint': '상태줄 맨 앞 {
|
|
108
|
+
'head.glyphHint': '상태줄 맨 앞 {안:이가} 이 뜻입니다. 바깥으로 가면 {밖:으로} 바뀝니다.',
|
|
107
109
|
'head.conn': '연결',
|
|
108
110
|
'head.streaming': '스트리밍',
|
|
109
111
|
'head.noStreaming': '스트리밍 없음',
|
|
@@ -116,7 +118,7 @@ export const ko = {
|
|
|
116
118
|
'head.shiftTab': 'Shift+Tab',
|
|
117
119
|
'head.shiftTabHint': '으로 바꿉니다 · ',
|
|
118
120
|
'head.tabHint': 'Tab 은 치던 / 명령을 채웁니다',
|
|
119
|
-
'head.newlineHint': '줄바꿈:
|
|
121
|
+
'head.newlineHint': '줄바꿈: 줄 끝에 빈칸+` 를 붙이고 Enter — 어디서나 됩니다 (Alt+Enter 는 터미널이 보내 줄 때만)',
|
|
120
122
|
'head.thisPC': '이 PC',
|
|
121
123
|
'head.skills': '스킬 {n}',
|
|
122
124
|
'head.commands': '명령 {n}',
|
|
@@ -171,12 +173,25 @@ export const ko = {
|
|
|
171
173
|
'bell.justRang': '방금 한 번 울렸습니다. 안 들렸으면 터미널 설정에서 소리를 확인해 주세요.',
|
|
172
174
|
'bell.appliesNow': '이 세션에도 바로 먹습니다.',
|
|
173
175
|
|
|
176
|
+
'motion.now': '지금:',
|
|
177
|
+
'motion.plain': '한 칸짜리 돌림표. 조용합니다',
|
|
178
|
+
'motion.knight': '중세 기사가 칼을 휘두릅니다. 가끔 깔려서 넘어집니다',
|
|
179
|
+
'motion.animal': '픽셀 동물들이 돌아다닙니다',
|
|
180
|
+
'motion.office': '입력칸 위에 사무실 12줄. 지금 무슨 일이 도는지를 방으로',
|
|
181
|
+
'motion.off': '그림을 아예 끕니다 (화면 읽기 프로그램을 쓰는 자리)',
|
|
182
|
+
'motion.set': '{것:으로} 바꿨습니다.',
|
|
183
|
+
'motion.appliesNow': '다음 판부터 바로 보입니다. 설정에도 남았습니다.',
|
|
184
|
+
'motion.unknown': '"{값}"{~값:은는} 모르는 그림입니다.',
|
|
185
|
+
'motion.pickOne': '고를 수 있는 것:',
|
|
186
|
+
'motion.envWins': '환경변수(DEEL_MOTION·DEEL_OFFICE)가 켜져 있어 그쪽이 이깁니다.',
|
|
187
|
+
'motion.tooSmall': '이 터미널은 {줄}줄 × {칸}칸입니다 — 사무실은 {최소줄}줄 × {최소칸}칸부터 뜹니다.',
|
|
188
|
+
|
|
174
189
|
// ── 화면 말 (/lang) ───────────────────────────────────────────────────
|
|
175
190
|
'lang.now': '화면 말은 지금 {이름} 입니다.',
|
|
176
191
|
'lang.ko': '한국어',
|
|
177
192
|
'lang.en': 'English (영어)',
|
|
178
193
|
'lang.howto': '바꾸려면',
|
|
179
|
-
'lang.changed': '화면 말을 {
|
|
194
|
+
'lang.changed': '화면 말을 {이름:으로} 바꿨습니다.',
|
|
180
195
|
'lang.unknown': 'ko 나 en 으로 적어 주세요.',
|
|
181
196
|
'lang.progress': '{언어}: {옮김}/{전체} 마디가 옮겨져 있습니다. 나머지는 한국어로 나옵니다.',
|
|
182
197
|
'lang.partial': '아직 다 안 옮겼습니다 — 빈칸을 내는 대신 한국어를 그대로 냅니다.',
|
package/src/repl.js
CHANGED
|
@@ -9,7 +9,7 @@ import { 보이기 as 인트로, 기본곁말 } from './ui/intro.js';
|
|
|
9
9
|
import { 언어잡기 } from './i18n/index.js';
|
|
10
10
|
import { 화면고르기 } from './ui/screen.js';
|
|
11
11
|
import { STAGES } from './agent/effort.js';
|
|
12
|
-
import { handle, COMMANDS,
|
|
12
|
+
import { handle, COMMANDS, 미리보기끄기, 적용하기 as 그림적용 } from './commands.js';
|
|
13
13
|
import { next as nextWork, get as getWork, canWrite, 보일이름, 보일한줄 } from './agent/modes.js';
|
|
14
14
|
import { route } from './agent/route.js';
|
|
15
15
|
import { run } from './agent/loop.js';
|
|
@@ -196,6 +196,10 @@ export async function chatLoop(opts = {}) {
|
|
|
196
196
|
*/
|
|
197
197
|
const 알림 = { 켬: cfg.bell !== false, 폴더: basename(root) };
|
|
198
198
|
|
|
199
|
+
// 지난번에 /motion 으로 골라 둔 그림을 되살린다. 화면을 세우기 **전에**
|
|
200
|
+
// 해야 첫 판부터 맞다 — 뒤에 하면 켜자마자 한 번은 기본 그림이 스친다.
|
|
201
|
+
그림적용(cfg.motion);
|
|
202
|
+
|
|
199
203
|
/*
|
|
200
204
|
* 화면 말을 여기서 한 번 정한다.
|
|
201
205
|
*
|
package/src/ui/motion.js
CHANGED
|
@@ -469,8 +469,24 @@ const 이름표 = {
|
|
|
469
469
|
기사: '기사', knight: '기사', 동물: '동물', animal: '동물', animals: '동물',
|
|
470
470
|
기본: '기본', default: '기본', plain: '기본',
|
|
471
471
|
};
|
|
472
|
+
/*
|
|
473
|
+
* 설정 파일에서 고른 그림. `/motion` 이 여기에 넣는다.
|
|
474
|
+
*
|
|
475
|
+
* **파일을 여기서 읽으면 안 된다.** 이 함수는 90ms 마다 불린다 — 판마다
|
|
476
|
+
* config.json 을 여는 셈이 된다. 그래서 켤 때 한 번 읽어서 이 값에 넣어 두고,
|
|
477
|
+
* 명령으로 바꿀 때 같이 갈아 끼운다.
|
|
478
|
+
*
|
|
479
|
+
* 환경변수가 이긴다. 한 번 띄우는 동안만 다르게 보고 싶을 때
|
|
480
|
+
* `DEEL_MOTION=기사 deel` 로 덮어쓸 수 있어야 하고, 검사도 그 길로 잰다.
|
|
481
|
+
*/
|
|
482
|
+
let 고른그림 = null;
|
|
483
|
+
export function 그림고르기설정(값) {
|
|
484
|
+
고른그림 = 이름표[String(값 ?? '').trim().toLowerCase()] ?? 이름표[String(값 ?? '').trim()] ?? null;
|
|
485
|
+
}
|
|
486
|
+
|
|
472
487
|
export const 테마이름 = () => 이름표[String(process.env.DEEL_MOTION ?? '').trim().toLowerCase()]
|
|
473
488
|
?? 이름표[String(process.env.DEEL_MOTION ?? '').trim()]
|
|
489
|
+
?? 고른그림
|
|
474
490
|
?? '기본';
|
|
475
491
|
export const 테마 = () => 테마들[테마이름()] ?? 테마들.기본;
|
|
476
492
|
|
|
@@ -481,7 +497,10 @@ export const 테마 = () => 테마들[테마이름()] ?? 테마들.기본;
|
|
|
481
497
|
* 자리가 있다. 그럴 때 DEEL_NO_MOTION=1 이면 예전처럼 한 칸짜리 돌림표만 돈다.
|
|
482
498
|
* 아예 못 끄게 해 두면 그런 사람은 도구를 못 쓴다.
|
|
483
499
|
*/
|
|
484
|
-
|
|
500
|
+
let 설정으로껐나 = false;
|
|
501
|
+
/** `/motion 끔` 이 여기에 넣는다. 환경변수는 따로 살아 있고, 둘 중 하나면 꺼진다. */
|
|
502
|
+
export function 끔설정(값) { 설정으로껐나 = !!값; }
|
|
503
|
+
export const 끔 = () => !!process.env.DEEL_NO_MOTION || 설정으로껐나;
|
|
485
504
|
|
|
486
505
|
// 못 끄는 자리에서 쓸 한 칸짜리. working.js 의 돌림틀과 같은 것이다.
|
|
487
506
|
const 한칸 = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
package/src/ui/office.js
CHANGED
|
@@ -350,9 +350,22 @@ export const 최소폭 = 60;
|
|
|
350
350
|
* 정해야지 화면 높이로 정하면 안 된다. 창을 줄였다 늘였다 할 때마다 상자 안
|
|
351
351
|
* 그림이 기사였다 돌림표였다 하면 무슨 규칙인지 아무도 모른다.
|
|
352
352
|
*/
|
|
353
|
+
/*
|
|
354
|
+
* 설정 파일에서 켜 뒀나. `/motion 사무실` 이 여기에 넣는다.
|
|
355
|
+
*
|
|
356
|
+
* motion.js 와 같은 이유로 파일을 여기서 안 읽는다 — 판마다 불리는 자리다.
|
|
357
|
+
* 환경변수가 이긴다(한 번만 다르게 보고 싶을 때, 그리고 검사가 쓰는 길).
|
|
358
|
+
*/
|
|
359
|
+
let 설정으로켰나 = false;
|
|
360
|
+
export function 사무실설정(켬) { 설정으로켰나 = !!켬; }
|
|
361
|
+
|
|
353
362
|
export function 켜달라했나() {
|
|
354
|
-
const
|
|
355
|
-
|
|
363
|
+
const raw = process.env.DEEL_OFFICE;
|
|
364
|
+
if (raw !== undefined) {
|
|
365
|
+
const v = String(raw).trim().toLowerCase();
|
|
366
|
+
return v === '1' || v === 'on' || v === '켬' || v === '사무실';
|
|
367
|
+
}
|
|
368
|
+
return 설정으로켰나;
|
|
356
369
|
}
|
|
357
370
|
|
|
358
371
|
export function 켜나(rows = process.stdout.rows, cols = process.stdout.columns) {
|
package/src/ui/screen.js
CHANGED
|
@@ -243,6 +243,23 @@ export class BoxScreen extends LineScreen {
|
|
|
243
243
|
// 그 다음 지우기가 답까지 같이 지운다.
|
|
244
244
|
this.상자.줄중간표시();
|
|
245
245
|
super.붙임(s);
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
* 줄이 끝나는 토막이면 그 자리에서 다시 세운다.
|
|
249
|
+
*
|
|
250
|
+
* 예전에는 답이 흐르는 내내 상자를 안 세웠다. 상자만 있을 때는 그래도
|
|
251
|
+
* 됐다 — 없어도 답이 흐르는 것 자체가 살아 있다는 신호였으니까.
|
|
252
|
+
* 그런데 **사무실을 켜면 얘기가 다르다.** 붙박아 두라고 켠 것이 정작
|
|
253
|
+
* 일할 때만 사라지니, 켠 사람 눈에는 고장으로 보인다.
|
|
254
|
+
*
|
|
255
|
+
* 그렇다고 토막마다 세우면 글자 몇 개마다 깜빡인다. 그래서 **줄이
|
|
256
|
+
* 끝날 때만** 세운다 — 줄() 이 이미 하고 있는 것과 같은 빈도이고,
|
|
257
|
+
* 그 자리에서는 답을 덮을 일이 없다.
|
|
258
|
+
*/
|
|
259
|
+
if (typeof s === 'string' && s.endsWith('\n')) {
|
|
260
|
+
this.상자.줄끝남();
|
|
261
|
+
this.상자.일그리기();
|
|
262
|
+
}
|
|
246
263
|
}
|
|
247
264
|
|
|
248
265
|
/** 일하기 시작·바뀜·끝. 상자 안엣것만 바뀐다 (ui/working.js 의 문구). */
|