deel-local-cli 1.1.1 → 1.2.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/src/tools/todo.js CHANGED
@@ -39,16 +39,32 @@ export function render(items) {
39
39
  export const TODO_TOOL = {
40
40
  schema: {
41
41
  name: 'TodoWrite',
42
+ /*
43
+ * 문장 차례가 곧 중요도다.
44
+ *
45
+ * 창이 좁으면 tools/index.js 가 **뒤에서부터 문장째로 잘라 낸다**(설명길이).
46
+ * 그러니 규격(state·doing)이 뒤에 있으면 작은 모델에서 조용히 사라진다.
47
+ * 규격 → 규칙 → 조언 차례로 둔다.
48
+ *
49
+ * '세 단계 이상 걸리는 일이면' 이라고 적었더니 모델이 그걸 **세 개를 적으라는
50
+ * 말**로 읽었다. 열 단계짜리 일도 세 줄로 뭉쳐 나왔다. 그래서 개수 이야기를
51
+ * 따로 못 박는다 — 개수는 일의 크기가 정하는 것이고 상한이 없다.
52
+ */
42
53
  description:
43
- '할 일 목록을 만들고 갱신한다. 단계 이상 걸리는 일이면 먼저 목록을 만들고, '
54
+ '할 일 목록을 만들고 갱신한다. 여러 단계가 걸리는 일이면 먼저 목록을 만들고, '
44
55
  + '하나를 끝낼 때마다 바로 갱신한다. 목록 전체를 매번 통째로 보낸다. '
45
- + 'state 는 todo(아직) / doing(하는 중) / done(끝) 셋 중 하나이고, doing 은 한 번에 하나만 둔다.',
56
+ + 'state 는 todo(아직) / doing(하는 중) / done(끝) 셋 중 하나이고, doing 은 한 번에 하나만 둔다. '
57
+ + '단계 수는 일의 크기가 정한다 — 정해진 개수도 상한도 없다. '
58
+ + '개수를 맞추려고 다른 일을 한 줄에 뭉치지 마라. '
59
+ + '각 단계는 끝났는지 따로 확인할 수 있는 크기로 적어라.',
46
60
  parameters: {
47
61
  type: 'object',
48
62
  properties: {
49
63
  todos: {
50
64
  type: 'array',
51
- description: '할 전체 목록',
65
+ // 인자 설명은 따로 잘리므로, 개수 이야기를 여기에도 한 번 둔다.
66
+ // 창이 좁아 바깥 설명이 잘려 나가도 이건 남는다.
67
+ description: '할 일 전체 목록. 개수 제한 없음 — 일의 크기만큼 적는다',
52
68
  items: {
53
69
  type: 'object',
54
70
  properties: {
package/src/ui/ansi.js CHANGED
@@ -54,6 +54,12 @@ export const cursor = {
54
54
  show: () => ON && process.stdout.write('\x1b[?25h'),
55
55
  up: (n = 1) => ON && process.stdout.write(`\x1b[${n}A`),
56
56
  clearLine: () => ON && process.stdout.write('\x1b[2K\r'),
57
+ // 여러 조각을 한 덩이로 묶어 한 번에 내보낼 때 쓰는 글자값.
58
+ //
59
+ // 나눠 쓰면 그 사이가 화면에 그대로 보인다. 커서를 숨기기 전에 지우는 글이
60
+ // 먼저 나가면, 커서가 상자 안을 훑고 지나가는 것이 눈에 띈다.
61
+ 숨김: ON ? '\x1b[?25l' : '',
62
+ 보임: ON ? '\x1b[?25h' : '',
57
63
  };
58
64
 
59
65
  // 한글·한자·가나는 터미널에서 두 칸을 차지한다. 표 정렬이 이걸 모르면 어긋난다.
@@ -175,5 +181,3 @@ export const mark = {
175
181
  tree: c.gray('└'),
176
182
  branch: c.gray('├'),
177
183
  };
178
-
179
- export const COLOR_ON = ON;
@@ -372,7 +372,6 @@ export class InputBox {
372
372
  process.stdout.write('\n');
373
373
  this.줄중간 = false;
374
374
  }
375
- this.지우기();
376
375
 
377
376
  const { 줄들, 커서: 자리 } = 프레임({
378
377
  글,
@@ -385,10 +384,35 @@ export class InputBox {
385
384
  추천,
386
385
  });
387
386
 
388
- cursor.hide();
389
- process.stdout.write(줄들.map((l) => l + 줄끝지움).join('\n') + '\n');
390
387
  /*
391
- * 방금 찍은 마지막 **다음** 줄에 커서가 있다. 거기서 되짚어 올라간다.
388
+ * ── 지우지 않고 덮어쓴다. 그리고 번에 내보낸다 ────────────────────
389
+ *
390
+ * 예전에는 이랬다: 지우기() 로 상자를 싹 지우고 → 새로 그리고 → 커서를
391
+ * 되돌린다. 세 번을 따로 내보냈다. 터미널은 받는 대로 그리므로 **그 사이가
392
+ * 사람 눈에 보인다** — 상자가 한 번 비었다가 다시 차는 것이 백스페이스를
393
+ * 누를 때마다 반복되어, 화면이 깜빡 꺼지는 것처럼 느껴졌다. 글자를 지울
394
+ * 때는 한 번에 한 글자씩 여러 번 누르므로 특히 심했다.
395
+ *
396
+ * 그래서 두 가지를 바꿨다.
397
+ * 1) 지우고 다시 그리지 않는다. 상자 맨 윗줄로 올라가 **덮어쓴다.**
398
+ * 줄 끝마다 줄끝지움이 붙으므로 전에 있던 글자가 남을 자리가 없다.
399
+ * 빈 화면이 스쳐 가는 순간 자체가 사라진다.
400
+ * 2) 지우기·그리기·커서 되돌리기를 한 덩이로 묶어 **한 번만** 쓴다.
401
+ * 중간 상태가 화면에 못 나간다.
402
+ *
403
+ * 상자가 전보다 짧아졌을 때만 남는 줄이 생긴다. 그때만 그 줄을 지운다.
404
+ */
405
+ const 이전줄 = this.그린줄;
406
+ // 커서는 상자 아래에서 커서위 번째 줄에 있다. 맨 윗줄까지 남은 거리.
407
+ const 윗줄까지 = 이전줄 ? Math.max(0, 이전줄 - 1 - this.커서위) : 0;
408
+ const 남은줄 = Math.max(0, 이전줄 - 줄들.length);
409
+
410
+ let 나갈것 = cursor.숨김 + '\r' + 위로(윗줄까지);
411
+ 나갈것 += 줄들.map((l) => l + 줄끝지움).join('\n') + '\n';
412
+ for (let i = 0; i < 남은줄; i++) 나갈것 += 줄끝지움 + '\n';
413
+ /*
414
+ * 방금 찍은 마지막 줄 **다음** 줄에 커서가 있다(남은 줄을 지웠으면 그만큼
415
+ * 더 아래). 거기서 되짚어 올라간다.
392
416
  *
393
417
  * 오른쪽으로 옮길 때 한 칸이 어긋나기 쉽다. \r 은 커서를 1칸째에 놓고,
394
418
  * \x1b[nC 는 거기서 n칸 **더** 간다 — 그래서 1부터 세는 목표 칸으로
@@ -396,8 +420,10 @@ export class InputBox {
396
420
  * 백스페이스가 엉뚱한 자리를 지우는 것처럼 보인다.
397
421
  */
398
422
  const 오른쪽 = Math.max(0, 자리.열 - 1);
399
- process.stdout.write(위로(자리.위 + 1) + '\r' + (오른쪽 ? `\x1b[${오른쪽}C` : ''));
400
- cursor.show();
423
+ 나갈것 += 위로(남은줄 + 자리.위 + 1) + '\r' + (오른쪽 ? `\x1b[${오른쪽}C` : '');
424
+ 나갈것 += cursor.보임;
425
+ process.stdout.write(나갈것);
426
+
401
427
  this.그린줄 = 줄들.length;
402
428
  this.커서위 = 자리.위;
403
429
  }
package/src/ui/md.js ADDED
@@ -0,0 +1,227 @@
1
+ // 모델 답을 사람이 읽는 모양으로 그린다.
2
+ //
3
+ // ── 왜 만드나 ───────────────────────────────────────────────────────────
4
+ //
5
+ // 여태 답을 **글자 그대로** 찍었다. 모델은 마크다운으로 말하는데 화면은 그걸
6
+ // 모르니, 실제로 이렇게 보였다.
7
+ //
8
+ // ▌ ## 고친 것
9
+ // ▌ **src/runner.js** 의 `console.log` 를 바꿨습니다.
10
+ // ▌ - 로그 형식 통일
11
+ // ▌ ```js
12
+ // ▌ log.info('x')
13
+ // ▌ ```
14
+ //
15
+ // 별표와 백틱과 우물정자가 글에 섞여 눈이 걸린다. 무엇이 제목이고 무엇이
16
+ // 코드인지 사람이 머릿속에서 다시 파싱해야 한다. 답이 길수록 심해진다.
17
+ //
18
+ // ── 왜 줄 단위인가 ─────────────────────────────────────────────────────
19
+ //
20
+ // 답은 토막토막 흘러온다. `**굵` 까지 왔을 때는 그게 굵은 글씨가 될지 알 수 없다.
21
+ // 한 번 찍은 글자는 되돌릴 수 없으니, **줄이 끝나야** 그릴 수 있다.
22
+ //
23
+ // 그런데 줄이 안 끝나면? 긴 문단 하나가 통째로 한 줄이면 몇 초 동안 화면에
24
+ // 아무것도 안 나온다 — 사람 눈에는 멈춘 것으로 보인다. 그래서 한 줄이 화면 폭을
25
+ // 넘게 길어지면 거기까지를 **날것 그대로** 흘려보낸다. 모양은 못 내지만 글은
26
+ // 계속 흐른다. 모양보다 '살아 있다' 가 먼저다.
27
+ //
28
+ // 실제로는 제목·목록·코드펜스가 다 짧은 줄이라 거의 다 그려진다. 안 그려지는
29
+ // 것은 긴 산문뿐인데, 산문은 원래 꾸밀 것이 없다.
30
+ import { c, width } from './ansi.js';
31
+
32
+ const 굵게 = (s) => c.bold(s);
33
+ const 코드 = (s) => c.hyellow(s);
34
+
35
+ // 줄 전체를 흐리게 할 때 쓴다.
36
+ //
37
+ // 그냥 c.gray() 로 감싸면 안 된다. 안쪽 꾸밈(링크·코드)이 제 색을 끝내면서
38
+ // 리셋을 남기는데, 그 리셋이 바깥 흐림까지 같이 꺼 버린다 — 한 줄이 반만
39
+ // 흐려 보인다. 그래서 안쪽이 색을 끝낼 때마다 흐림을 다시 켠다.
40
+ const 회색앞 = c.gray('·').replace(/·[\s\S]*$/, '');
41
+ const 이어흐리게 = (s) => c.gray(String(s).replace(/\x1b\[0m/g, `\x1b[0m${회색앞}`));
42
+
43
+ /**
44
+ * 한 줄 안쪽 꾸밈. `코드` → **굵게** → *기울임* → ~~취소~~ → [글](주소) 차례.
45
+ *
46
+ * 코드부터 하는 이유: 코드 안에 별표가 들어 있으면(`a ** b`) 굵게 처리가 먼저
47
+ * 먹어서 코드가 깨진다. 먼저 뽑아서 자리표시자로 빼 두고 맨 끝에 되돌린다.
48
+ */
49
+ export function 인라인(글) {
50
+ // 들어온 NUL 은 먼저 버린다. 아래에서 코드 조각을 빼 둘 때 NUL 을 자리표시자로
51
+ // 쓰는데, 답에 NUL 이 섞여 오면(도구가 읽은 바이너리를 되읊는 경우) 그것이
52
+ // 자리표시자로 읽혀 엉뚱한 글자로 바뀐다 — 모델이 한 말과 화면이 달라진다.
53
+ let s = String(글 ?? '').replace(/\x00/g, '');
54
+ const 뺀것 = [];
55
+ s = s.replace(/`([^`]+)`/g, (_, 안) => {
56
+ 뺀것.push(코드(안));
57
+ return `\x00${뺀것.length - 1}\x00`;
58
+ });
59
+
60
+ s = s.replace(/\*\*([^*]+)\*\*/g, (_, 안) => 굵게(안));
61
+ s = s.replace(/(^|[\s(])\*([^*\s][^*]*)\*(?=$|[\s.,)])/g, (_, 앞, 안) => `${앞}${c.italic(안)}`);
62
+ s = s.replace(/~~([^~]+)~~/g, (_, 안) => c.gray(안));
63
+ // 링크는 글만 보이고 주소는 흐리게. 주소를 지우면 따라갈 수가 없다.
64
+ s = s.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, 글자, 주소) => `${c.under(글자)} ${c.gray(주소)}`);
65
+
66
+ return s.replace(/\x00(\d+)\x00/g, (_, i) => 뺀것[Number(i)]);
67
+ }
68
+
69
+ /**
70
+ * 줄 하나를 그린다.
71
+ *
72
+ * @param {string} 줄
73
+ * @param {object} 상태 {펜스: boolean} — 코드 블록 안인가
74
+ * @param {number} 폭
75
+ * @returns {{글: string, 상태: object}}
76
+ */
77
+ export function 한줄(줄, 상태 = { 펜스: false }, 폭 = 80) {
78
+ // 줄 끝의 \r 를 먼저 뗀다. 서버가 \r\n 으로 흘려보내면 여기까지 딸려 오는데,
79
+ // 아래 정규식들이 다 `(.*)$` 로 끝나고 `.` 은 \r 를 안 먹는다 — 그래서 제목도
80
+ // 목록도 하나도 안 잡히고 별표와 우물정자가 그대로 화면에 뜬다.
81
+ const s = String(줄 ?? '').replace(/\r+$/, '');
82
+
83
+ // 코드 울타리. 여는 줄에 언어가 붙어 있으면 그걸 알려 준다.
84
+ const 울타리 = s.match(/^\s*```(.*)$/);
85
+ if (울타리) {
86
+ const 안쪽 = 울타리[1].trim();
87
+ return {
88
+ 글: 상태.펜스 ? c.gray('└' + '─'.repeat(Math.max(2, Math.min(30, 폭 - 6))))
89
+ : c.gray('┌' + '─'.repeat(Math.max(2, Math.min(30, 폭 - 6))) + (안쪽 ? ` ${안쪽}` : '')),
90
+ 상태: { ...상태, 펜스: !상태.펜스 },
91
+ };
92
+ }
93
+
94
+ // 코드 안에서는 아무것도 해석하지 않는다. 별표는 별표고 우물정자는 우물정자다.
95
+ if (상태.펜스) return { 글: `${c.gray('│')} ${c.white(s)}`, 상태 };
96
+
97
+ // 가로줄
98
+ if (/^\s*([-*_])\s*\1\s*\1[\s\-*_]*$/.test(s)) {
99
+ return { 글: c.gray('─'.repeat(Math.max(4, Math.min(40, 폭 - 6)))), 상태 };
100
+ }
101
+
102
+ // 제목. 굵기와 색으로 가른다 — 우물정자를 남기면 그게 제일 먼저 눈에 띈다.
103
+ const 제목 = s.match(/^(#{1,6})\s+(.*)$/);
104
+ if (제목) {
105
+ const 깊이 = 제목[1].length;
106
+ const 글자 = 인라인(제목[2]);
107
+ if (깊이 <= 2) return { 글: `${c.hcyan('▍')} ${c.bold(글자)}`, 상태 };
108
+ return { 글: c.bold(글자), 상태 };
109
+ }
110
+
111
+ // 인용
112
+ const 인용 = s.match(/^\s*>\s?(.*)$/);
113
+ if (인용) return { 글: `${c.gray('│')} ${이어흐리게(인라인(인용[1]))}`, 상태 };
114
+
115
+ // 할 일 목록은 목록보다 먼저 본다 — `- [ ]` 가 그냥 목록으로 먹히면 안 된다.
116
+ const 할일 = s.match(/^(\s*)[-*+]\s+\[([ xX])\]\s+(.*)$/);
117
+ if (할일) {
118
+ const 표 = 할일[2] === ' ' ? c.gray('☐') : c.green('☑');
119
+ return { 글: `${할일[1]}${표} ${인라인(할일[3])}`, 상태 };
120
+ }
121
+
122
+ // 글머리표
123
+ const 목록 = s.match(/^(\s*)[-*+]\s+(.*)$/);
124
+ if (목록) return { 글: `${목록[1]}${c.hcyan('•')} ${인라인(목록[2])}`, 상태 };
125
+
126
+ // 번호 목록. 번호는 살린다 — 순서가 뜻을 갖는 자리다.
127
+ const 번호 = s.match(/^(\s*)(\d+)([.)])\s+(.*)$/);
128
+ if (번호) return { 글: `${번호[1]}${c.hcyan(번호[2] + 번호[3])} ${인라인(번호[4])}`, 상태 };
129
+
130
+ // 표는 세로줄만 흐리게 한다. 칸을 맞추려면 표 전체를 들고 있어야 하는데,
131
+ // 그러면 표가 끝날 때까지 화면에 아무것도 안 나온다.
132
+ if (/^\s*\|.*\|\s*$/.test(s)) {
133
+ // 가름줄(`|---|---|`). 그대로 두면 화면에 작대기가 그대로 뜬다.
134
+ // 바로 앞 머리줄의 칸 너비를 기억해 뒀다가 그 자리에 맞춰 긋는다 —
135
+ // 그래야 세로줄과 가로줄이 만나서 표처럼 보인다.
136
+ if (/^\s*\|[\s:|-]+\|\s*$/.test(s)) {
137
+ const 너비 = 상태.표너비 ?? s.split('|').map(() => 3);
138
+ return { 글: c.gray(너비.map((w) => '─'.repeat(w)).join('┼')), 상태 };
139
+ }
140
+ // 칸에 먼저 꾸밈을 먹이고 그 다음에 잇는다. 순서가 반대면 색 제어문자가
141
+ // 이미 낀 글에 정규식을 돌리게 되어 칸이 통째로 망가진다.
142
+ const 칸들 = s.split('|').map((칸) => 인라인(칸));
143
+ return { 글: 칸들.join(c.gray('│')), 상태: { ...상태, 표너비: 칸들.map((칸) => width(칸)) } };
144
+ }
145
+
146
+ return { 글: 인라인(s), 상태 };
147
+ }
148
+
149
+ /**
150
+ * 흘러오는 토막을 받아 그릴 수 있게 된 줄만 내놓는다.
151
+ *
152
+ * 쓰는 쪽은 이렇게 한다:
153
+ * for (const 줄 of md.넣기(토막)) 화면에 찍는다
154
+ * ... 답이 끝나면
155
+ * for (const 줄 of md.끝()) 화면에 찍는다
156
+ */
157
+ export class 마크다운 {
158
+ constructor({ 폭 = 80 } = {}) {
159
+ // 창이 좁으면 부르는 쪽이 (columns - 6) 을 넘기는데, 그게 0 이나 음수가 될 수
160
+ // 있다. 그러면 모든 줄이 '폭을 넘는다' 가 되어 **아무것도 안 그려진다.**
161
+ // 바닥을 둬서, 좁은 창에서도 짧은 줄은 제 모양으로 나가게 한다.
162
+ this.폭 = Math.max(20, Number(폭) || 0);
163
+ this.담긴것 = '';
164
+ this.상태 = { 펜스: false };
165
+ // 이 줄은 이미 날것으로 흘려보내기 시작했다 — 뒤늦게 꾸미면 앞뒤가 안 맞는다.
166
+ this.날것중 = false;
167
+ }
168
+
169
+ #그리기(줄) {
170
+ // 이 줄은 이미 날것으로 나가기 시작했다. 뒤늦게 꾸미면 앞뒤가 안 맞으므로
171
+ // 남은 만큼을 그대로 이어 붙이고 줄만 끝낸다.
172
+ if (this.날것중) { this.날것중 = false; return { 이어붙임: String(줄).replace(/\r/g, ''), 끝났나: true }; }
173
+ const r = 한줄(줄, this.상태, this.폭);
174
+ this.상태 = r.상태;
175
+ return r.글;
176
+ }
177
+
178
+ 넣기(토막) {
179
+ this.담긴것 += String(토막 ?? '');
180
+ const 나갈것 = [];
181
+
182
+ for (;;) {
183
+ const i = this.담긴것.indexOf('\n');
184
+ if (i >= 0) {
185
+ // \r\n 으로 온 줄은 여기서 \r 를 뗀다. 남겨서 내보내면 터미널에서 커서가
186
+ // 줄 맨 앞으로 돌아가 앞에 세운 세로줄 위에 덮어쓴다 — 글자가 먹힌 것처럼
187
+ // 보인다. 토막이 \r 와 \n 사이에서 끊겨 와도 여기로 모여서 걸린다.
188
+ 나갈것.push(this.#그리기(this.담긴것.slice(0, i)));
189
+ this.담긴것 = this.담긴것.slice(i + 1);
190
+ continue;
191
+ }
192
+ // 줄이 안 끝나는데 너무 길어졌다. 여기까지 날것으로 흘려보낸다 —
193
+ // 멈춘 것처럼 보이는 것보다 안 꾸며진 글이 흐르는 편이 낫다.
194
+ if (width(this.담긴것) > this.폭) {
195
+ // 날것으로 나가는 길에는 한줄() 이 안 끼므로 여기서 \r 를 턴다.
196
+ 나갈것.push({ 이어붙임: this.담긴것.replace(/\r/g, ''), 첫조각: !this.날것중, 끝났나: false });
197
+ this.담긴것 = '';
198
+ this.날것중 = true;
199
+ }
200
+ break;
201
+ }
202
+ return 나갈것;
203
+ }
204
+
205
+ /** 답이 끝났다. 남은 것을 내놓는다. */
206
+ 끝() {
207
+ // 담긴 것은 없는데 날것으로 나가던 중이면 줄만 끝내 준다.
208
+ if (!this.담긴것) {
209
+ const 닫을것 = this.날것중 ? [{ 이어붙임: '', 끝났나: true }] : [];
210
+ this.날것중 = false;
211
+ return 닫을것;
212
+ }
213
+ const 남은것 = this.#그리기(this.담긴것);
214
+ this.담긴것 = '';
215
+ this.날것중 = false;
216
+ return [남은것];
217
+ }
218
+ }
219
+
220
+ /** 통째로 있는 글을 한 번에 그린다 (스트리밍이 아닌 자리). */
221
+ export function 그리기(글, 폭 = 80) {
222
+ const md = new 마크다운({ 폭 });
223
+ const 줄들 = [];
224
+ for (const x of md.넣기(String(글 ?? ''))) 줄들.push(typeof x === 'string' ? x : x.이어붙임);
225
+ for (const x of md.끝()) 줄들.push(typeof x === 'string' ? x : x.이어붙임);
226
+ return 줄들;
227
+ }
package/src/ui/prompt.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // 입력 받기 — 한 줄, 비밀번호(가림), 목록 선택, 예/아니오.
2
- import { c, say, mark, cursor } from './ansi.js';
2
+ import { c, say } from './ansi.js';
3
3
 
4
4
  function raw() {
5
5
  return process.stdin.isTTY ? process.stdin.setRawMode.bind(process.stdin) : null;
package/src/ui/screen.js CHANGED
@@ -30,7 +30,7 @@
30
30
  * 대화는 그냥 흘려보내기로 했다. 그러면 stdout 에 쓰는 모든 코드가 손 안 대고
31
31
  * 그대로 맞다. 스크롤·복사·`| tee` 도 그대로다.
32
32
  */
33
- import { c, say, cursor, box, cols } from './ansi.js';
33
+ import { c, say, cursor, box } from './ansi.js';
34
34
  import { statusLine, contextWarning } from './status.js';
35
35
  import { spin } from './spinner.js';
36
36
 
package/src/ui/status.js CHANGED
@@ -15,6 +15,19 @@ export const SEGMENTS = {
15
15
  // 이 조각 하나가 상태줄을 통째로 넘겨 줄이 접힌다(모델 이름은 이미 자르고 있었다).
16
16
  dir: { desc: '작업 폴더', make: (s) => c.gray(clip(base(s.root), 20)) },
17
17
 
18
+ /*
19
+ * 지금 어느 대화 갈래인가.
20
+ *
21
+ * 갈래를 안 쓰면 **아무것도 안 그린다.** 상태줄은 이미 빽빽해서, 안 쓰는
22
+ * 사람에게까지 한 칸을 더 내주면 좁은 터미널에서 뒤엣것이 떨어져 나간다.
23
+ * 갈래가 둘 이상일 때만 repl 이 session.갈래표 를 채운다(agent/threads.js).
24
+ */
25
+ thread: {
26
+ desc: '대화 갈래',
27
+ make: (s) => (s.갈래표 ? c.hcyan(`⑂ ${clip(s.갈래표, 12)}`) : null),
28
+ short: (s) => (s.갈래표 ? c.hcyan(`⑂ ${clip(s.갈래표, 6)}`) : null),
29
+ },
30
+
18
31
  /*
19
32
  * 모델 이름.
20
33
  *
@@ -138,7 +151,7 @@ export const SEGMENTS = {
138
151
  * 그러면 눈이 세 번만 멈춘다.
139
152
  */
140
153
  export const SEGMENT_GROUPS = [
141
- ['dir', 'model'],
154
+ ['dir', 'thread', 'model'],
142
155
  ['ctx', 'grade'],
143
156
  ['work', 'think', 'mode'],
144
157
  ['tok'],
package/src/ui/working.js CHANGED
@@ -109,9 +109,6 @@ export function 문구고르기(갈래, 회차 = 0) {
109
109
  return 목록[i];
110
110
  }
111
111
 
112
- // 돌아가는 표시. 점자 여덟 칸짜리는 폭이 한 칸이라 테두리를 안 밀어낸다.
113
- export const 돌림틀 = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
114
-
115
112
  /** 걸린 시간을 짧게. 1분이 넘으면 분까지 적는다 — 90초보다 1분 30초가 읽힌다. */
116
113
  export function 걸린시간(ms) {
117
114
  const 초 = Math.max(0, Math.floor(ms / 1000));
package/src/ui/wrap.js CHANGED
@@ -2,7 +2,7 @@
2
2
  //
3
3
  // 전에는 전체화면(tui.js)에만 쓰던 것이라 거기 있었다. 전체화면을 걷어내면서
4
4
  // 여기로 옮긴다 — 입력 상자가 긴 입력을 접을 때 같은 것이 필요하다.
5
- import { width, clip } from './ansi.js';
5
+ import { width } from './ansi.js';
6
6
 
7
7
  /**
8
8
  * 색을 유지하면서 폭에 맞춰 접는다.
@@ -59,10 +59,3 @@ export function 접어쓰기(글, 폭) {
59
59
  if (지금.trim() !== '' || 줄들.length === 0) 줄들.push(지금 + (색 ? '\x1b[0m' : ''));
60
60
  return 줄들;
61
61
  }
62
-
63
- /** 폭에 맞춰 오른쪽을 공백으로 채운다. 안 채우면 앞 그림이 남는다. */
64
- export function 채움(글, 폭) {
65
- const w = width(글);
66
- if (w > 폭) return clip(글, 폭);
67
- return 글 + ' '.repeat(폭 - w);
68
- }