deel-local-cli 1.20.10 → 1.20.11
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 +3 -2
- package/README.md +3 -2
- package/package.json +1 -1
- package/src/agent/loop.js +39 -4
- package/src/agent/session.js +279 -33
- package/src/agent/threads.js +52 -1
- package/src/commands.js +124 -10
- package/src/i18n/en.js +3 -1
- package/src/i18n/ja.js +3 -1
- package/src/i18n/ko.js +3 -1
- package/src/i18n/zh.js +3 -1
- package/src/repl.js +52 -0
- package/src/safety/undo.js +98 -8
- package/src/tools/fsutil.js +7 -1
- package/src/tools/index.js +427 -30
- package/src/ui/intro.js +2 -1
- package/src/ui/motion.js +40 -9
- package/src/ui/office.js +22 -1
- package/src/ui/screen.js +47 -4
- package/src/ui/working.js +4 -1
package/src/ui/intro.js
CHANGED
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
import { c, cursor, width } from './ansi.js';
|
|
36
36
|
import { 말 } from '../i18n/index.js';
|
|
37
37
|
import * as 큰이름 from './banner.js';
|
|
38
|
+
import { 끄라는값인가 } from './office.js';
|
|
38
39
|
|
|
39
40
|
const 앞 = 'deel';
|
|
40
41
|
const 꼬리 = '-local';
|
|
@@ -42,7 +43,7 @@ const 이름폭 = 앞.length + 꼬리.length; // 10
|
|
|
42
43
|
const 들여 = ' ';
|
|
43
44
|
|
|
44
45
|
/** 모션을 끄는 스위치. 그림 돌림표(motion.js)와 같은 스위치를 함께 본다. */
|
|
45
|
-
export const 끔 = () =>
|
|
46
|
+
export const 끔 = () => 끄라는값인가(process.env.DEEL_NO_INTRO) || 끄라는값인가();
|
|
46
47
|
|
|
47
48
|
/**
|
|
48
49
|
* 한 틀에 얼마나 머무나.
|
package/src/ui/motion.js
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
* 그림을 점자 코드로 직접 적으면 나중에 아무도 못 고친다. 그래서 눈에 보이는
|
|
29
29
|
* 대로 '#' 과 '.' 으로 적고, 켜질 때 한 번 점자로 바꾼다.
|
|
30
30
|
*/
|
|
31
|
-
import { 켜달라했나 as
|
|
31
|
+
import { 켜달라했나 as 사무실켜달라했나, 끄라는값인가 } from './office.js';
|
|
32
32
|
|
|
33
33
|
// 점 번호와 자리. 점자는 이렇게 번호가 매겨져 있다 —
|
|
34
34
|
// 1 4
|
|
@@ -183,7 +183,11 @@ export const 그림들 = {
|
|
|
183
183
|
* 한쪽으로만 지나가는 것과 왔다 갔다 하는 것은 느낌이 다르다. 앞의 것은
|
|
184
184
|
* '진행 중' 으로 읽히고, 뒤의 것은 '기다리는 중' 으로 읽힌다. 45초가 넘은
|
|
185
185
|
* 자리에서 진행 중인 척하면 곧 끝날 것처럼 보여서 오히려 답답해진다.
|
|
186
|
-
*
|
|
186
|
+
*
|
|
187
|
+
* 여덟 장이라 한 바퀴가 0.72초 — 이 테마에서 제일 길다(다음이 다섯 장).
|
|
188
|
+
* 기사·동물 쪽 느긋은 열 장(0.9초)인데, 그쪽은 넘어졌다 일어나는 그림이라
|
|
189
|
+
* 한 동작에 장이 더 든다. 세 테마가 같을 이유는 없고, 검사가 재는 것도
|
|
190
|
+
* 「제 테마 안에서 느긋이 제일 길다」 이지 장수 자체가 아니다.
|
|
187
191
|
*/
|
|
188
192
|
느긋: [
|
|
189
193
|
['......', '##....', '##....', '......'],
|
|
@@ -510,13 +514,31 @@ const 이름표 = {
|
|
|
510
514
|
*/
|
|
511
515
|
let 고른그림 = null;
|
|
512
516
|
export function 그림고르기설정(값) {
|
|
513
|
-
|
|
517
|
+
// 소문자로 한 번만 찾는다. 이름표에는 대문자 열쇠가 하나도 없어서, 원본
|
|
518
|
+
// 그대로 한 번 더 찾던 자리는 어떤 입력에도 안 걸리는 죽은 규칙이었다.
|
|
519
|
+
고른그림 = 이름표[String(값 ?? '').trim().toLowerCase()] ?? null;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* `DEEL_MOTION` 이 **실제로 이기고 있나.**
|
|
524
|
+
*
|
|
525
|
+
* `/motion` 은 「환경변수가 켜져 있어 그쪽이 이깁니다」 를 `process.env.DEEL_MOTION`
|
|
526
|
+
* 이 비어 있지 않다는 것만 보고 띄우고 있었다. 그런데 `DEEL_MOTION=cat` 은
|
|
527
|
+
* 우리가 아는 이름이 아니라 **그냥 무시되고 설정 파일이 그대로 쓰인다.**
|
|
528
|
+
* 그러면 화면이 하는 말이 사실과 반대다 — 사람은 설정을 바꿔도 소용없다고
|
|
529
|
+
* 믿고 손을 떼는데, 정작 이기고 있는 것은 그 설정이다.
|
|
530
|
+
*
|
|
531
|
+
* @returns {null|{값: string, 이름: string|null, 이김: boolean}}
|
|
532
|
+
* 안 적혀 있으면 null. 적혀 있으면 그 값과, 우리가 아는 이름인지.
|
|
533
|
+
*/
|
|
534
|
+
export function 환경그림() {
|
|
535
|
+
const 값 = String(process.env.DEEL_MOTION ?? '').trim();
|
|
536
|
+
if (!값) return null;
|
|
537
|
+
const 이름 = 이름표[값.toLowerCase()] ?? null;
|
|
538
|
+
return { 값, 이름, 이김: 이름 !== null };
|
|
514
539
|
}
|
|
515
540
|
|
|
516
|
-
export const 테마이름 = () =>
|
|
517
|
-
?? 이름표[String(process.env.DEEL_MOTION ?? '').trim()]
|
|
518
|
-
?? 고른그림
|
|
519
|
-
?? '기본';
|
|
541
|
+
export const 테마이름 = () => 환경그림()?.이름 ?? 고른그림 ?? '기본';
|
|
520
542
|
export const 테마 = () => 테마들[테마이름()] ?? 테마들.기본;
|
|
521
543
|
|
|
522
544
|
/*
|
|
@@ -529,7 +551,15 @@ export const 테마 = () => 테마들[테마이름()] ?? 테마들.기본;
|
|
|
529
551
|
let 설정으로껐나 = false;
|
|
530
552
|
/** `/motion 끔` 이 여기에 넣는다. 환경변수는 따로 살아 있고, 둘 중 하나면 꺼진다. */
|
|
531
553
|
export function 끔설정(값) { 설정으로껐나 = !!값; }
|
|
532
|
-
|
|
554
|
+
/**
|
|
555
|
+
* 환경변수 쪽이 꺼 놨나. `/motion` 이 「바꿨습니다」 라고 말해도 되는지 여기서 본다.
|
|
556
|
+
*
|
|
557
|
+
* 끔() 하나로는 모자란다 — 그건 「설정으로 껐다」 와 「환경변수가 껐다」 를
|
|
558
|
+
* 뭉쳐서 준다. 앞엣것은 방금 사람이 고른 것이고, 뒤엣것은 사람이 무엇을 골라도
|
|
559
|
+
* 안 바뀌는 것이다. 둘을 뭉치면 「고른 것이 안 먹힌다」 를 말해 줄 수가 없다.
|
|
560
|
+
*/
|
|
561
|
+
export const 환경으로껐나 = () => 끄라는값인가();
|
|
562
|
+
export const 끔 = () => 환경으로껐나() || 설정으로껐나;
|
|
533
563
|
|
|
534
564
|
// 못 끄는 자리에서 쓸 한 칸짜리. working.js 의 돌림틀과 같은 것이다.
|
|
535
565
|
const 한칸 = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
@@ -542,7 +572,8 @@ const 한칸 = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '
|
|
|
542
572
|
*
|
|
543
573
|
* @param {string} 갈래 working.js 의 갈래 이름
|
|
544
574
|
* @param {number} 틱 90ms 마다 하나씩 오르는 수
|
|
545
|
-
* @returns {string} 점자
|
|
575
|
+
* @returns {string} 그 테마의 칸수만큼 점자 — 기본은 세 글자, 기사·동물은
|
|
576
|
+
* 여섯 글자다(넓은칸수). 끈 상태·사무실이면 한 글자.
|
|
546
577
|
*/
|
|
547
578
|
export function 그림고르기(갈래, 틱 = 0) {
|
|
548
579
|
const n = Math.trunc(틱) || 0;
|
package/src/ui/office.js
CHANGED
|
@@ -555,9 +555,30 @@ export function 켜달라했나() {
|
|
|
555
555
|
return 설정으로켰나;
|
|
556
556
|
}
|
|
557
557
|
|
|
558
|
+
/*
|
|
559
|
+
* `DEEL_NO_MOTION` 을 「끄라는 말」 로 읽어도 되나.
|
|
560
|
+
*
|
|
561
|
+
* `!!process.env.DEEL_NO_MOTION` 로 두고 있었다. 그러면 `DEEL_NO_MOTION=0` 도
|
|
562
|
+
* 끈다 — **끄지 말라고 적은 값이 끄는 것이다.** 바로 위 켜달라했나() 는 값을
|
|
563
|
+
* 읽어서 `DEEL_OFFICE=0` 을 껐다로 보므로, 같은 .env 에 둘을 나란히 적으면
|
|
564
|
+
* 하나만 말을 듣고 다른 하나는 반대로 간다. 그런데 화면에는 아무 말도 안 뜬다.
|
|
565
|
+
*
|
|
566
|
+
* 그래서 여기도 값을 읽는다. 안 적혀 있거나 0·false·off·no·빈 값이면 안 끈다.
|
|
567
|
+
* 그 밖의 값(1·true·yes·아무 글자)은 다 끄는 것으로 본다 — 끄려고 적었는데
|
|
568
|
+
* 철자가 달라서 안 꺼지는 쪽이 더 나쁘다.
|
|
569
|
+
*
|
|
570
|
+
* 이 자리에 두는 까닭: 이 값을 읽는 데가 셋(여기·motion.js·intro.js)인데,
|
|
571
|
+
* motion.js 가 이 파일을 가져다 쓰므로 반대로 가져오면 고리가 생긴다.
|
|
572
|
+
*/
|
|
573
|
+
const 안끄는값 = new Set(['', '0', 'false', 'off', 'no']);
|
|
574
|
+
export function 끄라는값인가(raw = process.env.DEEL_NO_MOTION) {
|
|
575
|
+
if (raw === undefined || raw === null) return false;
|
|
576
|
+
return !안끄는값.has(String(raw).trim().toLowerCase());
|
|
577
|
+
}
|
|
578
|
+
|
|
558
579
|
export function 켜나(rows = process.stdout.rows, cols = process.stdout.columns) {
|
|
559
580
|
if (!켜달라했나()) return false;
|
|
560
|
-
if (
|
|
581
|
+
if (끄라는값인가()) return false;
|
|
561
582
|
if (!색충분한가()) return false;
|
|
562
583
|
if ((cols ?? 0) < 최소폭) return false;
|
|
563
584
|
return (rows ?? 0) >= 최소높이;
|
package/src/ui/screen.js
CHANGED
|
@@ -46,13 +46,39 @@ import { spin } from './spinner.js';
|
|
|
46
46
|
*/
|
|
47
47
|
export function 상자쓸까({ tui = null } = {}) {
|
|
48
48
|
if (tui === false) return false; // deel --no-tui
|
|
49
|
-
|
|
49
|
+
|
|
50
|
+
/*
|
|
51
|
+
* ── 여기까지가 **그릴 데가 아예 없는** 자리다 ──────────────────────────
|
|
52
|
+
*
|
|
53
|
+
* 앞자리가 터미널이 아니면 커서를 위로 올릴 수가 없다. 제어문자가 글에
|
|
54
|
+
* 그대로 섞여 파이프·기록을 망친다. `--tui` 로도 못 넘는다 — bin/deel.js 의
|
|
55
|
+
* 설명도 「**터미널이면** 무조건」 이라고 적어 두고 있다.
|
|
56
|
+
*/
|
|
57
|
+
if (!process.stdout.isTTY) return false; // 파이프·기록
|
|
50
58
|
if (!process.stdin.isTTY) return false; // 입력이 파이프로 들어옴 (검사·데모)
|
|
59
|
+
|
|
60
|
+
/*
|
|
61
|
+
* ── 아래는 「아마 아닐 것」 인 자리다. `--tui` 는 여기를 넘으라고 있다 ──
|
|
62
|
+
*
|
|
63
|
+
* 이 줄이 없었다. `if (tui === true) return true;` 가 아래 세 가지를 **전부
|
|
64
|
+
* 지난 뒤**에 있었고, 그 바로 다음 줄이 무조건 `return true` 였다. 그러니
|
|
65
|
+
* `--tui` 는 어디에도 안 붙어 있었다 — 켜지는 자리에서는 어차피 켜지고,
|
|
66
|
+
* 안 켜지는 자리에서는 줘도 안 켜졌다.
|
|
67
|
+
*
|
|
68
|
+
* 그런데 이 깃발은 `deel --help` 에도 자동완성에도 「입력 상자를 켠다」
|
|
69
|
+
* (completion.js: force the input box on) 라고 적혀 있다. CI 값이 남아 있는
|
|
70
|
+
* 터미널이나 39칸짜리 창에서 `deel --tui` 를 치면 아무 말 없이 줄 화면이
|
|
71
|
+
* 뜬다. 스위치가 있다고 적어 놓고 선이 안 이어져 있던 것이다.
|
|
72
|
+
*
|
|
73
|
+
* 넘게 두는 것이 맞다. 셋 다 「대개 이럴 것」 이라는 짐작이고, 짐작을
|
|
74
|
+
* 사람이 손으로 뒤집겠다고 한 자리다. 좁은 창에서 테두리가 어긋나 보이는
|
|
75
|
+
* 것은 달라고 해서 준 결과이니 놀랄 일이 아니다.
|
|
76
|
+
*/
|
|
77
|
+
if (tui === true) return true;
|
|
51
78
|
if (process.env.TERM === 'dumb') return false;
|
|
52
79
|
if (process.env.CI) return false;
|
|
53
80
|
// 너무 좁으면 상자 안에 남는 자리가 없다. 테두리만 남고 글이 안 보인다.
|
|
54
81
|
if ((process.stdout.columns ?? 0) < 40) return false;
|
|
55
|
-
if (tui === true) return true;
|
|
56
82
|
return true;
|
|
57
83
|
}
|
|
58
84
|
|
|
@@ -100,8 +126,25 @@ export class LineScreen {
|
|
|
100
126
|
*/
|
|
101
127
|
#안내() { return this.깊이 ? c.gray(' │').repeat(this.깊이) : ''; }
|
|
102
128
|
|
|
103
|
-
/**
|
|
104
|
-
|
|
129
|
+
/**
|
|
130
|
+
* 한 줄. 이미 색이 입혀진 글을 받는다.
|
|
131
|
+
*
|
|
132
|
+
* 하위 작업 구간에서는 **빈 줄과 여러 줄에도** 세로줄을 세운다.
|
|
133
|
+
*
|
|
134
|
+
* 처음에는 `this.깊이 && s` 였다. 그래서 빈 줄에는 안 붙었는데, repl.js 는
|
|
135
|
+
* 도구가 뜰 때마다 `say('')` 를 한 번씩 넣는다 — 세로줄이 도구마다 끊겨
|
|
136
|
+
* 점선이 됐다. 여러 줄짜리 글도 첫 줄에만 붙어서, 하위가 뱉은 나머지 줄이
|
|
137
|
+
* 구간 밖으로 삐져나왔다.
|
|
138
|
+
*
|
|
139
|
+
* 「훑는 것만으로 구간이 잡힌다」 가 이 줄을 그리는 이유다. 끊겨 있으면
|
|
140
|
+
* 그 이유가 사라진다.
|
|
141
|
+
*/
|
|
142
|
+
줄(s = '') {
|
|
143
|
+
this.임시지움();
|
|
144
|
+
if (!this.깊이) return say(s);
|
|
145
|
+
const 안내 = this.#안내();
|
|
146
|
+
say(String(s).split('\n').map((l) => 안내 + l).join('\n'));
|
|
147
|
+
}
|
|
105
148
|
|
|
106
149
|
/** 줄바꿈 없이 이어 붙인다 — 스트리밍으로 오는 답. */
|
|
107
150
|
붙임(s) { this.임시지움(); process.stdout.write(s); }
|
package/src/ui/working.js
CHANGED
|
@@ -119,7 +119,10 @@ export function 갈래고르기(도구이름) {
|
|
|
119
119
|
*/
|
|
120
120
|
export function 문구고르기(갈래, 회차 = 0) {
|
|
121
121
|
const 목록 = 문구[갈래] ?? 문구.기본;
|
|
122
|
-
|
|
122
|
+
// `|| 0` 은 NaN 막이다. 바로 옆에서 같이 불리는 motion.js 의 그림고르기 는
|
|
123
|
+
// 이걸 하고 있었는데 여기만 안 했다 — 둘이 같은 값을 받는 자리라, 한쪽만
|
|
124
|
+
// undefined 를 돌려주면 그림은 도는데 문구 자리만 빈다.
|
|
125
|
+
const i = (((Math.trunc(회차) || 0) % 목록.length) + 목록.length) % 목록.length;
|
|
123
126
|
return 목록[i];
|
|
124
127
|
}
|
|
125
128
|
|