wormclaude 1.0.22 → 1.0.24

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/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import React, { useState, useRef, useEffect } from 'react';
3
- import { render, Box, Text, Static, useApp, useInput } from 'ink';
3
+ import { render, Box, Text, useApp, useInput } from 'ink';
4
4
  import TextInput from 'ink-text-input';
5
5
  import * as path from 'node:path';
6
6
  import { theme, VERSION } from './theme.js';
@@ -11,6 +11,7 @@ import { allToolSchemas, executeToolCalls, executeTool, toolLabel, setToolConfig
11
11
  import { sanitizeError, sanitizeOutput } from './errorsan.js';
12
12
  import { cleanModelText } from './textclean.js';
13
13
  import { MarkdownDisplay } from './markdown.js';
14
+ import { highlight } from './highlight.js';
14
15
  import { resolveAtMentions } from './atmention.js';
15
16
  import { summarizeTools } from './toolSummary.js';
16
17
  import { pickTipId, tipText } from './tips.js';
@@ -85,10 +86,22 @@ const _initHistory = () => {
85
86
  sys.push({ role: 'system', content: _memCtx });
86
87
  return sys;
87
88
  };
88
- // NORMAL buffer (alt-screen YOK) — transcript <Static> ile gerçek terminal scrollback'ine
89
- // basılır. Böylece terminalin doğal kaydırması (fare tekerleği / Shift+PageUp) ve metin
90
- // seçip kopyalama tam çalışır. Eskiden ?1049h alt-screen kullanılıyordu; o, scrollback'i ve
91
- // kopyalamayı engelliyordu kaldırıldı.
89
+ // ALT-SCREEN (alternate screen) — ink normal buffer'da resize'da eski dinamik çerçeveyi
90
+ // silemeyip KASKAD yapıyor (scrollback reflow). Alt-screen tüm ekranı yönetir resize'da
91
+ // temiz yeniden çizim, kaskad YOK. Kopyalama: alt-screen'de fare seçimi sınırlı olduğundan
92
+ // /kopyala komutu (OSC52 ile panoya) sağlanır. ?1049h alt-screen · ?1007h alternate-scroll.
93
+ const _TUI = !!process.env.WORMCLAUDE_TUI; // özel renderer (deneysel) → ink'i atla
94
+ if (!_TUI) {
95
+ try {
96
+ process.stdout.write('\x1b[?1049h\x1b[?1007h\x1b[2J\x1b[H');
97
+ }
98
+ catch { }
99
+ const _leaveAlt = () => { try {
100
+ process.stdout.write('\x1b[?1007l\x1b[?1049l');
101
+ }
102
+ catch { } };
103
+ process.on('exit', _leaveAlt);
104
+ }
92
105
  function useDimensions() {
93
106
  const [d, setD] = useState({
94
107
  cols: process.stdout.columns || 80,
@@ -148,6 +161,91 @@ function wrapText(text, width) {
148
161
  }
149
162
  return out;
150
163
  }
164
+ // Inline markdown (`code` **bold**) → segment'ler.
165
+ function inlineSegs(text, base) {
166
+ const segs = [];
167
+ const re = /(`[^`]+`|\*\*[^*]+\*\*)/g;
168
+ let last = 0;
169
+ let m;
170
+ while ((m = re.exec(text)) !== null) {
171
+ if (m.index > last)
172
+ segs.push({ text: text.slice(last, m.index), color: base });
173
+ const f = m[0];
174
+ if (f.startsWith('`'))
175
+ segs.push({ text: f.slice(1, -1), color: theme.green });
176
+ else
177
+ segs.push({ text: f.slice(2, -2), color: base, bold: true });
178
+ last = re.lastIndex;
179
+ }
180
+ if (last < text.length)
181
+ segs.push({ text: text.slice(last), color: base });
182
+ return segs.length ? segs : [{ text, color: base }];
183
+ }
184
+ // Markdown metnini segment-satırlarına çevirir; kod blokları sözdizimi-vurgulu (highlight()).
185
+ function mdToSegLines(text, W) {
186
+ const out = [];
187
+ const white = theme.white;
188
+ let inCode = false, fence = '', lang = '', code = [];
189
+ const fenceRe = /^ *(`{3,}|~{3,}) *(\w*)? *$/;
190
+ const flush = () => {
191
+ if (lang)
192
+ out.push([{ text: ' ' + lang, dim: true }]);
193
+ for (const toks of highlight(code.join('\n'), lang)) {
194
+ const segs = [{ text: '│ ', dim: true }];
195
+ if (toks.length)
196
+ for (const tk of toks)
197
+ segs.push({ text: tk.text, color: tk.color });
198
+ else
199
+ segs.push({ text: ' ' });
200
+ out.push(segs);
201
+ }
202
+ code = [];
203
+ lang = '';
204
+ fence = '';
205
+ };
206
+ for (const line of text.split('\n')) {
207
+ if (inCode) {
208
+ const fm = line.match(fenceRe);
209
+ if (fm && fm[1][0] === fence[0] && fm[1].length >= fence.length) {
210
+ inCode = false;
211
+ flush();
212
+ }
213
+ else
214
+ code.push(line);
215
+ continue;
216
+ }
217
+ const fm = line.match(fenceRe);
218
+ if (fm) {
219
+ inCode = true;
220
+ fence = fm[1];
221
+ lang = (fm[2] || '').trim();
222
+ continue;
223
+ }
224
+ const hm = line.match(/^ *(#{1,4}) +(.*)/);
225
+ const um = line.match(/^([ \t]*)([-*+]) +(.*)/);
226
+ const om = line.match(/^([ \t]*)(\d+)\. +(.*)/);
227
+ if (hm) {
228
+ const lvl = hm[1].length;
229
+ out.push([{ text: hm[2], color: lvl <= 2 ? theme.redBright : white, bold: lvl <= 3, italic: lvl > 3 }]);
230
+ }
231
+ else if (um || om) {
232
+ const [, ws, marker, itemText] = (um || om);
233
+ const prefix = om ? `${marker}. ` : '• ';
234
+ const wrapped = wrapText(itemText, Math.max(8, W - ws.length - prefix.length));
235
+ wrapped.forEach((w, i) => out.push([{ text: ' '.repeat(ws.length) + (i === 0 ? prefix : ' '), dim: true }, ...inlineSegs(w, white)]));
236
+ }
237
+ else if (line.trim() === '') {
238
+ out.push([]);
239
+ }
240
+ else {
241
+ for (const w of wrapText(line, W))
242
+ out.push(inlineSegs(w, white));
243
+ }
244
+ }
245
+ if (inCode)
246
+ flush();
247
+ return out;
248
+ }
151
249
  // Tüm öğeleri görsel satır dizisine çevirir (kaydırma satır granülerliğinde).
152
250
  function buildLines(items, cols) {
153
251
  const lines = [];
@@ -171,7 +269,8 @@ function buildLines(items, cols) {
171
269
  lines.push([{ text: '╰' + '─'.repeat(W - 2) + '╯', dim: true }]);
172
270
  }
173
271
  else if (it.kind === 'assistant') {
174
- wrapText(it.text, W - 2).forEach((ln, i) => lines.push(i === 0 ? [{ text: '⏺ ', color: redB, bold: true }, { text: ln, color: white }] : [{ text: ' ' + ln, color: white }]));
272
+ const seg = mdToSegLines(it.text, W - 2);
273
+ seg.forEach((segs, i) => lines.push(i === 0 ? [{ text: '⏺ ', color: redB, bold: true }, ...segs] : [{ text: ' ', dim: true }, ...segs]));
175
274
  }
176
275
  else if (it.kind === 'tool') {
177
276
  const n = it.result.split('\n').length, chars = it.result.length;
@@ -440,9 +539,24 @@ function App() {
440
539
  else if (key.downArrow)
441
540
  setCmdSel((s) => (s + 1) % n);
442
541
  });
443
- // NOT: Transcript artık <Static> ile terminal scrollback'ine basılıyor; kaydırma
444
- // terminalin kendi işi (fare tekerleği / Shift+PageUp). Tuşları YAKALAMIYORUZ ki
445
- // doğal kaydırma + metin seçip kopyalama bozulmasın.
542
+ // Geçmişte kaydırma: PageUp/PageDown her zaman; boş input'ta ok tuşları (fare tekerleği
543
+ // alternate-scroll ile ok yollar tekerlekle kaydırma). Alt-screen olduğundan biz yönetiriz.
544
+ useInput((_inp, key) => {
545
+ if (!started || perm || ask || lang === null)
546
+ return;
547
+ if (key.pageUp)
548
+ setScroll((s) => s + 10);
549
+ else if (key.pageDown)
550
+ setScroll((s) => Math.max(0, s - 10));
551
+ else if (input === '' && !busy) {
552
+ if (key.upArrow)
553
+ setScroll((s) => s + 3);
554
+ else if (key.downArrow)
555
+ setScroll((s) => Math.max(0, s - 3));
556
+ }
557
+ });
558
+ // Yeni mesaj gelince en alta dön
559
+ useEffect(() => { setScroll(0); }, [items.length]);
446
560
  // Başlangıçta güven seviyesini (Doğrulanmış Araştırmacı Programı) çek → StatusLine rozeti
447
561
  useEffect(() => {
448
562
  if (!started)
@@ -695,6 +809,31 @@ function App() {
695
809
  v = filtered[Math.min(cmdSel, filtered.length - 1)].name;
696
810
  }
697
811
  const tok = v.split(' ')[0];
812
+ // /kopyala [hepsi] — son yanıtı (veya tüm sohbeti) panoya kopyalar (OSC52, fare gerekmez).
813
+ if (tok === '/kopyala' || tok === '/copy') {
814
+ const arg = v.slice(tok.length).trim().toLowerCase();
815
+ const en = getLang() === 'en';
816
+ const asst = items.filter((it) => it.kind === 'assistant').map((it) => it.text);
817
+ let text = '';
818
+ if (arg === 'all' || arg === 'hepsi') {
819
+ text = items.map((it) => it.kind === 'user' ? '> ' + it.text : it.kind === 'assistant' ? it.text : '').filter(Boolean).join('\n\n');
820
+ }
821
+ else
822
+ text = asst[asst.length - 1] || '';
823
+ if (!text) {
824
+ push({ kind: 'note', text: en ? 'Nothing to copy.' : 'Kopyalanacak içerik yok.' });
825
+ return;
826
+ }
827
+ try {
828
+ const b64 = Buffer.from(text, 'utf8').toString('base64');
829
+ process.stdout.write(`\x1b]52;c;${b64}\x07`);
830
+ push({ kind: 'note', text: en ? `✓ Copied to clipboard (${text.length} chars).` : `✓ Panoya kopyalandı (${text.length} karakter).` });
831
+ }
832
+ catch {
833
+ push({ kind: 'note', text: en ? 'Copy failed.' : 'Kopyalama başarısız.' });
834
+ }
835
+ return;
836
+ }
698
837
  // /agent ve /multi-agent — tek / çoklu alt-agent
699
838
  if (tok === '/agent' || tok === '/multi-agent') {
700
839
  const task = v.slice(tok.length).trim();
@@ -788,9 +927,10 @@ function App() {
788
927
  runAgent(v);
789
928
  }
790
929
  const { cols, rows } = useDimensions();
791
- // İlk açılış: dil seçimi (banner stdout'a basıldı; burada yalnız seçim footer'ı)
930
+ // İlk açılış: dil seçimi
792
931
  if (lang === null) {
793
- return (React.createElement(Box, { flexDirection: "column", width: cols },
932
+ return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
933
+ React.createElement(Banner, null),
794
934
  React.createElement(Box, { flexDirection: "column", marginTop: 1, paddingX: 1 },
795
935
  React.createElement(Text, { color: theme.redBright, bold: true }, t('lang.title')),
796
936
  React.createElement(Box, { flexDirection: "column", marginTop: 1 },
@@ -802,11 +942,14 @@ function App() {
802
942
  t('lang.en'))),
803
943
  React.createElement(Text, { color: theme.greyDim },
804
944
  " ",
805
- t('lang.hint')))));
945
+ t('lang.hint'))),
946
+ React.createElement(Box, { flexGrow: 1 }),
947
+ React.createElement(StatusLine, { model: config.model, ctxTokens: 0 })));
806
948
  }
807
- // Açılış: klasöre güven sorusu (banner stdout'ta; burada yalnız soru footer'ı)
949
+ // Açılış: klasöre güven sorusu (WormClaude tarzı)
808
950
  if (!started) {
809
- return (React.createElement(Box, { flexDirection: "column", width: cols },
951
+ return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
952
+ React.createElement(Banner, null),
810
953
  React.createElement(Box, { flexDirection: "column", marginTop: 1, paddingX: 1 },
811
954
  React.createElement(Text, { color: theme.redBright, bold: true }, t('trust.accessing')),
812
955
  React.createElement(Text, null, " "),
@@ -824,14 +967,36 @@ function App() {
824
967
  t('trust.no'))),
825
968
  React.createElement(Text, { color: theme.greyDim },
826
969
  " ",
827
- t('trust.hint')))));
970
+ t('trust.hint'))),
971
+ React.createElement(Box, { flexGrow: 1 }),
972
+ React.createElement(StatusLine, { model: config.model, ctxTokens: 0 })));
828
973
  }
829
- return (React.createElement(Box, { flexDirection: "column", width: cols },
830
- React.createElement(Static, { items: items }, (item, i) => React.createElement(RenderItem, { key: i, item: item })),
831
- streaming ? (React.createElement(Box, { marginTop: 1, flexDirection: "row" },
832
- React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
833
- React.createElement(Box, { flexDirection: "column", flexGrow: 1 },
834
- React.createElement(MarkdownDisplay, { text: streaming })))) : null,
974
+ return (React.createElement(Box, { flexDirection: "column", height: rows, width: cols },
975
+ React.createElement(Box, { flexDirection: "column", flexGrow: 1, overflow: "hidden" }, (() => {
976
+ // Alt-screen + satır-pencere pager: tüm öğeleri satıra çevir, pencere göster.
977
+ // (Kopyalama /kopyala komutuyla; resize'da kaskad olmaz.)
978
+ const all = buildLines(items, cols);
979
+ const reserve = busy ? 4 : 6;
980
+ const avail = Math.max(4, rows - reserve - (streaming ? 3 : 0) - 2);
981
+ const maxScroll = Math.max(0, all.length - avail);
982
+ const off = Math.min(scroll, maxScroll);
983
+ const startLine = Math.max(0, all.length - avail - off);
984
+ const view = all.slice(startLine, startLine + avail);
985
+ return (React.createElement(React.Fragment, null,
986
+ startLine > 0 ? React.createElement(Text, { color: theme.greyDim },
987
+ " \u2191 ",
988
+ startLine,
989
+ " sat\u0131r \u00B7 PageUp") : null,
990
+ view.map((segs, i) => (React.createElement(Text, { key: i }, segs.length === 0 ? ' ' : segs.map((s, j) => (React.createElement(Text, { key: j, color: s.dim ? theme.greyDim : s.color, bold: s.bold, italic: s.italic }, linkify(s.text))))))),
991
+ off > 0 ? React.createElement(Text, { color: theme.greyDim },
992
+ " \u2193 ",
993
+ off,
994
+ " sat\u0131r \u00B7 PageDown / Son") : null,
995
+ streaming && off === 0 ? (React.createElement(Box, { flexDirection: "row" },
996
+ React.createElement(Text, { color: theme.redBright, bold: true }, "\u23FA "),
997
+ React.createElement(Box, { flexDirection: "column" },
998
+ React.createElement(Text, { color: theme.white }, streaming)))) : null));
999
+ })()),
835
1000
  perm ? (React.createElement(Box, { flexDirection: "column", borderStyle: "round", borderColor: theme.red, paddingX: 1, marginTop: 1 },
836
1001
  React.createElement(Text, { color: theme.redBright, bold: true }, t('perm.title', perm.label)),
837
1002
  permMode === 'feedback' ? (React.createElement(React.Fragment, null,
@@ -908,4 +1073,9 @@ function App() {
908
1073
  tipText(tipId))),
909
1074
  React.createElement(StatusLine, { model: config.model, ctxTokens: ctxTokens, trust: trustLevel }))) : null));
910
1075
  }
911
- render(React.createElement(App, null));
1076
+ if (_TUI) {
1077
+ import('./tui.js').then((m) => m.runTui()).catch((e) => { console.error(e); process.exit(1); });
1078
+ }
1079
+ else {
1080
+ render(React.createElement(App, null));
1081
+ }
package/dist/commands.js CHANGED
@@ -18,6 +18,7 @@ import { programText, tier } from './program.js';
18
18
  export const COMMANDS = [
19
19
  { name: '/help', desc: 'komutları ve ipuçlarını göster' },
20
20
  { name: '/clear', desc: 'sohbeti ve geçmişi temizle' },
21
+ { name: '/kopyala', desc: 'son yanıtı panoya kopyala (/kopyala hepsi · tüm sohbet)' },
21
22
  { name: '/compact', desc: 'geçmişi modelle özetleyip bağlamı küçült' },
22
23
  { name: '/context', desc: 'bağlam / token kullanımını göster' },
23
24
  { name: '/cost', desc: 'oturum token tahminini göster' },
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.22';
19
+ export const VERSION = '1.0.24';
package/dist/tui.js ADDED
@@ -0,0 +1,133 @@
1
+ // Kendi renderer (ink YOK) — Milestone 1: saf sohbet.
2
+ // Mimari: geçmiş → console.log (kalıcı scrollback → fare ile kopyalama + doğal kaydırma);
3
+ // canlı footer → log-update (kendi bölgesini yönetir, resize'da kaskad yok); giriş → readline keypress.
4
+ // Renkli kod + markdown: ansi.ts (itemAnsi). WORMCLAUDE_TUI=1 ile çalışır; ink sürümü dokunulmadan kalır.
5
+ import readline from 'node:readline';
6
+ import logUpdate from 'log-update';
7
+ import { loadConfig, streamChat } from './api.js';
8
+ import { itemAnsi, bannerAnsi } from './ansi.js';
9
+ import { theme, VERSION } from './theme.js';
10
+ import { cleanModelText } from './textclean.js';
11
+ const RESET = '\x1b[0m';
12
+ 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` : ''; };
13
+ const paint = (s, c, bold = false) => `${bold ? '\x1b[1m' : ''}${c ? hex(c) : ''}${s}${RESET}`;
14
+ const cols = () => process.stdout.columns || 80;
15
+ export async function runTui() {
16
+ const config = loadConfig();
17
+ const history = []; // model bağlamı {role, content}
18
+ let inputBuf = '';
19
+ let busy = false;
20
+ let streamPreview = ''; // akış sırasında footer'da gösterilen ham metin
21
+ let spin = 0;
22
+ const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
23
+ // ── Geçmişe kalıcı yaz (scrollback) ──
24
+ const printItem = (it) => { logUpdate.clear(); process.stdout.write(itemAnsi(it, cols()) + '\n'); renderFooter(); };
25
+ // ── Canlı footer (log-update yönetir) ──
26
+ function renderFooter() {
27
+ const lines = [];
28
+ if (busy && !streamPreview)
29
+ lines.push(paint(`${SPIN[spin % SPIN.length]} `, theme.red) + paint('yanıt bekleniyor…', theme.grey));
30
+ if (streamPreview) {
31
+ const tail = streamPreview.split('\n').slice(-12); // footer'ı sınırla (uzun akışta taşmasın)
32
+ lines.push(paint('⏺ ', theme.redBright, true) + paint(tail.shift() || '', theme.white));
33
+ for (const l of tail)
34
+ lines.push(' ' + paint(l, theme.white));
35
+ }
36
+ lines.push('');
37
+ lines.push(paint('❯ ', theme.redBright, true) + inputBuf + (busy ? '' : paint('▌', theme.greyDim)));
38
+ lines.push(paint(' /kopyala panoya · Ctrl+C çıkış', theme.greyDim));
39
+ logUpdate(lines.join('\n'));
40
+ }
41
+ // ── Bir sohbet turu (Milestone 1: araç yok, saf metin) ──
42
+ async function runTurn(userText) {
43
+ busy = true;
44
+ streamPreview = '';
45
+ history.push({ role: 'user', content: userText });
46
+ const spinTimer = setInterval(() => { spin++; if (busy && !streamPreview)
47
+ renderFooter(); }, 120);
48
+ let answer = '';
49
+ try {
50
+ for await (const ev of streamChat(history, [], config)) {
51
+ if (ev.type === 'text') {
52
+ answer += ev.text;
53
+ streamPreview = cleanModelText(answer);
54
+ renderFooter();
55
+ }
56
+ else if (ev.type === 'error') {
57
+ answer += `\n[hata: ${ev.error}]`;
58
+ }
59
+ }
60
+ }
61
+ catch (e) {
62
+ answer += `\n[bağlantı hatası: ${e?.message || e}]`;
63
+ }
64
+ clearInterval(spinTimer);
65
+ answer = cleanModelText(answer).trim();
66
+ busy = false;
67
+ streamPreview = '';
68
+ if (answer) {
69
+ history.push({ role: 'assistant', content: answer });
70
+ printItem({ kind: 'assistant', text: answer });
71
+ }
72
+ renderFooter();
73
+ }
74
+ // ── Giriş (readline keypress, raw mode) ──
75
+ readline.emitKeypressEvents(process.stdin);
76
+ if (process.stdin.isTTY)
77
+ process.stdin.setRawMode(true);
78
+ process.stdout.write(bannerAnsi(cols()) + '\n');
79
+ process.stdout.write(paint(' uncensored security + code', theme.greyDim) + paint(' · WormClaude ', theme.greyDim) + paint('v' + VERSION, theme.red, true) + paint(' · özel renderer (deneysel)', theme.greyDim) + '\n\n');
80
+ renderFooter();
81
+ process.stdin.on('keypress', (str, key) => {
82
+ if (key && key.ctrl && key.name === 'c') {
83
+ logUpdate.clear();
84
+ process.stdout.write('\n');
85
+ process.exit(0);
86
+ }
87
+ if (busy)
88
+ return; // tur sırasında giriş kilitli (M1)
89
+ if (key && key.name === 'return') {
90
+ const v = inputBuf.trim();
91
+ inputBuf = '';
92
+ if (!v) {
93
+ renderFooter();
94
+ return;
95
+ }
96
+ if (v === '/cikis' || v === '/exit' || v === '/quit') {
97
+ logUpdate.clear();
98
+ process.exit(0);
99
+ }
100
+ // /kopyala — son yanıtı OSC52 ile panoya
101
+ if (v === '/kopyala' || v === '/copy') {
102
+ const last = [...history].reverse().find((m) => m.role === 'assistant');
103
+ if (last) {
104
+ process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
105
+ printItem({ kind: 'note', text: `✓ Panoya kopyalandı (${last.content.length} karakter).` });
106
+ }
107
+ else
108
+ printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
109
+ return;
110
+ }
111
+ printItem({ kind: 'user', text: v });
112
+ runTurn(v);
113
+ return;
114
+ }
115
+ if (key && (key.name === 'backspace')) {
116
+ inputBuf = inputBuf.slice(0, -1);
117
+ renderFooter();
118
+ return;
119
+ }
120
+ if (key && key.name === 'escape') {
121
+ inputBuf = '';
122
+ renderFooter();
123
+ return;
124
+ }
125
+ // yazdırılabilir karakter / yapıştırma
126
+ if (str && !key?.ctrl && !key?.meta && str !== '\r' && str !== '\n') {
127
+ inputBuf += str;
128
+ renderFooter();
129
+ }
130
+ });
131
+ // resize → footer'ı yeniden çiz (geçmiş scrollback'te terminalce sarılır)
132
+ process.stdout.on('resize', () => renderFooter());
133
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {
@@ -19,6 +19,7 @@
19
19
  "ink": "^5.0.1",
20
20
  "ink-spinner": "^5.0.0",
21
21
  "ink-text-input": "^6.0.0",
22
+ "log-update": "^5.0.1",
22
23
  "react": "^18.3.1",
23
24
  "string-width": "^7.2.0"
24
25
  },