deel-local-cli 1.0.2 → 1.1.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/ui/screen.js CHANGED
@@ -69,12 +69,36 @@ export class LineScreen {
69
69
  // 다음에 오는 짧은 글이 그 줄 위에 겹쳐 찍힌다.
70
70
  this.임시중 = false;
71
71
  this.돌림 = null;
72
+ /*
73
+ * 하위 작업 안쪽인가 — 그렇다면 몇 겹인가.
74
+ *
75
+ * 하위 작업(tools/task.js)은 제 대화에서 따로 돌지만 화면은 하나다.
76
+ * 표시가 없으면 하위가 부른 도구 줄이 부모 것과 똑같이 찍혀서, 사람은
77
+ * 부모가 파일 열두 개를 읽은 줄로 본다. 한 단 들여 그으면 어디부터
78
+ * 어디까지가 떼어 낸 일인지가 그냥 보인다.
79
+ *
80
+ * 여기 한 군데서만 붙인다. repl.js 의 say() 자리 예순 곳을 고치는 대신
81
+ * 이벤트가 들고 온 깊이를 화면에 알려 주는 쪽이 안 빠뜨린다.
82
+ */
83
+ this.깊이 = 0;
72
84
  }
73
85
 
74
86
  // ── 흘러가는 글 ────────────────────────────────────────────────────────
75
87
 
88
+ /** 지금부터 찍는 줄이 몇 겹 안쪽인지. 0 이면 평소대로. */
89
+ 들여쓰기(n) { this.깊이 = Math.max(0, Number(n) || 0); }
90
+
91
+ /*
92
+ * 하위 작업 구간에 세로줄을 세운다.
93
+ *
94
+ * 빈 칸으로만 밀면 어디서 시작해 어디서 끝났는지가 안 보인다 — 특히 하위가
95
+ * 파일을 열 개 만지고 나면, 스크롤을 올려 여는 줄을 찾아야 그게 하위였는지
96
+ * 안다. 세로줄 한 칸이면 훑는 것만으로 구간이 잡힌다.
97
+ */
98
+ #안내() { return this.깊이 ? c.gray(' │').repeat(this.깊이) : ''; }
99
+
76
100
  /** 한 줄. 이미 색이 입혀진 글을 받는다. */
