wormclaude 1.0.53 → 1.0.55

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/ansi.js CHANGED
@@ -81,7 +81,7 @@ export function bannerAnsi(cols) {
81
81
  return body + '\n' + paint(' ' + t('banner.subtitle'), theme.greyDim);
82
82
  }
83
83
  // ── Markdown bloğu → ANSI (kod blokları sözdizimi-vurgulu) ──
84
- function markdownAnsi(text, cols) {
84
+ export function markdownAnsi(text, cols) {
85
85
  const W = Math.max(20, cols - 2);
86
86
  const lines = text.split('\n');
87
87
  const out = [];
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.53';
19
+ export const VERSION = '1.0.55';
package/dist/tools.js CHANGED
@@ -507,7 +507,7 @@ export const toolSchemas = [
507
507
  type: 'function',
508
508
  function: {
509
509
  name: 'AskUserQuestion',
510
- description: 'Ask the user a multiple-choice question and get their answer. Use when you need a decision only the user can make.',
510
+ description: 'Ask the user a multiple-choice question and get their answer (renders as an interactive picker). Use when a request is AMBIGUOUS and you need a decision only the user can make BEFORE acting — e.g. they ask you to build a website/app/project but did not specify the type, tech stack, or style. Prefer asking ONE concise question with 2-4 clear options (each with a short description) over guessing. Do NOT use it for things you can reasonably decide yourself.',
511
511
  parameters: {
512
512
  type: 'object',
513
513
  properties: {
package/dist/tui.js CHANGED
@@ -8,7 +8,7 @@ import stringWidth from 'string-width';
8
8
  import { loadConfig, streamChat, fetchAccount } from './api.js';
9
9
  import { allToolSchemas, executeToolCalls, executeTool, toolLabel, setToolConfig } from './tools.js';
10
10
  import { sanitizeOutput } from './errorsan.js';
11
- import { itemAnsi } from './ansi.js';
11
+ import { itemAnsi, markdownAnsi } from './ansi.js';
12
12
  import { theme, VERSION } from './theme.js';
13
13
  import { cleanModelText } from './textclean.js';
14
14
  import { COMMANDS, runSlashCommand } from './commands.js';
@@ -64,8 +64,7 @@ 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
+ // Canlı akış artık FOOTER'da DEĞİL içerik akışına (mesajın altından aşağı) satır-satır basılır.
69
68
  const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
70
69
  // Giriş kutusunu genişliğe göre ÇOK SATIRA sar; çok uzunsa (yapıştırma) KISALTIP özet gösterir.
71
70
  const MAX_INPUT_LINES = 6;
@@ -196,16 +195,7 @@ export async function runTui() {
196
195
  else {
197
196
  const inputLines = inputBoxLines(W); // çok-satırlı giriş (sarılmış)
198
197
  const m = cmdMatches();
199
- if (busy && streamPreview) {
200
- // CANLI akış önizlemesi (son PREVIEW_LINES satır, sabit yükseklik)
201
- const pl = streamPreview.split('\n');
202
- const tail = pl.slice(-PREVIEW_LINES);
203
- while (tail.length < PREVIEW_LINES)
204
- tail.push('');
205
- const prev = tail.map((l, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(l, theme.white));
206
- body = [...prev, line, ...inputLines, line];
207
- }
208
- else if (m.length) {
198
+ if (m.length) {
209
199
  // DİKEY slash menü — KAYAN pencere (8 satır, seçimi takip eder), seçili kırmızı.
210
200
  const sel = Math.min(cmdSel, m.length - 1);
211
201
  const WIN = 8;
@@ -254,7 +244,6 @@ export async function runTui() {
254
244
  async function runTurn(userText, displayText) {
255
245
  busy = true;
256
246
  streamChars = 0;
257
- streamPreview = '';
258
247
  if (displayText !== undefined)
259
248
  printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
260
249
  history.push({ role: 'user', content: userText });
@@ -264,7 +253,6 @@ export async function runTui() {
264
253
  try {
265
254
  for (let iter = 0; iter < 25; iter++) {
266
255
  streamChars = 0;
267
- streamPreview = '';
268
256
  // Oto-compact: bağlam eşiği aşılınca otomatik özetle (uzun sohbet patlamasın)
269
257
  if (shouldAutoCompact(history)) {
270
258
  try {
@@ -277,28 +265,59 @@ export async function runTui() {
277
265
  }
278
266
  let answer = '';
279
267
  let toolCalls = [];
268
+ // ── CANLI akış İÇERİK AKIŞINA (footer'a değil): tamamlanan satırları, mesajın altından
269
+ // aşağı doğru, formatlı (kod renkli) bas. Açık kod-bloğu kapanana dek bekle (yarım fence
270
+ // bozuk renklenmesin). ✶ öneki yalnız bu cevabın İLK satırına; gerisi " " ile hizalı. ──
271
+ let committed = 0, emittedFirst = false, leadEmitted = false;
272
+ const flushStream = (final) => {
273
+ const lines = answer.split('\n');
274
+ let cut;
275
+ if (final)
276
+ cut = answer.length;
277
+ else {
278
+ let pos = 0, fence = false, lastSafe = 0;
279
+ for (let k = 0; k < lines.length - 1; k++) { // son eleman = yarım satır (newline gelmedi)
280
+ pos += lines[k].length + 1;
281
+ if (/^ *(`{3,}|~{3,})/.test(lines[k]))
282
+ fence = !fence;
283
+ if (!fence)
284
+ lastSafe = pos;
285
+ }
286
+ cut = lastSafe;
287
+ }
288
+ if (cut <= committed)
289
+ return;
290
+ const chunk = cleanModelText(answer.slice(committed, cut)).replace(/\n+$/, '');
291
+ committed = cut;
292
+ if (!chunk && !final)
293
+ return;
294
+ const md = markdownAnsi(chunk, cols()).split('\n');
295
+ let out = leadEmitted ? '' : (leadEmitted = true, '\n'); // cevabın üstüne 1 ayraç satır
296
+ out += md.map((ln) => emittedFirst ? ' ' + ln : (emittedFirst = true, paint('✶ ', theme.redBright, true) + ln)).join('\n') + '\n';
297
+ process.stdout.write(out); // imleç içerik bölgesinde → aşağı akar, taşınca scrollback'e
298
+ refresh(); // footer'ı dibe yeniden sabitle
299
+ };
280
300
  for await (const ev of streamChat(history, allToolSchemas(), config)) {
281
- // CANLI akış: answer biriktir + streamPreview güncelle. drawFooter'ı 120ms timer çizer (titreme yok).
282
301
  if (ev.type === 'text') {
283
302
  answer += ev.text;
284
303
  streamChars = answer.length;
285
- streamPreview = cleanModelText(answer);
304
+ flushStream(false);
286
305
  }
287
306
  else if (ev.type === 'done')
288
307
  toolCalls = ev.toolCalls || [];
289
- else if (ev.type === 'error')
308
+ else if (ev.type === 'error') {
290
309
  answer += `\n[hata: ${ev.error}]`;
310
+ flushStream(false);
311
+ }
291
312
  }
313
+ flushStream(true); // kalan yarım satırı bas
292
314
  answer = cleanModelText(answer).trim();
293
- streamPreview = ''; // canlı önizlemeyi kapat → footer küçülür, formatlı cevap içeriğe basılır
294
315
  const am = { role: 'assistant', content: answer || '' };
295
316
  if (toolCalls.length)
296
317
  am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
297
318
  history.push(am);
298
- if (answer) {
299
- lastAnswer = answer;
300
- printItem({ kind: 'assistant', text: answer });
301
- }
319
+ if (answer)
320
+ lastAnswer = answer; // gösterim akışta yapıldı; tekrar printItem YOK (duplikasyon olmaz)
302
321
  if (!toolCalls.length)
303
322
  break;
304
323
  // takılma koruması: aynı araç-çağrısı 3 kez üst üste → dur
@@ -335,7 +354,6 @@ export async function runTui() {
335
354
  busy = false;
336
355
  perm = null;
337
356
  ask = null;
338
- streamPreview = '';
339
357
  refresh();
340
358
  // Web-öğrenme: web'de arayıp cevap ürettiyse {soru→cevap}'ı eğitim datasına ekle
341
359
  if (usedWeb && lastAnswer) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.53",
3
+ "version": "1.0.55",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {