wormclaude 1.0.23 → 1.0.25
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 +17 -9
- package/dist/theme.js +1 -1
- package/dist/tui.js +168 -0
- package/package.json +2 -1
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
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
|
-
|
|
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
package/dist/tui.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
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 stringWidth from 'string-width';
|
|
8
|
+
import { loadConfig, streamChat } from './api.js';
|
|
9
|
+
import { itemAnsi, bannerAnsi } from './ansi.js';
|
|
10
|
+
import { theme, VERSION } from './theme.js';
|
|
11
|
+
import { cleanModelText } from './textclean.js';
|
|
12
|
+
const RESET = '\x1b[0m';
|
|
13
|
+
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` : ''; };
|
|
14
|
+
const paint = (s, c, bold = false) => `${bold ? '\x1b[1m' : ''}${c ? hex(c) : ''}${s}${RESET}`;
|
|
15
|
+
const cols = () => process.stdout.columns || 80;
|
|
16
|
+
// Footer satırını terminal genişliğine ANSI-korumalı kırp (otomatik sarmayı önler → log-update
|
|
17
|
+
// satır sayımı şaşmaz, küçük terminalde bozulmaz). ANSI escape'leri genişliğe saymaz, korur.
|
|
18
|
+
function fit(line, max) {
|
|
19
|
+
let out = '', w = 0, i = 0;
|
|
20
|
+
while (i < line.length) {
|
|
21
|
+
if (line[i] === '\x1b') {
|
|
22
|
+
const m = /^\x1b\[[0-9;]*m/.exec(line.slice(i));
|
|
23
|
+
if (m) {
|
|
24
|
+
out += m[0];
|
|
25
|
+
i += m[0].length;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const ch = line[i];
|
|
30
|
+
const cw = stringWidth(ch) || 1;
|
|
31
|
+
if (w + cw > max)
|
|
32
|
+
break;
|
|
33
|
+
out += ch;
|
|
34
|
+
w += cw;
|
|
35
|
+
i++;
|
|
36
|
+
}
|
|
37
|
+
return out + RESET;
|
|
38
|
+
}
|
|
39
|
+
// Görünür (ANSI'siz) genişlik
|
|
40
|
+
const vis = (s) => stringWidth(s.replace(/\x1b\[[0-9;]*m/g, ''));
|
|
41
|
+
export async function runTui() {
|
|
42
|
+
const config = loadConfig();
|
|
43
|
+
const history = []; // model bağlamı {role, content}
|
|
44
|
+
let inputBuf = '';
|
|
45
|
+
let busy = false;
|
|
46
|
+
let streamPreview = ''; // akış sırasında footer'da gösterilen ham metin
|
|
47
|
+
let spin = 0;
|
|
48
|
+
const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
|
|
49
|
+
// ── Geçmişe kalıcı yaz (scrollback) ──
|
|
50
|
+
const printItem = (it) => { logUpdate.clear(); process.stdout.write(itemAnsi(it, cols()) + '\n'); renderFooter(); };
|
|
51
|
+
// ── Canlı footer (log-update yönetir) ──
|
|
52
|
+
function renderFooter() {
|
|
53
|
+
const W = Math.max(8, cols());
|
|
54
|
+
const lines = [];
|
|
55
|
+
if (busy && !streamPreview)
|
|
56
|
+
lines.push(paint(`${SPIN[spin % SPIN.length]} `, theme.red) + paint('yanıt bekleniyor…', theme.grey));
|
|
57
|
+
if (streamPreview) {
|
|
58
|
+
const maxTail = Math.max(2, Math.min(12, (process.stdout.rows || 24) - 4)); // ekran boyunu aşma
|
|
59
|
+
const tail = streamPreview.split('\n').slice(-maxTail);
|
|
60
|
+
lines.push(paint('⏺ ', theme.redBright, true) + paint(tail.shift() || '', theme.white));
|
|
61
|
+
for (const l of tail)
|
|
62
|
+
lines.push(' ' + paint(l, theme.white));
|
|
63
|
+
}
|
|
64
|
+
lines.push('');
|
|
65
|
+
// Giriş satırı: uzunsa SONU göster (yazdıkça imleç görünür kalsın)
|
|
66
|
+
const prompt = paint('❯ ', theme.redBright, true);
|
|
67
|
+
let shown = inputBuf;
|
|
68
|
+
const avail = W - 3; // "❯ " + imleç payı
|
|
69
|
+
if (vis(shown) > avail)
|
|
70
|
+
shown = '…' + shown.slice(-(avail - 1));
|
|
71
|
+
lines.push(prompt + shown + (busy ? '' : paint('▌', theme.greyDim)));
|
|
72
|
+
lines.push(paint('/kopyala panoya · Ctrl+C çıkış', theme.greyDim));
|
|
73
|
+
// HER satırı genişliğe kırp → küçük terminalde sarma/bozulma olmaz
|
|
74
|
+
logUpdate(lines.map((l) => fit(l, W)).join('\n'));
|
|
75
|
+
}
|
|
76
|
+
// ── Bir sohbet turu (Milestone 1: araç yok, saf metin) ──
|
|
77
|
+
async function runTurn(userText) {
|
|
78
|
+
busy = true;
|
|
79
|
+
streamPreview = '';
|
|
80
|
+
history.push({ role: 'user', content: userText });
|
|
81
|
+
const spinTimer = setInterval(() => { spin++; if (busy && !streamPreview)
|
|
82
|
+
renderFooter(); }, 120);
|
|
83
|
+
let answer = '';
|
|
84
|
+
try {
|
|
85
|
+
for await (const ev of streamChat(history, [], config)) {
|
|
86
|
+
if (ev.type === 'text') {
|
|
87
|
+
answer += ev.text;
|
|
88
|
+
streamPreview = cleanModelText(answer);
|
|
89
|
+
renderFooter();
|
|
90
|
+
}
|
|
91
|
+
else if (ev.type === 'error') {
|
|
92
|
+
answer += `\n[hata: ${ev.error}]`;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (e) {
|
|
97
|
+
answer += `\n[bağlantı hatası: ${e?.message || e}]`;
|
|
98
|
+
}
|
|
99
|
+
clearInterval(spinTimer);
|
|
100
|
+
answer = cleanModelText(answer).trim();
|
|
101
|
+
busy = false;
|
|
102
|
+
streamPreview = '';
|
|
103
|
+
if (answer) {
|
|
104
|
+
history.push({ role: 'assistant', content: answer });
|
|
105
|
+
printItem({ kind: 'assistant', text: answer });
|
|
106
|
+
}
|
|
107
|
+
renderFooter();
|
|
108
|
+
}
|
|
109
|
+
// ── Giriş (readline keypress, raw mode) ──
|
|
110
|
+
readline.emitKeypressEvents(process.stdin);
|
|
111
|
+
if (process.stdin.isTTY)
|
|
112
|
+
process.stdin.setRawMode(true);
|
|
113
|
+
process.stdout.write(bannerAnsi(cols()) + '\n');
|
|
114
|
+
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');
|
|
115
|
+
renderFooter();
|
|
116
|
+
process.stdin.on('keypress', (str, key) => {
|
|
117
|
+
if (key && key.ctrl && key.name === 'c') {
|
|
118
|
+
logUpdate.clear();
|
|
119
|
+
process.stdout.write('\n');
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
if (busy)
|
|
123
|
+
return; // tur sırasında giriş kilitli (M1)
|
|
124
|
+
if (key && key.name === 'return') {
|
|
125
|
+
const v = inputBuf.trim();
|
|
126
|
+
inputBuf = '';
|
|
127
|
+
if (!v) {
|
|
128
|
+
renderFooter();
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (v === '/cikis' || v === '/exit' || v === '/quit') {
|
|
132
|
+
logUpdate.clear();
|
|
133
|
+
process.exit(0);
|
|
134
|
+
}
|
|
135
|
+
// /kopyala — son yanıtı OSC52 ile panoya
|
|
136
|
+
if (v === '/kopyala' || v === '/copy') {
|
|
137
|
+
const last = [...history].reverse().find((m) => m.role === 'assistant');
|
|
138
|
+
if (last) {
|
|
139
|
+
process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
|
|
140
|
+
printItem({ kind: 'note', text: `✓ Panoya kopyalandı (${last.content.length} karakter).` });
|
|
141
|
+
}
|
|
142
|
+
else
|
|
143
|
+
printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
printItem({ kind: 'user', text: v });
|
|
147
|
+
runTurn(v);
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (key && (key.name === 'backspace')) {
|
|
151
|
+
inputBuf = inputBuf.slice(0, -1);
|
|
152
|
+
renderFooter();
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
if (key && key.name === 'escape') {
|
|
156
|
+
inputBuf = '';
|
|
157
|
+
renderFooter();
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
// yazdırılabilir karakter / yapıştırma
|
|
161
|
+
if (str && !key?.ctrl && !key?.meta && str !== '\r' && str !== '\n') {
|
|
162
|
+
inputBuf += str;
|
|
163
|
+
renderFooter();
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
// resize → footer'ı yeniden çiz (geçmiş scrollback'te terminalce sarılır)
|
|
167
|
+
process.stdout.on('resize', () => renderFooter());
|
|
168
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wormclaude",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.25",
|
|
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
|
},
|