wormclaude 1.0.38 → 1.0.39
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/theme.js +1 -1
- package/dist/tui.js +107 -26
- package/package.json +1 -1
package/dist/theme.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import readline from 'node:readline';
|
|
7
7
|
import stringWidth from 'string-width';
|
|
8
8
|
import { loadConfig, streamChat, fetchAccount } from './api.js';
|
|
9
|
+
import { allToolSchemas, executeToolCalls, toolLabel, setToolConfig } from './tools.js';
|
|
10
|
+
import { sanitizeOutput } from './errorsan.js';
|
|
9
11
|
import { itemAnsi } from './ansi.js';
|
|
10
12
|
import { theme, VERSION } from './theme.js';
|
|
11
13
|
import { cleanModelText } from './textclean.js';
|
|
@@ -43,7 +45,11 @@ export async function runTui() {
|
|
|
43
45
|
const displayItems = [{ kind: 'banner' }];
|
|
44
46
|
let inputBuf = '', busy = false, streamChars = 0, spin = 0;
|
|
45
47
|
const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
|
|
46
|
-
const FOOTER_H = 4; // kutu (üst çizgi +
|
|
48
|
+
const FOOTER_H = 4; // kutu (üst çizgi + giriş/dialog + alt çizgi) + ipucu satırı
|
|
49
|
+
setToolConfig(config); // araçlar (Write/Bash/Edit…) aynı config'i kullansın
|
|
50
|
+
// İzin (araç onayı) durumu
|
|
51
|
+
let perm = null;
|
|
52
|
+
const allowAll = new Set(); // "Tümüne izin" denen araçlar (oturum boyu)
|
|
47
53
|
// ── Claude tarzı başlık (model/plan/mail/cwd) — responsive ──
|
|
48
54
|
function headerLines(W) {
|
|
49
55
|
const a = paint(' WormClaude ', theme.greyDim) + paint('v' + VERSION, theme.red, true)
|
|
@@ -61,18 +67,28 @@ export async function runTui() {
|
|
|
61
67
|
function drawFooter() {
|
|
62
68
|
const W = Math.max(8, cols());
|
|
63
69
|
const start = rows() - FOOTER_H + 1;
|
|
64
|
-
let shown = inputBuf;
|
|
65
|
-
const avail = W - 3;
|
|
66
|
-
if (vis(shown) > avail)
|
|
67
|
-
shown = '…' + shown.slice(-(avail - 1));
|
|
68
|
-
// İki uzun KIRMIZI çizgi arasında giriş (köşesiz → resize'da bozulmaz). Altında ipucu.
|
|
69
70
|
const line = paint('─'.repeat(W), theme.red);
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
let body;
|
|
72
|
+
if (perm) {
|
|
73
|
+
// İzin dialogu: araç adı + Evet/Tümü/Hayır
|
|
74
|
+
const q = paint(' ⚠ ', theme.redBright, true) + paint(perm.label, theme.white) + paint(' çalıştırılsın mı?', theme.grey);
|
|
75
|
+
const opts = paint(' [E] Evet', theme.redBright, true) + paint(' · ', theme.greyDim)
|
|
76
|
+
+ paint('[T] Tümüne izin', theme.grey) + paint(' · ', theme.greyDim) + paint('[H] Hayır', theme.grey);
|
|
77
|
+
body = [line, q, opts, line];
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
let shown = inputBuf;
|
|
81
|
+
const avail = W - 3;
|
|
82
|
+
if (vis(shown) > avail)
|
|
83
|
+
shown = '…' + shown.slice(-(avail - 1));
|
|
84
|
+
const inputLine = paint('❯ ', theme.redBright, true) + paint(shown, theme.white) + paint('▌', theme.greyDim);
|
|
85
|
+
const hint = busy
|
|
86
|
+
? paint(`${SPIN[spin % SPIN.length]} çalışıyor… ${streamChars ? streamChars + ' karakter' : ''}`, theme.grey)
|
|
87
|
+
: paint(' /kopyala panoya · /help komutlar · Ctrl+C çıkış', theme.greyDim);
|
|
88
|
+
body = [line, inputLine, line, hint];
|
|
89
|
+
}
|
|
74
90
|
let out = '\x1b7'; // imleci kaydet
|
|
75
|
-
|
|
91
|
+
body.forEach((l, i) => { out += `\x1b[${start + i};1H\x1b[2K` + fit(l, W); });
|
|
76
92
|
out += '\x1b8'; // imleci geri yükle (içerik alanı)
|
|
77
93
|
process.stdout.write(out);
|
|
78
94
|
}
|
|
@@ -90,34 +106,71 @@ export async function runTui() {
|
|
|
90
106
|
}
|
|
91
107
|
// İçerik öğesini ekle + içerik alanına bas (taşarsa scrollback'e → kopyalanır). Footer sabit kalır.
|
|
92
108
|
const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); drawFooter(); };
|
|
93
|
-
// ──
|
|
109
|
+
// ── Agent döngüsü (M2: araçlar + izin). Model tool çağırırsa izin sorulup çalıştırılır,
|
|
110
|
+
// sonuç geçmişe eklenip döngü devam eder; tool yoksa biter. ──
|
|
94
111
|
async function runTurn(userText) {
|
|
95
112
|
busy = true;
|
|
96
113
|
streamChars = 0;
|
|
97
114
|
history.push({ role: 'user', content: userText });
|
|
98
|
-
const timer = setInterval(() => { spin++; if (busy)
|
|
115
|
+
const timer = setInterval(() => { spin++; if (busy && !perm)
|
|
99
116
|
drawFooter(); }, 120);
|
|
100
|
-
let
|
|
117
|
+
let lastSig = '', sameCount = 0;
|
|
101
118
|
try {
|
|
102
|
-
for
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
119
|
+
for (let iter = 0; iter < 25; iter++) {
|
|
120
|
+
streamChars = 0;
|
|
121
|
+
let answer = '';
|
|
122
|
+
let toolCalls = [];
|
|
123
|
+
for await (const ev of streamChat(history, allToolSchemas(), config)) {
|
|
124
|
+
if (ev.type === 'text') {
|
|
125
|
+
answer += ev.text;
|
|
126
|
+
streamChars = answer.length;
|
|
127
|
+
drawFooter();
|
|
128
|
+
}
|
|
129
|
+
else if (ev.type === 'done')
|
|
130
|
+
toolCalls = ev.toolCalls || [];
|
|
131
|
+
else if (ev.type === 'error')
|
|
132
|
+
answer += `\n[hata: ${ev.error}]`;
|
|
133
|
+
}
|
|
134
|
+
answer = cleanModelText(answer).trim();
|
|
135
|
+
const am = { role: 'assistant', content: answer || '' };
|
|
136
|
+
if (toolCalls.length)
|
|
137
|
+
am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
|
|
138
|
+
history.push(am);
|
|
139
|
+
if (answer)
|
|
140
|
+
printItem({ kind: 'assistant', text: answer });
|
|
141
|
+
if (!toolCalls.length)
|
|
142
|
+
break;
|
|
143
|
+
// takılma koruması: aynı araç-çağrısı 3 kez üst üste → dur
|
|
144
|
+
const sig = JSON.stringify(toolCalls.map((tc) => tc.name + ':' + (tc.args || '')));
|
|
145
|
+
if (sig === lastSig)
|
|
146
|
+
sameCount++;
|
|
147
|
+
else {
|
|
148
|
+
sameCount = 0;
|
|
149
|
+
lastSig = sig;
|
|
150
|
+
}
|
|
151
|
+
if (sameCount >= 2) {
|
|
152
|
+
printItem({ kind: 'note', text: 'Aynı adım tekrarlandı, döngü önlemek için durduruldu.' });
|
|
153
|
+
break;
|
|
106
154
|
}
|
|
107
|
-
|
|
108
|
-
|
|
155
|
+
const results = await executeToolCalls(toolCalls, {
|
|
156
|
+
confirm: (c, args) => new Promise((resolve) => {
|
|
157
|
+
if (allowAll.has(c.name))
|
|
158
|
+
return resolve('allow');
|
|
159
|
+
perm = { label: toolLabel(c.name, args), name: c.name, resolve };
|
|
160
|
+
drawFooter();
|
|
161
|
+
}),
|
|
162
|
+
onResult: (c, _i, res) => printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }),
|
|
163
|
+
});
|
|
164
|
+
for (let i = 0; i < toolCalls.length; i++)
|
|
165
|
+
history.push({ role: 'tool', tool_call_id: toolCalls[i].id, content: (results[i].output || '').slice(0, 8000) });
|
|
109
166
|
}
|
|
110
167
|
}
|
|
111
168
|
catch (e) {
|
|
112
|
-
|
|
169
|
+
printItem({ kind: 'note', text: `[bağlantı hatası: ${e?.message || e}]` });
|
|
113
170
|
}
|
|
114
171
|
clearInterval(timer);
|
|
115
|
-
answer = cleanModelText(answer).trim();
|
|
116
172
|
busy = false;
|
|
117
|
-
|
|
118
|
-
history.push({ role: 'assistant', content: answer });
|
|
119
|
-
printItem({ kind: 'assistant', text: answer });
|
|
120
|
-
}
|
|
173
|
+
perm = null;
|
|
121
174
|
drawFooter();
|
|
122
175
|
}
|
|
123
176
|
// ── Kurulum ──
|
|
@@ -141,6 +194,34 @@ export async function runTui() {
|
|
|
141
194
|
catch { } });
|
|
142
195
|
let ctrlcAt = 0;
|
|
143
196
|
process.stdin.on('keypress', (str, key) => {
|
|
197
|
+
// İzin dialogu aktifse: E=Evet, T=Tümüne izin, H/Esc=Hayır (Ctrl+C de Hayır)
|
|
198
|
+
if (perm) {
|
|
199
|
+
const k = (str || '').toLowerCase();
|
|
200
|
+
const r = perm.resolve, nm = perm.name;
|
|
201
|
+
if (key && key.ctrl && key.name === 'c') {
|
|
202
|
+
perm = null;
|
|
203
|
+
r({ deny: 'kullanıcı iptal etti' });
|
|
204
|
+
drawFooter();
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (k === 'e' || k === '1' || (key && key.name === 'return')) {
|
|
208
|
+
perm = null;
|
|
209
|
+
r('allow');
|
|
210
|
+
drawFooter();
|
|
211
|
+
}
|
|
212
|
+
else if (k === 't' || k === '2') {
|
|
213
|
+
allowAll.add(nm);
|
|
214
|
+
perm = null;
|
|
215
|
+
r('allow');
|
|
216
|
+
drawFooter();
|
|
217
|
+
}
|
|
218
|
+
else if (k === 'h' || k === '3' || (key && key.name === 'escape')) {
|
|
219
|
+
perm = null;
|
|
220
|
+
r({ deny: '' });
|
|
221
|
+
drawFooter();
|
|
222
|
+
}
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
144
225
|
if (key && key.ctrl && key.name === 'c') {
|
|
145
226
|
if (inputBuf) {
|
|
146
227
|
inputBuf = '';
|