wormclaude 1.0.37 → 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 +108 -25
- 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 =
|
|
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,16 +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
|
-
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
const line = paint('─'.repeat(W), theme.red);
|
|
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
|
+
}
|
|
72
90
|
let out = '\x1b7'; // imleci kaydet
|
|
73
|
-
|
|
91
|
+
body.forEach((l, i) => { out += `\x1b[${start + i};1H\x1b[2K` + fit(l, W); });
|
|
74
92
|
out += '\x1b8'; // imleci geri yükle (içerik alanı)
|
|
75
93
|
process.stdout.write(out);
|
|
76
94
|
}
|
|
@@ -88,34 +106,71 @@ export async function runTui() {
|
|
|
88
106
|
}
|
|
89
107
|
// İçerik öğesini ekle + içerik alanına bas (taşarsa scrollback'e → kopyalanır). Footer sabit kalır.
|
|
90
108
|
const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); drawFooter(); };
|
|
91
|
-
// ──
|
|
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. ──
|
|
92
111
|
async function runTurn(userText) {
|
|
93
112
|
busy = true;
|
|
94
113
|
streamChars = 0;
|
|
95
114
|
history.push({ role: 'user', content: userText });
|
|
96
|
-
const timer = setInterval(() => { spin++; if (busy)
|
|
115
|
+
const timer = setInterval(() => { spin++; if (busy && !perm)
|
|
97
116
|
drawFooter(); }, 120);
|
|
98
|
-
let
|
|
117
|
+
let lastSig = '', sameCount = 0;
|
|
99
118
|
try {
|
|
100
|
-
for
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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;
|
|
104
154
|
}
|
|
105
|
-
|
|
106
|
-
|
|
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) });
|
|
107
166
|
}
|
|
108
167
|
}
|
|
109
168
|
catch (e) {
|
|
110
|
-
|
|
169
|
+
printItem({ kind: 'note', text: `[bağlantı hatası: ${e?.message || e}]` });
|
|
111
170
|
}
|
|
112
171
|
clearInterval(timer);
|
|
113
|
-
answer = cleanModelText(answer).trim();
|
|
114
172
|
busy = false;
|
|
115
|
-
|
|
116
|
-
history.push({ role: 'assistant', content: answer });
|
|
117
|
-
printItem({ kind: 'assistant', text: answer });
|
|
118
|
-
}
|
|
173
|
+
perm = null;
|
|
119
174
|
drawFooter();
|
|
120
175
|
}
|
|
121
176
|
// ── Kurulum ──
|
|
@@ -139,6 +194,34 @@ export async function runTui() {
|
|
|
139
194
|
catch { } });
|
|
140
195
|
let ctrlcAt = 0;
|
|
141
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
|
+
}
|
|
142
225
|
if (key && key.ctrl && key.name === 'c') {
|
|
143
226
|
if (inputBuf) {
|
|
144
227
|
inputBuf = '';
|