wormclaude 1.0.56 → 1.0.58

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
@@ -3,7 +3,7 @@
3
3
  // stdout'a BİR KEZ yazılır; ink yalnız canlı footer'ı yönetir. Böylece doğal kaydırma +
4
4
  // metin seçip kopyalama çalışır. Markdown + sözdizimi vurgusu burada ANSI'ye çevrilir.
5
5
  import { theme } from './theme.js';
6
- import { highlight } from './highlight.js';
6
+ import { highlight, isHighlightable } from './highlight.js';
7
7
  import { t } from './i18n.js';
8
8
  const RESET = '\x1b[0m';
9
9
  function hexAnsi(hex) {
@@ -90,8 +90,15 @@ export function markdownAnsi(text, cols) {
90
90
  const flushCode = () => {
91
91
  if (lang)
92
92
  out.push(paint(' ' + lang, theme.greyDim));
93
- for (const toks of highlight(code.join('\n'), lang)) {
94
- out.push(paint('│ ', theme.greyDim) + (toks.length ? toks.map((tk) => paint(tk.text, tk.color)).join('') : ' '));
93
+ if (isHighlightable(lang)) {
94
+ for (const toks of highlight(code.join('\n'), lang)) {
95
+ out.push(paint('│ ', theme.greyDim) + (toks.length ? toks.map((tk) => paint(tk.text, tk.color)).join('') : ' '));
96
+ }
97
+ }
98
+ else {
99
+ // Dilsiz/düz-metin blok: çubuk + nötr renk, sözdizimi-vurgusu YOK (saçma renk olmaz).
100
+ for (const ln of code)
101
+ out.push(paint('│ ', theme.greyDim) + paint(ln || ' ', theme.grey));
95
102
  }
96
103
  code = [];
97
104
  lang = '';
package/dist/highlight.js CHANGED
@@ -2,6 +2,13 @@
2
2
  // Kod bloklarını dile göre token'lara böler; her token'a tema rengi atar. Asla patlamaz —
3
3
  // tanımadığı dilde ortak kurallarla (string/sayı/yorum/anahtar kelime) renklendirir.
4
4
  import { theme } from './theme.js';
5
+ // Sözdizimi-vurgusu YALNIZ gerçek programlama dili etiketli bloklarda yapılır.
6
+ // Dil yoksa/düz-metin/markdown ise → renklendirme YOK (yoksa düz nesir "kod" sanılıp
7
+ // büyük-harfli kelimeler "tip", `in` gibi kelimeler "keyword" diye saçma renklenir).
8
+ const NON_CODE = new Set(['', 'text', 'txt', 'plain', 'plaintext', 'markdown', 'md', 'prose', 'none', 'output', 'log', 'console', 'raw']);
9
+ export function isHighlightable(lang) {
10
+ return !NON_CODE.has((lang || '').trim().toLowerCase());
11
+ }
5
12
  // Dil bazlı anahtar kelime setleri (ortak + spesifik).
6
13
  const KW = {
7
14
  js: ['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'new', 'class', 'extends', 'super', 'this', 'typeof', 'instanceof', 'in', 'of', 'await', 'async', 'yield', 'try', 'catch', 'finally', 'throw', 'import', 'export', 'from', 'default', 'delete', 'void', 'null', 'undefined', 'true', 'false'],
package/dist/i18n.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Çoklu dil (TR/EN) — arayüz metinleri. İlk açılışta dil seçilir, .wormclaude/settings.json'a yazılır.
2
2
  import * as fs from 'node:fs';
3
3
  import * as path from 'node:path';
4
- let current = 'tr';
4
+ let current = 'en'; // varsayılan: İngilizce (ilk kurulum). loadLang() kayıtlıysa onu uygular.
5
5
  export function setLang(l) { current = l; }
6
6
  export function getLang() { return current; }
7
7
  // Klasör bazlı ayar: her proje klasöründe bir kez sorulur (.wormclaude/settings.json)
@@ -67,6 +67,27 @@ const STR = {
67
67
  'mcp.errors': ' · {0} hata ({1})',
68
68
  'pill.running': '⏳ {0} çalışıyor',
69
69
  'pill.done': '✓ {0} görev bitti',
70
+ 'tui.footerHint': '/ komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış',
71
+ 'tui.working': 'çalışıyor…',
72
+ 'tui.chars': 'karakter',
73
+ 'tui.pasteSummary': '…(+{0} satır · {1} karakter, Enter ile gönder)',
74
+ 'tui.permLabel': 'İZİN',
75
+ 'tui.permRun': 'çalıştırılsın mı?',
76
+ 'tui.permYes': '[E] Evet',
77
+ 'tui.permAll': '[T] Tümüne izin',
78
+ 'tui.permNo': '[H] Hayır',
79
+ 'tui.ok': 'Tamam',
80
+ 'tui.ctrlcExit': 'Çıkmak için tekrar Ctrl+C (veya /cikis). Kopyalama Ctrl+C\'yi etkilemez.',
81
+ 'tui.copied': '✓ Panoya kopyalandı ({0} karakter).',
82
+ 'tui.nothingCopy': 'Kopyalanacak yanıt yok.',
83
+ 'tui.mcpConnected': '🔌 {0} MCP sunucusu bağlandı',
84
+ 'tui.autoCompacted': '✎ bağlam otomatik özetlendi',
85
+ 'tui.loopStop': 'Aynı adım tekrarlandı, döngü önlemek için durduruldu.',
86
+ 'tui.connErr': '[bağlantı hatası: {0}]',
87
+ 'tui.learned': '✎ eğitim datasına eklendi',
88
+ 'tui.userCancel': 'kullanıcı iptal etti',
89
+ 'tui.cmdErr': 'Komut hatası: {0}',
90
+ 'tui.langSet': 'Dil değiştirildi: Türkçe',
70
91
  },
71
92
  en: {
72
93
  'lang.title': 'Select language / Dil seçin',
@@ -106,6 +127,27 @@ const STR = {
106
127
  'mcp.errors': ' · {0} error ({1})',
107
128
  'pill.running': '⏳ {0} running',
108
129
  'pill.done': '✓ {0} task(s) done',
130
+ 'tui.footerHint': '/ commands · ↑↓ history · /copy · Ctrl+C exit',
131
+ 'tui.working': 'working…',
132
+ 'tui.chars': 'chars',
133
+ 'tui.pasteSummary': '…(+{0} lines · {1} chars, Enter to send)',
134
+ 'tui.permLabel': 'PERMISSION',
135
+ 'tui.permRun': 'run it?',
136
+ 'tui.permYes': '[Y] Yes',
137
+ 'tui.permAll': '[A] Allow all',
138
+ 'tui.permNo': '[N] No',
139
+ 'tui.ok': 'OK',
140
+ 'tui.ctrlcExit': 'Press Ctrl+C again to exit (or /quit). Copying does not trigger Ctrl+C.',
141
+ 'tui.copied': '✓ Copied to clipboard ({0} chars).',
142
+ 'tui.nothingCopy': 'No response to copy.',
143
+ 'tui.mcpConnected': '🔌 {0} MCP server(s) connected',
144
+ 'tui.autoCompacted': '✎ context auto-summarized',
145
+ 'tui.loopStop': 'Same step repeated, stopped to avoid a loop.',
146
+ 'tui.connErr': '[connection error: {0}]',
147
+ 'tui.learned': '✎ added to training data',
148
+ 'tui.userCancel': 'user cancelled',
149
+ 'tui.cmdErr': 'Command error: {0}',
150
+ 'tui.langSet': 'Language changed: English',
109
151
  },
110
152
  };
111
153
  // Komut açıklamaları (slash menüsü + /help)
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.56';
19
+ export const VERSION = '1.0.58';
package/dist/tui.js CHANGED
@@ -12,7 +12,7 @@ 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';
15
- import { cmdDesc } from './i18n.js';
15
+ import { cmdDesc, t, setLang, loadLang } from './i18n.js';
16
16
  import { getSkill, getSkills, buildSkillPrompt } from './skills.js';
17
17
  import { getExtCommand, getExtCommands, buildExtCommandPrompt } from './extensions.js';
18
18
  import { resolveAtMentions } from './atmention.js';
@@ -49,6 +49,7 @@ function fit(line, max) {
49
49
  const vis = (s) => stringWidth(s.replace(/\x1b\[[0-9;]*m/g, ''));
50
50
  const padVis = (s, w) => s + ' '.repeat(Math.max(0, w - vis(s))); // görünür genişliğe boşluk doldur
51
51
  export async function runTui() {
52
+ setLang(loadLang() || 'en'); // kayıtlı dil varsa onu, yoksa İngilizce (ilk kurulum)
52
53
  const config = loadConfig();
53
54
  let account = { plan: '', email: '', name: '' };
54
55
  // Ortam bağlamı (Windows/cwd) — model DOĞRU yol/komut kullansın (yoksa /home/user gibi yanlış yol yazar).
@@ -66,6 +67,11 @@ export async function runTui() {
66
67
  let inputBuf = '', busy = false, streamChars = 0, spin = 0;
67
68
  // 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.
68
69
  const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
70
+ // Canlı bağlam ölçer: son isteğin prompt_tokens'ı = o anki kullanılan bağlam (footer'da gösterilir).
71
+ const CTX_MAX = Number(process.env.WORMCLAUDE_CTX) || 12288;
72
+ let ctxUsed = 0;
73
+ const _k = (n) => (n >= 1000 ? (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k' : String(n));
74
+ const ctxGauge = () => `◷ ${_k(ctxUsed)}/${_k(CTX_MAX)} (%${Math.min(100, Math.round((ctxUsed / CTX_MAX) * 100))})`;
69
75
  // Giriş kutusunu genişliğe göre ÇOK SATIRA sar; çok uzunsa (yapıştırma) KISALTIP özet gösterir.
70
76
  const MAX_INPUT_LINES = 6;
71
77
  const inputBoxLines = (W) => {
@@ -87,7 +93,7 @@ export async function runTui() {
87
93
  // Çok uzun (büyük yapıştırma): son birkaç satır + üstte özet — footer'ı doldurmaz.
88
94
  const extra = wrapped.length - (MAX_INPUT_LINES - 1);
89
95
  const tail = wrapped.slice(-(MAX_INPUT_LINES - 1));
90
- const head = paint(`✶ (+${extra} satır · ${inputBuf.length} karakter, Enter ile gönder)`, theme.greyDim);
96
+ const head = paint('✶ ' + t('tui.pasteSummary', extra, inputBuf.length), theme.greyDim);
91
97
  return [head, ...tail.map((ln, i) => ' ' + paint(ln, theme.white) + (i === tail.length - 1 ? paint('▌', theme.greyDim) : ''))];
92
98
  };
93
99
  // Footer yüksekliği DİNAMİK: izin=4; değilse (durum/menü) + çizgi + giriş-satırları + çizgi.
@@ -141,7 +147,7 @@ export async function runTui() {
141
147
  await runSlashCommand(v, cmdCtx);
142
148
  }
143
149
  catch (e) {
144
- printItem({ kind: 'note', text: 'Komut hatası: ' + (e?.message || e) });
150
+ printItem({ kind: 'note', text: t('tui.cmdErr', e?.message || e) });
145
151
  }
146
152
  clearInterval(timer);
147
153
  busy = false;
@@ -149,7 +155,7 @@ export async function runTui() {
149
155
  }
150
156
  // ── İkonlu, 2-sütunlu bilgi başlığı (3 sol / 3 sağ) — responsive ──
151
157
  function headerLines(W) {
152
- const ctx = (Number(process.env.WORMCLAUDE_CTX) || 12288).toLocaleString('tr-TR');
158
+ const ctx = CTX_MAX.toLocaleString('tr-TR');
153
159
  // [ikon, etiket, değer, renk]
154
160
  const L = [
155
161
  ['◈', 'Version', 'v' + VERSION, theme.red],
@@ -158,7 +164,7 @@ export async function runTui() {
158
164
  ];
159
165
  const R = [
160
166
  ['◉', 'Account', account.email || '—', theme.grey],
161
- ['◷', 'Context', ctx + ' tokens', theme.grey],
167
+ ['◷', 'Context', ctx + ' tok (pencere)', theme.grey],
162
168
  ['▸', 'Workspace', process.cwd(), theme.grey],
163
169
  ];
164
170
  const cell = ([ic, lb, val, col]) => ' ' + paint(ic + ' ', theme.red) + paint(lb.padEnd(11), theme.greyDim) + paint(val, col);
@@ -181,9 +187,9 @@ export async function runTui() {
181
187
  let body;
182
188
  if (perm) {
183
189
  // İzin dialogu: araç adı + Evet/Tümü/Hayır
184
- const q = paint(' İZİN ', theme.redBright, true) + paint(perm.label, theme.white) + paint(' çalıştırılsın mı?', theme.grey);
185
- const opts = paint(' [E] Evet', theme.redBright, true) + paint(' · ', theme.greyDim)
186
- + paint('[T] Tümüne izin', theme.grey) + paint(' · ', theme.greyDim) + paint('[H] Hayır', theme.grey);
190
+ const q = paint(' ' + t('tui.permLabel') + ' ', theme.redBright, true) + paint(perm.label, theme.white) + paint(' ' + t('tui.permRun'), theme.grey);
191
+ const opts = paint(' ' + t('tui.permYes'), theme.redBright, true) + paint(' · ', theme.greyDim)
192
+ + paint(t('tui.permAll'), theme.grey) + paint(' · ', theme.greyDim) + paint(t('tui.permNo'), theme.grey);
187
193
  body = [line, q, opts, line];
188
194
  }
189
195
  else if (ask) {
@@ -208,9 +214,10 @@ export async function runTui() {
208
214
  body = [...menu, line, ...inputLines, line];
209
215
  }
210
216
  else {
217
+ const g = ctxUsed ? paint(' ' + ctxGauge(), theme.greyDim) : '';
211
218
  const status = busy
212
- ? paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey)
213
- : paint(' / komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış', theme.greyDim);
219
+ ? paint(` ${SPIN[spin % SPIN.length]} ${t('tui.working')}${streamChars ? ' ' + streamChars + ' ' + t('tui.chars') : ''}`, theme.grey) + g
220
+ : paint(' ' + t('tui.footerHint'), theme.greyDim) + g;
214
221
  body = [status, line, ...inputLines, line];
215
222
  }
216
223
  }
@@ -259,7 +266,7 @@ export async function runTui() {
259
266
  const { history: nh } = await runCompact(history, config);
260
267
  history.length = 0;
261
268
  history.push(...nh);
262
- printItem({ kind: 'note', text: '✎ bağlam otomatik özetlendi' });
269
+ printItem({ kind: 'note', text: t('tui.autoCompacted') });
263
270
  }
264
271
  catch { }
265
272
  }
@@ -303,8 +310,12 @@ export async function runTui() {
303
310
  streamChars = answer.length;
304
311
  flushStream(false);
305
312
  }
306
- else if (ev.type === 'done')
313
+ else if (ev.type === 'done') {
307
314
  toolCalls = ev.toolCalls || [];
315
+ const u = ev.usage;
316
+ if (u && u.input)
317
+ ctxUsed = u.input;
318
+ }
308
319
  else if (ev.type === 'error') {
309
320
  answer += `\n[hata: ${ev.error}]`;
310
321
  flushStream(false);
@@ -329,7 +340,7 @@ export async function runTui() {
329
340
  lastSig = sig;
330
341
  }
331
342
  if (sameCount >= 2) {
332
- printItem({ kind: 'note', text: 'Aynı adım tekrarlandı, döngü önlemek için durduruldu.' });
343
+ printItem({ kind: 'note', text: t('tui.loopStop') });
333
344
  break;
334
345
  }
335
346
  const results = await executeToolCalls(toolCalls, {
@@ -339,7 +350,7 @@ export async function runTui() {
339
350
  perm = { label: toolLabel(c.name, args), name: c.name, resolve };
340
351
  refresh();
341
352
  }),
342
- ask: (q) => new Promise((resolve) => { ask = { question: q.question, options: q.options.length ? q.options : [{ label: 'Tamam' }], sel: 0, resolve }; refresh(); }),
353
+ ask: (q) => new Promise((resolve) => { ask = { question: q.question, options: q.options.length ? q.options : [{ label: t('tui.ok') }], sel: 0, resolve }; refresh(); }),
343
354
  onResult: (c, _i, res) => { if ((c.name === 'WebSearch' || c.name === 'WebFetch') && res.ok)
344
355
  usedWeb = true; printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }); },
345
356
  });
@@ -348,7 +359,7 @@ export async function runTui() {
348
359
  }
349
360
  }
350
361
  catch (e) {
351
- printItem({ kind: 'note', text: `[bağlantı hatası: ${e?.message || e}]` });
362
+ printItem({ kind: 'note', text: t('tui.connErr', e?.message || e) });
352
363
  }
353
364
  clearInterval(timer);
354
365
  busy = false;
@@ -359,7 +370,7 @@ export async function runTui() {
359
370
  if (usedWeb && lastAnswer) {
360
371
  const sources = (lastAnswer.match(/https?:\/\/[^\s<>"')\]]+/g) || []).slice(0, 8);
361
372
  if (recordLearned(userText, lastAnswer, sources, config))
362
- printItem({ kind: 'note', text: '✎ eğitim datasına eklendi' });
373
+ printItem({ kind: 'note', text: t('tui.learned') });
363
374
  }
364
375
  // Oto-hafıza: eşik geçildiyse arka planda hafızayı güncelle
365
376
  if (shouldExtract(history))
@@ -380,7 +391,7 @@ export async function runTui() {
380
391
  redrawAll();
381
392
  } }).catch(() => { });
382
393
  connectMcpServers().then((srv) => { if (srv && srv.length)
383
- printItem({ kind: 'note', text: `🔌 ${srv.length} MCP sunucusu bağlandı` }); }).catch(() => { }); // MCP araçları (varsa)
394
+ printItem({ kind: 'note', text: t('tui.mcpConnected', srv.length) }); }).catch(() => { }); // MCP araçları (varsa)
384
395
  const quit = () => { process.stdout.write('\x1b[r\x1b[?25h\x1b[2J\x1b[3J\x1b[H'); process.exit(0); };
385
396
  process.on('exit', () => { try {
386
397
  process.stdout.write('\x1b[r\x1b[?25h');
@@ -428,26 +439,26 @@ export async function runTui() {
428
439
  const r = perm.resolve, nm = perm.name;
429
440
  if (key && key.ctrl && key.name === 'c') {
430
441
  perm = null;
431
- r({ deny: 'kullanıcı iptal etti' });
442
+ r({ deny: t('tui.userCancel') });
432
443
  refresh();
433
444
  return;
434
445
  }
435
- if (k === 'e' || k === '1' || (key && key.name === 'return')) {
446
+ if (k === 'e' || k === 'y' || k === '1' || (key && key.name === 'return')) {
436
447
  perm = null;
437
448
  r('allow');
438
449
  refresh();
439
- }
440
- else if (k === 't' || k === '2') {
450
+ } // Evet / Yes
451
+ else if (k === 't' || k === 'a' || k === '2') {
441
452
  allowAll.add(nm);
442
453
  perm = null;
443
454
  r('allow');
444
455
  refresh();
445
- }
446
- else if (k === 'h' || k === '3' || (key && key.name === 'escape')) {
456
+ } // Tümü / Allow all
457
+ else if (k === 'h' || k === 'n' || k === '3' || (key && key.name === 'escape')) {
447
458
  perm = null;
448
459
  r({ deny: '' });
449
460
  refresh();
450
- }
461
+ } // Hayır / No
451
462
  return;
452
463
  }
453
464
  if (key && key.ctrl && key.name === 'c') {
@@ -461,7 +472,7 @@ export async function runTui() {
461
472
  quit();
462
473
  }
463
474
  ctrlcAt = now;
464
- printItem({ kind: 'note', text: 'Çıkmak için tekrar Ctrl+C (veya /cikis). Kopyalama Ctrl+C\'yi etkilemez.' });
475
+ printItem({ kind: 'note', text: t('tui.ctrlcExit') });
465
476
  return;
466
477
  }
467
478
  if (key && key.ctrl && key.name === 'd') {
@@ -531,7 +542,7 @@ export async function runTui() {
531
542
  executeTool('Bash', { command: cmd }).then((res) => {
532
543
  printItem({ kind: 'tool', label: `! ${cmd.slice(0, 60)}`, result: sanitizeOutput(res.output), ok: res.ok });
533
544
  history.push({ role: 'user', content: `Şu shell komutunu çalıştırdım:\n\`\`\`\n${cmd}\n\`\`\`\nÇıktı:\n\`\`\`\n${(res.output || '').slice(0, 4000)}\n\`\`\`` });
534
- }).catch((e) => printItem({ kind: 'note', text: 'Komut hatası: ' + (e?.message || e) }))
545
+ }).catch((e) => printItem({ kind: 'note', text: t('tui.cmdErr', e?.message || e) }))
535
546
  .finally(() => { busy = false; refresh(); });
536
547
  return;
537
548
  }
@@ -553,10 +564,10 @@ export async function runTui() {
553
564
  const last = [...history].reverse().find((mm) => mm.role === 'assistant');
554
565
  if (last) {
555
566
  process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
556
- printItem({ kind: 'note', text: `✓ Panoya kopyalandı (${last.content.length} karakter).` });
567
+ printItem({ kind: 'note', text: t('tui.copied', last.content.length) });
557
568
  }
558
569
  else
559
- printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
570
+ printItem({ kind: 'note', text: t('tui.nothingCopy') });
560
571
  return;
561
572
  }
562
573
  const builtin = COMMANDS.some((c) => c.name === tok);
@@ -572,6 +583,11 @@ export async function runTui() {
572
583
  runTurn(buildExtCommandPrompt(ext, a), `/${ext.name}${a ? ' ' + a : ''}`);
573
584
  return;
574
585
  }
586
+ // /lang|/dil: dil değişince banner alt-yazısı + footer yeni dilde olsun → tüm ekranı yeniden çiz
587
+ if (tok === '/lang') {
588
+ runCommand(v).then(() => redrawAll());
589
+ return;
590
+ }
575
591
  runCommand(v);
576
592
  return;
577
593
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.56",
3
+ "version": "1.0.58",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {