wormclaude 1.0.50 → 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 CHANGED
@@ -16,4 +16,4 @@ export const theme = {
16
16
  synType: '#a78bfa', // tip/sınıf adları, sabitler
17
17
  synProp: '#e0e0e0', // özellik/anahtar adları
18
18
  };
19
- export const VERSION = '1.0.50';
19
+ export const VERSION = '1.0.51';
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 + inputBoxLines(Math.max(8, cols())).length;
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,10 +190,25 @@ 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 (m.length) {
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) {
189
212
  // DİKEY slash menü — KAYAN pencere (8 satır, seçimi takip eder), seçili kırmızı.
190
213
  const sel = Math.min(cmdSel, m.length - 1);
191
214
  const WIN = 8;
@@ -229,15 +252,17 @@ export async function runTui() {
229
252
  async function runTurn(userText, displayText) {
230
253
  busy = true;
231
254
  streamChars = 0;
255
+ streamPreview = '';
232
256
  if (displayText !== undefined)
233
257
  printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
234
258
  history.push({ role: 'user', content: userText });
235
- const timer = setInterval(() => { spin++; if (busy && !perm)
259
+ const timer = setInterval(() => { spin++; if (busy && !perm && !ask)
236
260
  refresh(); }, 120);
237
261
  let lastSig = '', sameCount = 0, usedWeb = false, lastAnswer = '';
238
262
  try {
239
263
  for (let iter = 0; iter < 25; iter++) {
240
264
  streamChars = 0;
265
+ streamPreview = '';
241
266
  // Oto-compact: bağlam eşiği aşılınca otomatik özetle (uzun sohbet patlamasın)
242
267
  if (shouldAutoCompact(history)) {
243
268
  try {
@@ -251,11 +276,11 @@ export async function runTui() {
251
276
  let answer = '';
252
277
  let toolCalls = [];
253
278
  for await (const ev of streamChat(history, allToolSchemas(), config)) {
254
- // NOT: token başına drawFooter ÇAĞIRMA (uzun cevapta binlerce çizim titreme/kaybolma).
255
- // 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).
256
280
  if (ev.type === 'text') {
257
281
  answer += ev.text;
258
282
  streamChars = answer.length;
283
+ streamPreview = cleanModelText(answer);
259
284
  }
260
285
  else if (ev.type === 'done')
261
286
  toolCalls = ev.toolCalls || [];
@@ -263,6 +288,7 @@ export async function runTui() {
263
288
  answer += `\n[hata: ${ev.error}]`;
264
289
  }
265
290
  answer = cleanModelText(answer).trim();
291
+ streamPreview = ''; // canlı önizlemeyi kapat → footer küçülür, formatlı cevap içeriğe basılır
266
292
  const am = { role: 'assistant', content: answer || '' };
267
293
  if (toolCalls.length)
268
294
  am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
@@ -292,6 +318,7 @@ export async function runTui() {
292
318
  perm = { label: toolLabel(c.name, args), name: c.name, resolve };
293
319
  refresh();
294
320
  }),
321
+ ask: (q) => new Promise((resolve) => { ask = { question: q.question, options: q.options.length ? q.options : [{ label: 'Tamam' }], sel: 0, resolve }; refresh(); }),
295
322
  onResult: (c, _i, res) => { if ((c.name === 'WebSearch' || c.name === 'WebFetch') && res.ok)
296
323
  usedWeb = true; printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }); },
297
324
  });
@@ -305,6 +332,8 @@ export async function runTui() {
305
332
  clearInterval(timer);
306
333
  busy = false;
307
334
  perm = null;
335
+ ask = null;
336
+ streamPreview = '';
308
337
  refresh();
309
338
  // Web-öğrenme: web'de arayıp cevap ürettiyse {soru→cevap}'ı eğitim datasına ekle
310
339
  if (usedWeb && lastAnswer) {
@@ -339,6 +368,40 @@ export async function runTui() {
339
368
  catch { } });
340
369
  let ctrlcAt = 0;
341
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
+ }
342
405
  // İzin dialogu aktifse: E=Evet, T=Tümüne izin, H/Esc=Hayır (Ctrl+C de Hayır)
343
406
  if (perm) {
344
407
  const k = (str || '').toLowerCase();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.50",
3
+ "version": "1.0.51",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {