wormclaude 1.0.43 → 1.0.45

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
@@ -145,17 +145,17 @@ export function itemAnsi(it, cols) {
145
145
  if (it.kind === 'banner')
146
146
  return '\n' + bannerAnsi(cols);
147
147
  if (it.kind === 'user') {
148
- // Kenarlıksız (kutu, küçültünce bozuluyordu) — sadece " metin", resize'da temiz sarılır.
149
- const body = wrap(it.text || '', W - 2).map((ln, i) => (i === 0 ? paint(' ', theme.greyDim, true) : ' ') + paint(ln, theme.white));
148
+ // Kenarlıksız (kutu, küçültünce bozuluyordu) — sadece " metin", resize'da temiz sarılır.
149
+ const body = wrap(it.text || '', W - 2).map((ln, i) => (i === 0 ? paint(' ', theme.greyDim, true) : ' ') + paint(ln, theme.white));
150
150
  return '\n' + body.join('\n');
151
151
  }
152
152
  if (it.kind === 'assistant') {
153
153
  const md = markdownAnsi(it.text || '', cols).split('\n');
154
- return '\n' + md.map((ln, i) => (i === 0 ? paint(' ', theme.redBright, true) + ln : ' ' + ln)).join('\n');
154
+ return '\n' + md.map((ln, i) => (i === 0 ? paint(' ', theme.redBright, true) + ln : ' ' + ln)).join('\n');
155
155
  }
156
156
  if (it.kind === 'tool') {
157
157
  const n = (it.result || '').split('\n').length, chars = (it.result || '').length;
158
- const head = paint(' ', theme.redBright, true) + paint(it.label || '', theme.white);
158
+ const head = paint(' ', theme.redBright, true) + paint(it.label || '', theme.white);
159
159
  const sub = paint(' ⎿ ', theme.greyDim) + (it.ok
160
160
  ? paint(`${n} ${t('common.lines') || 'satır'} (${chars})`, theme.grey)
161
161
  : paint('✗ ' + (it.result || '').slice(0, 160), theme.errorRed));
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.43';
19
+ export const VERSION = '1.0.45';
package/dist/tui.js CHANGED
@@ -11,6 +11,7 @@ import { sanitizeOutput } from './errorsan.js';
11
11
  import { itemAnsi } from './ansi.js';
12
12
  import { theme, VERSION } from './theme.js';
13
13
  import { cleanModelText } from './textclean.js';
14
+ import { COMMANDS, runSlashCommand } from './commands.js';
14
15
  const RESET = '\x1b[0m';
15
16
  const hex = (h) => { const m = /^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(h); return m ? `\x1b[38;2;${parseInt(m[1], 16)};${parseInt(m[2], 16)};${parseInt(m[3], 16)}m` : ''; };
16
17
  const paint = (s, c, bold = false) => `${bold ? '\x1b[1m' : ''}${c ? hex(c) : ''}${s}${RESET}`;
@@ -38,6 +39,7 @@ function fit(line, max) {
38
39
  return out + RESET;
39
40
  }
40
41
  const vis = (s) => stringWidth(s.replace(/\x1b\[[0-9;]*m/g, ''));
42
+ const padVis = (s, w) => s + ' '.repeat(Math.max(0, w - vis(s))); // görünür genişliğe boşluk doldur
41
43
  export async function runTui() {
42
44
  const config = loadConfig();
43
45
  let account = { plan: '', email: '', name: '' };
@@ -59,13 +61,59 @@ export async function runTui() {
59
61
  const allowAll = new Set(); // "Tümüne izin" denen araçlar (oturum boyu)
60
62
  const inputHistory = []; // gönderilen mesajlar (↑/↓ ile geri çağrılır)
61
63
  let histIdx = -1; // -1 = geçmişte gezinmiyor
62
- // ── Claude tarzı başlık (model/plan/mail/cwd) responsive ──
64
+ let cmdSel = 0; // slash menüde seçili komut
65
+ // Slash menü: "/" ile başlayan girişe uyan komutlar
66
+ const cmdMatches = () => {
67
+ if (busy || !inputBuf.startsWith('/'))
68
+ return [];
69
+ const tok = inputBuf.split(' ')[0];
70
+ return COMMANDS.filter((c) => c.name.startsWith(tok));
71
+ };
72
+ // Slash komutları için bağlam (cli.tsx ile aynı arayüz) — /clear,/config,/model,/memory,/program…
73
+ const cmdCtx = {
74
+ config,
75
+ getHistory: () => history,
76
+ setHistory: (h) => { history.length = 0; history.push(...h); },
77
+ note: (text) => printItem({ kind: 'note', text }),
78
+ assistant: (text) => printItem({ kind: 'assistant', text }),
79
+ clearConv: () => { history.length = 1; displayItems.length = 1; redrawAll(); }, // sistem msg + banner kalır
80
+ exit: () => quit(),
81
+ };
82
+ async function runCommand(v) {
83
+ busy = true;
84
+ drawFooter();
85
+ const timer = setInterval(() => { spin++; if (busy && !perm)
86
+ drawFooter(); }, 120);
87
+ try {
88
+ await runSlashCommand(v, cmdCtx);
89
+ }
90
+ catch (e) {
91
+ printItem({ kind: 'note', text: 'Komut hatası: ' + (e?.message || e) });
92
+ }
93
+ clearInterval(timer);
94
+ busy = false;
95
+ drawFooter();
96
+ }
97
+ // ── İkonlu, 2-sütunlu bilgi başlığı (3 sol / 3 sağ) — responsive ──
63
98
  function headerLines(W) {
64
- const a = paint(' WormClaude ', theme.greyDim) + paint('v' + VERSION, theme.red, true)
65
- + paint(' · model: ', theme.greyDim) + paint(config.model, theme.redBright)
66
- + (account.plan ? paint(' · plan: ', theme.greyDim) + paint(account.plan, theme.white) : '');
67
- const b = paint(' ' + (account.email ? account.email + ' · ' : '') + process.cwd(), theme.greyDim);
68
- return [a, b].map((l) => fit(l, W));
99
+ const ctx = (Number(process.env.WORMCLAUDE_CTX) || 12288).toLocaleString('tr-TR');
100
+ // [ikon, etiket, değer, renk]
101
+ const L = [
102
+ ['', 'Version', 'v' + VERSION, theme.red],
103
+ ['◆', 'Model', config.model, theme.redBright],
104
+ ['★', 'Plan', (account.plan || '—').toUpperCase(), theme.white],
105
+ ];
106
+ const R = [
107
+ ['◉', 'Account', account.email || '—', theme.grey],
108
+ ['◷', 'Context', ctx + ' tokens', theme.grey],
109
+ ['▸', 'Workspace', process.cwd(), theme.grey],
110
+ ];
111
+ const cell = ([ic, lb, val, col]) => ' ' + paint(ic + ' ', theme.red) + paint(lb.padEnd(9), theme.greyDim) + paint(val, col);
112
+ const colW = Math.max(28, Math.floor(W / 2)); // sol sütun genişliği
113
+ const narrow = W < 64;
114
+ if (narrow)
115
+ return [...L, ...R].map((r) => fit(cell(r), W)); // dar ekran → alt alta
116
+ return [0, 1, 2].map((i) => fit(padVis(cell(L[i]), colW) + cell(R[i]), W)); // geniş → 2 sütun
69
117
  }
70
118
  // ── Scroll region: üst = içerik (kayar), alt = sabit footer ──
71
119
  function setRegion() {
@@ -90,11 +138,21 @@ export async function runTui() {
90
138
  const avail = W - 3;
91
139
  if (vis(shown) > avail)
92
140
  shown = '…' + shown.slice(-(avail - 1));
93
- const inputLine = paint(' ', theme.redBright, true) + paint(shown, theme.white) + paint('▌', theme.greyDim);
94
- // Durum satırı KUTUNUN ÜSTÜNDE (en altta değil): çalışırken spinner, boştayken ipucu.
95
- const status = busy
96
- ? paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey)
97
- : paint(' /kopyala · /help komutlar · ↑↓ geçmiş · Ctrl+C çıkış', theme.greyDim);
141
+ const inputLine = paint(' ', theme.redBright, true) + paint(shown, theme.white) + paint('▌', theme.greyDim);
142
+ // Durum satırı KUTUNUN ÜSTÜNDE: slash menü > spinner > ipucu.
143
+ const m = cmdMatches();
144
+ let status;
145
+ if (m.length) {
146
+ const sel = Math.min(cmdSel, m.length - 1);
147
+ const names = m.slice(0, 8).map((c, i) => i === sel ? paint(c.name, theme.redBright, true) : paint(c.name, theme.greyDim)).join(' ');
148
+ status = ' ' + names + paint(' ↹ tamamla · ↑↓ seç', theme.greyDim);
149
+ }
150
+ else if (busy) {
151
+ status = paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey);
152
+ }
153
+ else {
154
+ status = paint(' / komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış', theme.greyDim);
155
+ }
98
156
  body = [status, line, inputLine, line];
99
157
  }
100
158
  let out = '\x1b7'; // imleci kaydet
@@ -250,8 +308,14 @@ export async function runTui() {
250
308
  if (key && key.ctrl && key.name === 'd') {
251
309
  quit();
252
310
  }
253
- // ↑/↓ — geçmiş mesajlarda gezin (shell tarzı komut geçmişi)
311
+ // ↑/↓ — slash menü açıksa komut seç, değilse geçmiş mesajlarda gezin
254
312
  if (key && key.name === 'up') {
313
+ const m = cmdMatches();
314
+ if (m.length) {
315
+ cmdSel = (cmdSel - 1 + m.length) % m.length;
316
+ drawFooter();
317
+ return;
318
+ }
255
319
  if (inputHistory.length) {
256
320
  histIdx = histIdx < 0 ? inputHistory.length - 1 : Math.max(0, histIdx - 1);
257
321
  inputBuf = inputHistory[histIdx];
@@ -260,6 +324,12 @@ export async function runTui() {
260
324
  return;
261
325
  }
262
326
  if (key && key.name === 'down') {
327
+ const m = cmdMatches();
328
+ if (m.length) {
329
+ cmdSel = (cmdSel + 1) % m.length;
330
+ drawFooter();
331
+ return;
332
+ }
263
333
  if (histIdx >= 0) {
264
334
  histIdx++;
265
335
  if (histIdx >= inputHistory.length) {
@@ -272,37 +342,59 @@ export async function runTui() {
272
342
  }
273
343
  return;
274
344
  }
345
+ // Tab — seçili slash komutunu tamamla
346
+ if (key && key.name === 'tab') {
347
+ const m = cmdMatches();
348
+ if (m.length) {
349
+ inputBuf = m[Math.min(cmdSel, m.length - 1)].name + ' ';
350
+ cmdSel = 0;
351
+ drawFooter();
352
+ }
353
+ return;
354
+ }
275
355
  if (key && key.name === 'return') {
276
356
  if (busy)
277
357
  return; // tur sürerken Enter beklemede; yazılan metin durur (type-ahead)
278
- const v = inputBuf.trim();
358
+ let v = inputBuf.trim();
279
359
  inputBuf = '';
280
360
  histIdx = -1;
281
361
  if (!v) {
282
362
  drawFooter();
283
363
  return;
284
364
  }
285
- if (v)
286
- inputHistory.push(v); // geçmişe ekle (↑ ile geri çağrılır)
287
- if (v === '/cikis' || v === '/exit' || v === '/quit') {
288
- quit();
289
- }
290
- if (v === '/kopyala' || v === '/copy') {
291
- const last = [...history].reverse().find((m) => m.role === 'assistant');
292
- if (last) {
293
- process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
294
- printItem({ kind: 'note', text: `✓ Panoya kopyalandı (${last.content.length} karakter).` });
365
+ // Slash komutu: tam eşleşme yoksa menüde SEÇİLİ olanı kullan
366
+ if (v.startsWith('/')) {
367
+ const tok = v.split(' ')[0];
368
+ const matches = COMMANDS.filter((c) => c.name.startsWith(tok));
369
+ if (!COMMANDS.some((c) => c.name === tok) && matches.length)
370
+ v = matches[Math.min(cmdSel, matches.length - 1)].name;
371
+ cmdSel = 0;
372
+ if (v === '/cikis' || v === '/exit' || v === '/quit') {
373
+ quit();
374
+ return;
295
375
  }
296
- else
297
- printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
376
+ if (v === '/kopyala' || v === '/copy') {
377
+ const last = [...history].reverse().find((mm) => mm.role === 'assistant');
378
+ if (last) {
379
+ process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
380
+ printItem({ kind: 'note', text: `✓ Panoya kopyalandı (${last.content.length} karakter).` });
381
+ }
382
+ else
383
+ printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
384
+ return;
385
+ }
386
+ inputHistory.push(v);
387
+ runCommand(v);
298
388
  return;
299
389
  }
390
+ inputHistory.push(v); // geçmişe ekle (↑ ile geri çağrılır)
300
391
  printItem({ kind: 'user', text: v });
301
392
  runTurn(v);
302
393
  return;
303
394
  }
304
395
  if (key && key.name === 'backspace') {
305
396
  inputBuf = inputBuf.slice(0, -1);
397
+ cmdSel = 0;
306
398
  drawFooter();
307
399
  return;
308
400
  }
@@ -314,6 +406,7 @@ export async function runTui() {
314
406
  // sadece gerçek yazdırılabilir karakter (her zaman → cevap üretilirken bile type-ahead)
315
407
  if (str && !key?.ctrl && !key?.meta && !str.startsWith('\x1b') && !/[\x00-\x1f]/.test(str)) {
316
408
  inputBuf += str;
409
+ cmdSel = 0;
317
410
  drawFooter();
318
411
  }
319
412
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.43",
3
+ "version": "1.0.45",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {