wormclaude 1.0.23 → 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
@@ -90,15 +90,18 @@ const _initHistory = () => {
90
90
  // silemeyip KASKAD yapıyor (scrollback reflow). Alt-screen tüm ekranı yönetir → resize'da
91
91
  // temiz yeniden çizim, kaskad YOK. Kopyalama: alt-screen'de fare seçimi sınırlı olduğundan
92
92
  // /kopyala komutu (OSC52 ile panoya) sağlanır. ?1049h alt-screen · ?1007h alternate-scroll.
93
- try {
94
- process.stdout.write('\x1b[?1049h\x1b[?1007h\x1b[2J\x1b[H');
95
- }
96
- catch { }
97
- const _leaveAlt = () => { try {
98
- process.stdout.write('\x1b[?1007l\x1b[?1049l');
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);
99
104
  }
100
- catch { } };
101
- process.on('exit', _leaveAlt);
102
105
  function useDimensions() {
103
106
  const [d, setD] = useState({
104
107
  cols: process.stdout.columns || 80,
@@ -1070,4 +1073,9 @@ function App() {
1070
1073
  tipText(tipId))),
1071
1074
  React.createElement(StatusLine, { model: config.model, ctxTokens: ctxTokens, trust: trustLevel }))) : null));
1072
1075
  }
1073
- 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/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.23';
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.23",
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
  },