77
- 줄(s = '') { this.임시지움(); say(s); }
101
+ 줄(s = '') { this.임시지움(); say(this.깊이 && s ? this.#안내() + s : s); }
78
102
 
79
103
  /** 줄바꿈 없이 이어 붙인다 — 스트리밍으로 오는 답. */
80
104
  붙임(s) { this.임시지움(); process.stdout.write(s); }
@@ -150,6 +174,12 @@ export class LineScreen {
150
174
  /** 사람이 치는 중인 글이 바뀌었다. 줄화면은 readline 이 알아서 되비춘다. */
151
175
  입력갱신() { }
152
176
 
177
+ /** 일하는 중 미리 치는 글. 줄화면은 상자가 없어 보여 줄 자리가 없다. */
178
+ 대기갱신() { }
179
+
180
+ /** 줄화면은 붙여 쓰기 전에 임시글만 지우면 되므로 따로 셀 것이 없다. */
181
+ 줄중간인가() { return false; }
182
+
153
183
  /*
154
184
  * 일하는 중 표시. 줄화면은 안 그린다.
155
185
  *
@@ -189,7 +219,13 @@ export class BoxScreen extends LineScreen {
189
219
  * 찍히는 순간 상자가 사라지고, 그 뒤로는 일이 끝날 때까지 화면 아래가
190
220
  * 텅 빈 채로 남는다 — 정확히 그게 "밋밋해 보인다" 는 상태다.
191
221
  */
192
- 줄(s = '') { this.상자.지우기(); super.줄(s); this.상자.일그리기(); }
222
+ 줄(s = '') {
223
+ this.상자.지우기();
224
+ super.줄(s);
225
+ // 줄이 끝났다 — 이제 아래에 상자를 세워도 대화를 안 덮는다.
226
+ this.상자.줄끝남();
227
+ this.상자.일그리기();
228
+ }
193
229
 
194
230
  /*
195
231
  * 스트리밍으로 이어 붙이는 글은 다시 안 세운다.
@@ -198,13 +234,25 @@ export class BoxScreen extends LineScreen {
198
234
  * 위아래로 튄다. 답이 흘러나오는 동안에는 그 자체가 살아 있다는 신호라
199
235
  * 상자가 없어도 밋밋하지 않다. 답이 끝나면 repl.js 가 다시 세운다.
200
236
  */
201
- 붙임(s) { this.상자.지우기(); super.붙임(s); }
237
+ 붙임(s) {
238
+ // 상자를 걷고, **줄 중간에 멈춰 있다고 표시한다.** 이 표시가 없으면
239
+ // 돌아가는 표시가 90ms 뒤에 제 시계로 상자를 답 줄 위에 그려 버리고,
240
+ // 그 다음 지우기가 답까지 같이 지운다.
241
+ this.상자.줄중간표시();
242
+ super.붙임(s);
243
+ }
202
244
 
203
245
  /** 일하기 시작·바뀜·끝. 상자 안엣것만 바뀐다 (ui/working.js 의 문구). */
204
246
  일시작(session, 갈래) { this.임시지움(); this.상자.일시작(session, 갈래); }
205
247
  일바꿈(갈래, 곁정보) { this.상자.일바꿈(갈래, 곁정보); }
206
248
  일끝() { this.상자.일끝(); }
207
249
 
250
+ /** 일하는 도중에 미리 치고 있는 글 · 예약해 둔 개수. */
251
+ 대기갱신(글, 예약수) { this.상자.대기갱신(글, 예약수); }
252
+
253
+ /** 대화가 줄 중간에 멈춰 있나 (답이 흘러나오는 중). */
254
+ 줄중간인가() { return this.상자.줄중간; }
255
+
208
256
  /*
209
257
  * 줄화면에서 '생각 중…' 을 한 줄로 흘리던 자리.
210
258
  *
package/src/ui/status.js CHANGED
@@ -30,6 +30,28 @@ export const SEGMENTS = {
30
30
  short: (s) => c.hcyan(clip(s.conn.model, 14)),
31
31
  },
32
32
 
33
+ /*
34
+ * 모델 급 — 얼마나 알아서 하나 (agent/grade.js).
35
+ *
36
+ * 컨텍스트 게이지 바로 옆에 둔다. 그 둘이 다른 축이라는 것이 화면에서
37
+ * 나란히 보여야 한다 — 창이 128k 인데 급이 '작음' 인 경우가 실제로 있고,
38
+ * 그때 "왜 이렇게 조심스럽게 하지" 의 답이 이 한 글자다.
39
+ *
40
+ * 짐작일 때는 흐리게 그린다. 정한 것과 짐작한 것을 같은 색으로 보여주면,
41
+ * 사람은 프로그램이 확인한 사실이라고 읽는다.
42
+ */
43
+ grade: {
44
+ desc: '모델 급 (얼마나 알아서 하나)',
45
+ make: (s) => {
46
+ const g = 볼만한급(s);
47
+ return g ? (g.짐작 ? c.gray(`◈ ${g.급}?`) : c.white(`◈ ${g.급}`)) : '';
48
+ },
49
+ short: (s) => {
50
+ const g = 볼만한급(s);
51
+ return g ? (g.짐작 ? c.gray(`◈${g.급[0]}?`) : c.white(`◈${g.급[0]}`)) : '';
52
+ },
53
+ },
54
+
33
55
  ctx: {
34
56
  desc: '컨텍스트 사용량',
35
57
  make: (s) => {
@@ -117,7 +139,7 @@ export const SEGMENTS = {
117
139
  */
118
140
  export const SEGMENT_GROUPS = [
119
141
  ['dir', 'model'],
120
- ['ctx'],
142
+ ['ctx', 'grade'],
121
143
  ['work', 'think', 'mode'],
122
144
  ['tok'],
123
145
  ];
@@ -125,6 +147,23 @@ export const SEGMENT_GROUPS = [
125
147
  // 옛 이름. 조각 이름을 직접 넘기던 자리(검사·설정)가 그대로 돌게 남긴다.
126
148
  export const DEFAULT_SEGMENTS = SEGMENT_GROUPS.flat();
127
149
 
150
+ /**
151
+ * 이 급을 화면에 낼 값어치가 있나.
152
+ *
153
+ * '보통' 을 짐작으로 잡은 것은 **아무것도 못 알아낸 상태**다. 사내 게이트웨이가
154
+ * 그렇고, 그건 기본값이지 알아낸 사실이 아니다. 그걸 상태줄에 적으면 자리만
155
+ * 먹고, 좁은 터미널에서는 그 한 조각 때문에 승인 방식이 밀려 사라진다 —
156
+ * 내 파일이 안 물어보고 바뀌는지가 화면에서 없어지는 것이라 훨씬 비싸다.
157
+ *
158
+ * 알아낸 것이 있거나(짐작이 아님) 기본이 아닌 급일 때만 낸다.
159
+ */
160
+ function 볼만한급(s) {
161
+ if (typeof s.급 !== 'function') return null;
162
+ const g = s.급();
163
+ if (g.급 === '보통' && g.짐작) return null;
164
+ return g;
165
+ }
166
+
128
167
  // 컨텍스트 게이지가 쓰는 숫자. 자세한 꼴과 짧은 꼴이 같은 값을 봐야 한다.
129
168
  function 참값(s) {
130
169
  const b = s.breakdown();
@@ -167,9 +206,10 @@ export function statusLine(session, { segments = null, max = cols() - 2 } = {})
167
206
  ? segments.map((k) => [k])
168
207
  : SEGMENT_GROUPS;
169
208
 
170
- const 그리기 = (짧게) => 덩이들
209
+ const 그리기 = (짧게, 뺄것 = null) => 덩이들
171
210
  .map((그룹) => 그룹
172
211
  .map((k) => {
212
+ if (뺄것?.has(k)) return null;
173
213
  const seg = SEGMENTS[k];
174
214
  if (!seg) return null;
175
215
  return 짧게 && seg.short ? seg.short(session) : seg.make(session);
@@ -194,7 +234,16 @@ export function statusLine(session, { segments = null, max = cols() - 2 } = {})
194
234
  let parts = 그리기(false);
195
235
  if (!맞나(parts)) parts = 그리기(true);
196
236
 
197
- // 그래도 모자라면 뒤에서부터 떨군다. 앞쪽(폴더·모델·컨텍스트)이 마지막까지 남는다.
237
+ /*
238
+ * 그래도 모자라면 **급부터** 접는다.
239
+ *
240
+ * 뒤에서부터 떨구면 승인 방식이 먼저 사라진다. 모델 급은 알아 두면 좋은
241
+ * 것이고, 승인 방식은 지금 봐야 하는 것이다 — 내 파일이 안 물어보고 바뀌는지가
242
+ * 거기 적혀 있다. 둘 중 하나를 접어야 하면 언제나 이쪽이다.
243
+ */
244
+ if (!맞나(parts) && !segments) parts = 그리기(true, new Set(['grade']));
245
+
246
+ // 여전히 모자라면 뒤에서부터 떨군다. 앞쪽(폴더·모델·컨텍스트)이 마지막까지 남는다.
198
247
  while (parts.length > 1 && !맞나(parts)) parts.pop();
199
248
 
200
249
  return ` ${c.gray('▏')}${parts.join(칸막이)}`;
@@ -212,6 +261,13 @@ export function contextWarning(session) {
212
261
  /**
213
262
  * 켤 때 한 번 그리는 머리말 상자 안쪽 줄들.
214
263
  */
264
+ /** 머리말 모델 줄에 붙는 급 표시. 못 매기면 아무것도 안 붙는다. */
265
+ function 급머리말(session) {
266
+ if (typeof session.급 !== 'function') return '';
267
+ const g = session.급();
268
+ return c.gray(` · 급 ${g.급}${g.짐작 ? ' 짐작' : ''}`);
269
+ }
270
+
215
271
  export function headerLines(session, found) {
216
272
  const conn = session.conn;
217
273
  const 능력 = [
@@ -232,7 +288,16 @@ export function headerLines(session, found) {
232
288
  const lines = [
233
289
  `${c.hcyan(c.bold('deel'))} ${c.gray(conn.kind === 'ollama' ? 'Ollama 규격' : 'OpenAI 호환 규격')}`,
234
290
  '',
235
- `${c.gray('모델')} ${c.white(conn.model)} ${c.gray('(' + short(conn.ctx) + ' 토큰)')}`,
291
+ /*
292
+ * 모델 줄에 급을 같이 적는다.
293
+ *
294
+ * 창 크기(토큰)와 급은 다른 축인데, 켤 때 이 둘을 나란히 보지 않으면
295
+ * 나중에 상태줄의 `◈ 작음?` 이 무슨 뜻인지 알 길이 없다. 여기서 한 번
296
+ * 같이 읽고 나면 그 다음부터는 글자만 봐도 안다.
297
+ * 짐작이면 그렇다고 적는다 — 알아낸 사실과 같은 낯으로 내밀면 안 된다.
298
+ */
299
+ `${c.gray('모델')} ${c.white(conn.model)} ${c.gray('(' + short(conn.ctx) + ' 토큰')}`
300
+ + `${급머리말(session)}${c.gray(')')}`,
236
301
  `${c.gray('보냄')} ${목적지} ${c.gray('← 여기 말고는 어디로도 안 갑니다')}`,
237
302
  `${c.gray('연결')} ${능력}`,
238
303
  `${c.gray('폴더')} ${c.white(clip(session.root, 56))}`,
package/src/ui/working.js CHANGED
@@ -54,6 +54,12 @@ export const 문구 = {
54
54
  '지난 대화 접는 중',
55
55
  '자리 만드는 중',
56
56
  ],
57
+ // 하위 작업을 돌리는 중. 부모는 기다리기만 하니, 무엇을 기다리는지 말해 준다.
58
+ 하위: [
59
+ '떼어 낸 일 맡기는 중',
60
+ '하위 작업 도는 중',
61
+ '한 덩이씩 끝내는 중',
62
+ ],
57
63
  // 무슨 일인지 아직 모를 때. 첫 걸음과 도구 사이의 짧은 틈이다.
58
64
  기본: [
59
65
  '열심히 일하는 중',
@@ -79,7 +85,8 @@ export const 문구 = {
79
85
  const 도구갈래 = {
80
86
  Read: '읽기', Glob: '읽기', Grep: '읽기', Skill: '읽기', Recall: '읽기',
81
87
  Write: '쓰기', Edit: '쓰기', Append: '쓰기', Remember: '쓰기', TodoWrite: '쓰기',
82
- Bash: '명령',
88
+ Bash: '명령', Jobs: '명령',
89
+ Task: '하위',
83
90
  WebFetch: '웹',
84
91
  };
85
92