wormclaude 1.0.49 → 1.0.51
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/dist/theme.js +1 -1
- package/dist/tui.js +76 -11
- package/package.json +1 -1
package/dist/theme.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -64,6 +64,8 @@ export async function runTui() {
|
|
|
64
64
|
];
|
|
65
65
|
const displayItems = [{ kind: 'banner' }];
|
|
66
66
|
let inputBuf = '', busy = false, streamChars = 0, spin = 0;
|
|
67
|
+
let streamPreview = ''; // canlı akış önizlemesi (footer'da)
|
|
68
|
+
const PREVIEW_LINES = 6; // akışta sabit önizleme satırı (yükseklik stabil → titreme yok)
|
|
67
69
|
const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
|
|
68
70
|
// Giriş kutusunu genişliğe göre ÇOK SATIRA sar (uzun mesaj/kod sağa taşmaz, alta iner).
|
|
69
71
|
const MAX_INPUT_LINES = 10;
|
|
@@ -88,9 +90,14 @@ export async function runTui() {
|
|
|
88
90
|
const footerHeight = () => {
|
|
89
91
|
if (perm)
|
|
90
92
|
return 4;
|
|
93
|
+
if (ask)
|
|
94
|
+
return ask.options.length + 3; // çizgi + soru + seçenekler... aslında: line+q+opts+line
|
|
95
|
+
const il = inputBoxLines(Math.max(8, cols())).length;
|
|
96
|
+
if (busy && streamPreview)
|
|
97
|
+
return PREVIEW_LINES + 2 + il; // canlı akış önizlemesi
|
|
91
98
|
const m = cmdMatches();
|
|
92
99
|
const statusLines = m.length ? Math.min(8, m.length) : 1;
|
|
93
|
-
return statusLines + 2 +
|
|
100
|
+
return statusLines + 2 + il;
|
|
94
101
|
};
|
|
95
102
|
// Yükseklik değiştiyse scroll-region'ı + içeriği yeniden bas; değişmediyse sadece footer'ı çiz.
|
|
96
103
|
const refresh = () => { const fh = footerHeight(); if (fh !== prevFH)
|
|
@@ -98,8 +105,9 @@ export async function runTui() {
|
|
|
98
105
|
else
|
|
99
106
|
drawFooter(); };
|
|
100
107
|
setToolConfig(config); // araçlar (Write/Bash/Edit…) aynı config'i kullansın
|
|
101
|
-
// İzin (araç onayı) durumu
|
|
108
|
+
// İzin (araç onayı) + soru (AskUserQuestion) durumu
|
|
102
109
|
let perm = null;
|
|
110
|
+
let ask = null;
|
|
103
111
|
const allowAll = new Set(); // "Tümüne izin" denen araçlar (oturum boyu)
|
|
104
112
|
const inputHistory = []; // gönderilen mesajlar (↑/↓ ile geri çağrılır)
|
|
105
113
|
let histIdx = -1; // -1 = geçmişte gezinmiyor
|
|
@@ -182,15 +190,32 @@ export async function runTui() {
|
|
|
182
190
|
+ paint('[T] Tümüne izin', theme.grey) + paint(' · ', theme.greyDim) + paint('[H] Hayır', theme.grey);
|
|
183
191
|
body = [line, q, opts, line];
|
|
184
192
|
}
|
|
193
|
+
else if (ask) {
|
|
194
|
+
// Soru dialogu (AskUserQuestion): soru + numaralı seçenekler
|
|
195
|
+
const q = paint(' ? ', theme.redBright, true) + paint(ask.question, theme.white);
|
|
196
|
+
const opts = ask.options.map((o, i) => ' ' + paint((i === ask.sel ? '✶ ' : ' ') + (i + 1) + '. ' + o.label, i === ask.sel ? theme.redBright : theme.grey) + (o.description ? paint(' ' + o.description, theme.greyDim) : ''));
|
|
197
|
+
body = [line, q, ...opts, line];
|
|
198
|
+
}
|
|
185
199
|
else {
|
|
186
200
|
const inputLines = inputBoxLines(W); // çok-satırlı giriş (sarılmış)
|
|
187
201
|
const m = cmdMatches();
|
|
188
|
-
if (
|
|
189
|
-
//
|
|
190
|
-
const
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
202
|
+
if (busy && streamPreview) {
|
|
203
|
+
// CANLI akış önizlemesi (son PREVIEW_LINES satır, sabit yükseklik)
|
|
204
|
+
const pl = streamPreview.split('\n');
|
|
205
|
+
const tail = pl.slice(-PREVIEW_LINES);
|
|
206
|
+
while (tail.length < PREVIEW_LINES)
|
|
207
|
+
tail.push('');
|
|
208
|
+
const prev = tail.map((l, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(l, theme.white));
|
|
209
|
+
body = [...prev, line, ...inputLines, line];
|
|
210
|
+
}
|
|
211
|
+
else if (m.length) {
|
|
212
|
+
// DİKEY slash menü — KAYAN pencere (8 satır, seçimi takip eder), seçili kırmızı.
|
|
213
|
+
const sel = Math.min(cmdSel, m.length - 1);
|
|
214
|
+
const WIN = 8;
|
|
215
|
+
const start = Math.max(0, Math.min(sel - 3, m.length - WIN));
|
|
216
|
+
const view = m.slice(start, start + WIN);
|
|
217
|
+
const menu = view.map((c, i) => {
|
|
218
|
+
const on = (start + i) === sel;
|
|
194
219
|
return ' ' + paint((on ? '✶ ' : ' ') + c.name.padEnd(13), on ? theme.redBright : theme.grey) + paint(cmdDesc(c.name) || c.desc || '', theme.greyDim);
|
|
195
220
|
});
|
|
196
221
|
body = [...menu, line, ...inputLines, line];
|
|
@@ -227,15 +252,17 @@ export async function runTui() {
|
|
|
227
252
|
async function runTurn(userText, displayText) {
|
|
228
253
|
busy = true;
|
|
229
254
|
streamChars = 0;
|
|
255
|
+
streamPreview = '';
|
|
230
256
|
if (displayText !== undefined)
|
|
231
257
|
printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
|
|
232
258
|
history.push({ role: 'user', content: userText });
|
|
233
|
-
const timer = setInterval(() => { spin++; if (busy && !perm)
|
|
259
|
+
const timer = setInterval(() => { spin++; if (busy && !perm && !ask)
|
|
234
260
|
refresh(); }, 120);
|
|
235
261
|
let lastSig = '', sameCount = 0, usedWeb = false, lastAnswer = '';
|
|
236
262
|
try {
|
|
237
263
|
for (let iter = 0; iter < 25; iter++) {
|
|
238
264
|
streamChars = 0;
|
|
265
|
+
streamPreview = '';
|
|
239
266
|
// Oto-compact: bağlam eşiği aşılınca otomatik özetle (uzun sohbet patlamasın)
|
|
240
267
|
if (shouldAutoCompact(history)) {
|
|
241
268
|
try {
|
|
@@ -249,11 +276,11 @@ export async function runTui() {
|
|
|
249
276
|
let answer = '';
|
|
250
277
|
let toolCalls = [];
|
|
251
278
|
for await (const ev of streamChat(history, allToolSchemas(), config)) {
|
|
252
|
-
//
|
|
253
|
-
// streamChars güncellenir; footer'ı 120ms'lik spinTimer çizer; yazma anında keypress çizer.
|
|
279
|
+
// CANLI akış: answer biriktir + streamPreview güncelle. drawFooter'ı 120ms timer çizer (titreme yok).
|
|
254
280
|
if (ev.type === 'text') {
|
|
255
281
|
answer += ev.text;
|
|
256
282
|
streamChars = answer.length;
|
|
283
|
+
streamPreview = cleanModelText(answer);
|
|
257
284
|
}
|
|
258
285
|
else if (ev.type === 'done')
|
|
259
286
|
toolCalls = ev.toolCalls || [];
|
|
@@ -261,6 +288,7 @@ export async function runTui() {
|
|
|
261
288
|
answer += `\n[hata: ${ev.error}]`;
|
|
262
289
|
}
|
|
263
290
|
answer = cleanModelText(answer).trim();
|
|
291
|
+
streamPreview = ''; // canlı önizlemeyi kapat → footer küçülür, formatlı cevap içeriğe basılır
|
|
264
292
|
const am = { role: 'assistant', content: answer || '' };
|
|
265
293
|
if (toolCalls.length)
|
|
266
294
|
am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
|
|
@@ -290,6 +318,7 @@ export async function runTui() {
|
|
|
290
318
|
perm = { label: toolLabel(c.name, args), name: c.name, resolve };
|
|
291
319
|
refresh();
|
|
292
320
|
}),
|
|
321
|
+
ask: (q) => new Promise((resolve) => { ask = { question: q.question, options: q.options.length ? q.options : [{ label: 'Tamam' }], sel: 0, resolve }; refresh(); }),
|
|
293
322
|
onResult: (c, _i, res) => { if ((c.name === 'WebSearch' || c.name === 'WebFetch') && res.ok)
|
|
294
323
|
usedWeb = true; printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }); },
|
|
295
324
|
});
|
|
@@ -303,6 +332,8 @@ export async function runTui() {
|
|
|
303
332
|
clearInterval(timer);
|
|
304
333
|
busy = false;
|
|
305
334
|
perm = null;
|
|
335
|
+
ask = null;
|
|
336
|
+
streamPreview = '';
|
|
306
337
|
refresh();
|
|
307
338
|
// Web-öğrenme: web'de arayıp cevap ürettiyse {soru→cevap}'ı eğitim datasına ekle
|
|
308
339
|
if (usedWeb && lastAnswer) {
|
|
@@ -337,6 +368,40 @@ export async function runTui() {
|
|
|
337
368
|
catch { } });
|
|
338
369
|
let ctrlcAt = 0;
|
|
339
370
|
process.stdin.on('keypress', (str, key) => {
|
|
371
|
+
// Soru dialogu (AskUserQuestion) aktifse: ↑↓ / 1-9 seç, Enter onayla, Esc/Ctrl+C ilk seçenek
|
|
372
|
+
if (ask) {
|
|
373
|
+
const n = ask.options.length;
|
|
374
|
+
if (key && key.name === 'up') {
|
|
375
|
+
ask.sel = (ask.sel - 1 + n) % n;
|
|
376
|
+
refresh();
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
if (key && key.name === 'down') {
|
|
380
|
+
ask.sel = (ask.sel + 1) % n;
|
|
381
|
+
refresh();
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
if (str && /^[1-9]$/.test(str) && +str <= n) {
|
|
385
|
+
ask.sel = +str - 1;
|
|
386
|
+
refresh();
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
if (key && key.name === 'return') {
|
|
390
|
+
const r = ask.resolve, v = ask.options[ask.sel]?.label || '';
|
|
391
|
+
ask = null;
|
|
392
|
+
r(v);
|
|
393
|
+
refresh();
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (key && (key.name === 'escape' || (key.ctrl && key.name === 'c'))) {
|
|
397
|
+
const r = ask.resolve, v = ask.options[0]?.label || '';
|
|
398
|
+
ask = null;
|
|
399
|
+
r(v);
|
|
400
|
+
refresh();
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
340
405
|
// İzin dialogu aktifse: E=Evet, T=Tümüne izin, H/Esc=Hayır (Ctrl+C de Hayır)
|
|
341
406
|
if (perm) {
|
|
342
407
|
const k = (str || '').toLowerCase();
|