wormclaude 1.0.47 → 1.0.49
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 +117 -29
- package/package.json +1 -1
package/dist/theme.js
CHANGED
package/dist/tui.js
CHANGED
|
@@ -6,13 +6,20 @@
|
|
|
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';
|
|
9
|
+
import { allToolSchemas, executeToolCalls, executeTool, toolLabel, setToolConfig } from './tools.js';
|
|
10
10
|
import { sanitizeOutput } from './errorsan.js';
|
|
11
11
|
import { itemAnsi } from './ansi.js';
|
|
12
12
|
import { theme, VERSION } from './theme.js';
|
|
13
13
|
import { cleanModelText } from './textclean.js';
|
|
14
14
|
import { COMMANDS, runSlashCommand } from './commands.js';
|
|
15
15
|
import { cmdDesc } from './i18n.js';
|
|
16
|
+
import { getSkill, getSkills, buildSkillPrompt } from './skills.js';
|
|
17
|
+
import { getExtCommand, getExtCommands, buildExtCommandPrompt } from './extensions.js';
|
|
18
|
+
import { resolveAtMentions } from './atmention.js';
|
|
19
|
+
import { loadMemoryContext, shouldExtract, triggerMemory } from './memory.js';
|
|
20
|
+
import { shouldAutoCompact, runCompact } from './compact.js';
|
|
21
|
+
import { recordLearned } from './learn.js';
|
|
22
|
+
import { connectMcpServers } from './mcp.js';
|
|
16
23
|
const RESET = '\x1b[0m';
|
|
17
24
|
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` : ''; };
|
|
18
25
|
const paint = (s, c, bold = false) => `${bold ? '\x1b[1m' : ''}${c ? hex(c) : ''}${s}${RESET}`;
|
|
@@ -49,21 +56,41 @@ export async function runTui() {
|
|
|
49
56
|
const _winNote = process.platform === 'win32'
|
|
50
57
|
? ' This is WINDOWS. Write files to the CURRENT directory (relative paths) or under C:\\Users\\... — NEVER use /home/user or other Unix paths. The Bash tool runs via cmd.exe (use Windows commands).'
|
|
51
58
|
: ' Use POSIX paths.';
|
|
59
|
+
const _memCtx = loadMemoryContext(); // WORMCLAUDE.md + .wormclaude/memory.md
|
|
60
|
+
const _sysCount = _memCtx ? 2 : 1; // başlangıç sistem mesajı sayısı (clear bunları korur)
|
|
52
61
|
const history = [
|
|
53
62
|
{ role: 'system', content: `ENVIRONMENT: The user machine runs ${_plat}. Current working directory: ${process.cwd()}.${_winNote}` },
|
|
63
|
+
...(_memCtx ? [{ role: 'system', content: _memCtx }] : []),
|
|
54
64
|
];
|
|
55
65
|
const displayItems = [{ kind: 'banner' }];
|
|
56
66
|
let inputBuf = '', busy = false, streamChars = 0, spin = 0;
|
|
57
67
|
const SPIN = ['·', '✢', '✳', '✶', '✻', '✽', '✶', '✳', '✢'];
|
|
58
|
-
//
|
|
68
|
+
// Giriş kutusunu genişliğe göre ÇOK SATIRA sar (uzun mesaj/kod sağa taşmaz, alta iner).
|
|
69
|
+
const MAX_INPUT_LINES = 10;
|
|
70
|
+
const inputBoxLines = (W) => {
|
|
71
|
+
const inner = Math.max(4, W - 2); // "✶ " önek payı
|
|
72
|
+
const wrapped = [];
|
|
73
|
+
let cur = '';
|
|
74
|
+
for (const ch of inputBuf) {
|
|
75
|
+
if (vis(cur + ch) > inner) {
|
|
76
|
+
wrapped.push(cur);
|
|
77
|
+
cur = ch;
|
|
78
|
+
}
|
|
79
|
+
else
|
|
80
|
+
cur += ch;
|
|
81
|
+
}
|
|
82
|
+
wrapped.push(cur);
|
|
83
|
+
const shown = wrapped.length > MAX_INPUT_LINES ? wrapped.slice(-MAX_INPUT_LINES) : wrapped; // çok uzunsa sonu göster
|
|
84
|
+
return shown.map((ln, i) => (i === 0 ? paint('✶ ', theme.redBright, true) : ' ') + paint(ln, theme.white) + (i === shown.length - 1 ? paint('▌', theme.greyDim) : ''));
|
|
85
|
+
};
|
|
86
|
+
// Footer yüksekliği DİNAMİK: izin=4; değilse (durum/menü) + çizgi + giriş-satırları + çizgi.
|
|
59
87
|
let prevFH = 4;
|
|
60
88
|
const footerHeight = () => {
|
|
61
89
|
if (perm)
|
|
62
90
|
return 4;
|
|
63
91
|
const m = cmdMatches();
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return 4;
|
|
92
|
+
const statusLines = m.length ? Math.min(8, m.length) : 1;
|
|
93
|
+
return statusLines + 2 + inputBoxLines(Math.max(8, cols())).length;
|
|
67
94
|
};
|
|
68
95
|
// Yükseklik değiştiyse scroll-region'ı + içeriği yeniden bas; değişmediyse sadece footer'ı çiz.
|
|
69
96
|
const refresh = () => { const fh = footerHeight(); if (fh !== prevFH)
|
|
@@ -77,12 +104,18 @@ export async function runTui() {
|
|
|
77
104
|
const inputHistory = []; // gönderilen mesajlar (↑/↓ ile geri çağrılır)
|
|
78
105
|
let histIdx = -1; // -1 = geçmişte gezinmiyor
|
|
79
106
|
let cmdSel = 0; // slash menüde seçili komut
|
|
107
|
+
// Tüm komutlar: yerleşik + skill + extension
|
|
108
|
+
const allCmds = () => [
|
|
109
|
+
...COMMANDS,
|
|
110
|
+
...getSkills().map((s) => ({ name: '/' + s.name, desc: s.description })),
|
|
111
|
+
...getExtCommands().map((c) => ({ name: '/' + c.name, desc: c.description })),
|
|
112
|
+
];
|
|
80
113
|
// Slash menü: "/" ile başlayan girişe uyan komutlar
|
|
81
114
|
const cmdMatches = () => {
|
|
82
115
|
if (busy || !inputBuf.startsWith('/'))
|
|
83
116
|
return [];
|
|
84
117
|
const tok = inputBuf.split(' ')[0];
|
|
85
|
-
return
|
|
118
|
+
return allCmds().filter((c) => c.name.startsWith(tok));
|
|
86
119
|
};
|
|
87
120
|
// Slash komutları için bağlam (cli.tsx ile aynı arayüz) — /clear,/config,/model,/memory,/program…
|
|
88
121
|
const cmdCtx = {
|
|
@@ -91,7 +124,7 @@ export async function runTui() {
|
|
|
91
124
|
setHistory: (h) => { history.length = 0; history.push(...h); },
|
|
92
125
|
note: (text) => printItem({ kind: 'note', text }),
|
|
93
126
|
assistant: (text) => printItem({ kind: 'assistant', text }),
|
|
94
|
-
clearConv: () => { history.length =
|
|
127
|
+
clearConv: () => { history.length = _sysCount; displayItems.length = 1; redrawAll(); }, // sistem mesajları + banner kalır
|
|
95
128
|
exit: () => quit(),
|
|
96
129
|
};
|
|
97
130
|
async function runCommand(v) {
|
|
@@ -150,11 +183,7 @@ export async function runTui() {
|
|
|
150
183
|
body = [line, q, opts, line];
|
|
151
184
|
}
|
|
152
185
|
else {
|
|
153
|
-
|
|
154
|
-
const avail = W - 3;
|
|
155
|
-
if (vis(shown) > avail)
|
|
156
|
-
shown = '…' + shown.slice(-(avail - 1));
|
|
157
|
-
const inputLine = paint('✶ ', theme.redBright, true) + paint(shown, theme.white) + paint('▌', theme.greyDim);
|
|
186
|
+
const inputLines = inputBoxLines(W); // çok-satırlı giriş (sarılmış)
|
|
158
187
|
const m = cmdMatches();
|
|
159
188
|
if (m.length) {
|
|
160
189
|
// DİKEY slash menü (alt alta), seçili kırmızı — kutunun üstünde.
|
|
@@ -164,13 +193,13 @@ export async function runTui() {
|
|
|
164
193
|
const on = i === sel;
|
|
165
194
|
return ' ' + paint((on ? '✶ ' : ' ') + c.name.padEnd(13), on ? theme.redBright : theme.grey) + paint(cmdDesc(c.name) || c.desc || '', theme.greyDim);
|
|
166
195
|
});
|
|
167
|
-
body = [...menu, line,
|
|
196
|
+
body = [...menu, line, ...inputLines, line];
|
|
168
197
|
}
|
|
169
198
|
else {
|
|
170
199
|
const status = busy
|
|
171
200
|
? paint(` ${SPIN[spin % SPIN.length]} çalışıyor…${streamChars ? ' ' + streamChars + ' karakter' : ''}`, theme.grey)
|
|
172
201
|
: paint(' / komutlar · ↑↓ geçmiş · /kopyala · Ctrl+C çıkış', theme.greyDim);
|
|
173
|
-
body = [status, line,
|
|
202
|
+
body = [status, line, ...inputLines, line];
|
|
174
203
|
}
|
|
175
204
|
}
|
|
176
205
|
let out = '\x1b7'; // imleci kaydet
|
|
@@ -195,16 +224,28 @@ export async function runTui() {
|
|
|
195
224
|
const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); refresh(); };
|
|
196
225
|
// ── Agent döngüsü (M2: araçlar + izin). Model tool çağırırsa izin sorulup çalıştırılır,
|
|
197
226
|
// sonuç geçmişe eklenip döngü devam eder; tool yoksa biter. ──
|
|
198
|
-
async function runTurn(userText) {
|
|
227
|
+
async function runTurn(userText, displayText) {
|
|
199
228
|
busy = true;
|
|
200
229
|
streamChars = 0;
|
|
230
|
+
if (displayText !== undefined)
|
|
231
|
+
printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
|
|
201
232
|
history.push({ role: 'user', content: userText });
|
|
202
233
|
const timer = setInterval(() => { spin++; if (busy && !perm)
|
|
203
234
|
refresh(); }, 120);
|
|
204
|
-
let lastSig = '', sameCount = 0;
|
|
235
|
+
let lastSig = '', sameCount = 0, usedWeb = false, lastAnswer = '';
|
|
205
236
|
try {
|
|
206
237
|
for (let iter = 0; iter < 25; iter++) {
|
|
207
238
|
streamChars = 0;
|
|
239
|
+
// Oto-compact: bağlam eşiği aşılınca otomatik özetle (uzun sohbet patlamasın)
|
|
240
|
+
if (shouldAutoCompact(history)) {
|
|
241
|
+
try {
|
|
242
|
+
const { history: nh } = await runCompact(history, config);
|
|
243
|
+
history.length = 0;
|
|
244
|
+
history.push(...nh);
|
|
245
|
+
printItem({ kind: 'note', text: '✎ bağlam otomatik özetlendi' });
|
|
246
|
+
}
|
|
247
|
+
catch { }
|
|
248
|
+
}
|
|
208
249
|
let answer = '';
|
|
209
250
|
let toolCalls = [];
|
|
210
251
|
for await (const ev of streamChat(history, allToolSchemas(), config)) {
|
|
@@ -224,8 +265,10 @@ export async function runTui() {
|
|
|
224
265
|
if (toolCalls.length)
|
|
225
266
|
am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
|
|
226
267
|
history.push(am);
|
|
227
|
-
if (answer)
|
|
268
|
+
if (answer) {
|
|
269
|
+
lastAnswer = answer;
|
|
228
270
|
printItem({ kind: 'assistant', text: answer });
|
|
271
|
+
}
|
|
229
272
|
if (!toolCalls.length)
|
|
230
273
|
break;
|
|
231
274
|
// takılma koruması: aynı araç-çağrısı 3 kez üst üste → dur
|
|
@@ -247,7 +290,8 @@ export async function runTui() {
|
|
|
247
290
|
perm = { label: toolLabel(c.name, args), name: c.name, resolve };
|
|
248
291
|
refresh();
|
|
249
292
|
}),
|
|
250
|
-
onResult: (c, _i, res) =>
|
|
293
|
+
onResult: (c, _i, res) => { if ((c.name === 'WebSearch' || c.name === 'WebFetch') && res.ok)
|
|
294
|
+
usedWeb = true; printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }); },
|
|
251
295
|
});
|
|
252
296
|
for (let i = 0; i < toolCalls.length; i++)
|
|
253
297
|
history.push({ role: 'tool', tool_call_id: toolCalls[i].id, content: (results[i].output || '').slice(0, 8000) });
|
|
@@ -260,6 +304,15 @@ export async function runTui() {
|
|
|
260
304
|
busy = false;
|
|
261
305
|
perm = null;
|
|
262
306
|
refresh();
|
|
307
|
+
// Web-öğrenme: web'de arayıp cevap ürettiyse {soru→cevap}'ı eğitim datasına ekle
|
|
308
|
+
if (usedWeb && lastAnswer) {
|
|
309
|
+
const sources = (lastAnswer.match(/https?:\/\/[^\s<>"')\]]+/g) || []).slice(0, 8);
|
|
310
|
+
if (recordLearned(userText, lastAnswer, sources, config))
|
|
311
|
+
printItem({ kind: 'note', text: '✎ eğitim datasına eklendi' });
|
|
312
|
+
}
|
|
313
|
+
// Oto-hafıza: eşik geçildiyse arka planda hafızayı güncelle
|
|
314
|
+
if (shouldExtract(history))
|
|
315
|
+
triggerMemory(history, config);
|
|
263
316
|
}
|
|
264
317
|
// ── Kurulum ──
|
|
265
318
|
readline.emitKeypressEvents(process.stdin);
|
|
@@ -275,6 +328,8 @@ export async function runTui() {
|
|
|
275
328
|
account = a;
|
|
276
329
|
redrawAll();
|
|
277
330
|
} }).catch(() => { });
|
|
331
|
+
connectMcpServers().then((srv) => { if (srv && srv.length)
|
|
332
|
+
printItem({ kind: 'note', text: `🔌 ${srv.length} MCP sunucusu bağlandı` }); }).catch(() => { }); // MCP araçları (varsa)
|
|
278
333
|
const quit = () => { process.stdout.write('\x1b[r\x1b[?25h\x1b[2J\x1b[3J\x1b[H'); process.exit(0); };
|
|
279
334
|
process.on('exit', () => { try {
|
|
280
335
|
process.stdout.write('\x1b[r\x1b[?25h');
|
|
@@ -381,18 +436,35 @@ export async function runTui() {
|
|
|
381
436
|
refresh();
|
|
382
437
|
return;
|
|
383
438
|
}
|
|
384
|
-
|
|
439
|
+
inputHistory.push(v);
|
|
440
|
+
// ! shell modu — LLM'siz doğrudan shell komutu; çıktıyı modele bağlam olarak ekle
|
|
441
|
+
if (v.startsWith('!') && v.length > 1) {
|
|
442
|
+
const cmd = v.slice(1).trim();
|
|
443
|
+
printItem({ kind: 'user', text: v });
|
|
444
|
+
busy = true;
|
|
445
|
+
refresh();
|
|
446
|
+
executeTool('Bash', { command: cmd }).then((res) => {
|
|
447
|
+
printItem({ kind: 'tool', label: `! ${cmd.slice(0, 60)}`, result: sanitizeOutput(res.output), ok: res.ok });
|
|
448
|
+
history.push({ role: 'user', content: `Şu shell komutunu çalıştırdım:\n\`\`\`\n${cmd}\n\`\`\`\nÇıktı:\n\`\`\`\n${(res.output || '').slice(0, 4000)}\n\`\`\`` });
|
|
449
|
+
}).catch((e) => printItem({ kind: 'note', text: 'Komut hatası: ' + (e?.message || e) }))
|
|
450
|
+
.finally(() => { busy = false; refresh(); });
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
385
453
|
if (v.startsWith('/')) {
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
if (!
|
|
389
|
-
|
|
454
|
+
const tok0 = v.split(' ')[0];
|
|
455
|
+
// tam eşleşme yoksa menüde SEÇİLİ olanı kullan (skill/ext dahil), argümanları koru
|
|
456
|
+
if (!allCmds().some((c) => c.name === tok0)) {
|
|
457
|
+
const ms = allCmds().filter((c) => c.name.startsWith(tok0));
|
|
458
|
+
if (ms.length)
|
|
459
|
+
v = ms[Math.min(cmdSel, ms.length - 1)].name + v.slice(tok0.length);
|
|
460
|
+
}
|
|
390
461
|
cmdSel = 0;
|
|
391
|
-
|
|
462
|
+
const tok = v.split(' ')[0];
|
|
463
|
+
if (tok === '/cikis' || tok === '/exit' || tok === '/quit') {
|
|
392
464
|
quit();
|
|
393
465
|
return;
|
|
394
466
|
}
|
|
395
|
-
if (
|
|
467
|
+
if (tok === '/kopyala' || tok === '/copy') {
|
|
396
468
|
const last = [...history].reverse().find((mm) => mm.role === 'assistant');
|
|
397
469
|
if (last) {
|
|
398
470
|
process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
|
|
@@ -402,13 +474,29 @@ export async function runTui() {
|
|
|
402
474
|
printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
|
|
403
475
|
return;
|
|
404
476
|
}
|
|
405
|
-
|
|
477
|
+
const builtin = COMMANDS.some((c) => c.name === tok);
|
|
478
|
+
const skill = !builtin ? getSkill(tok.slice(1)) : undefined;
|
|
479
|
+
if (skill) {
|
|
480
|
+
const a = v.slice(tok.length).trim();
|
|
481
|
+
runTurn(buildSkillPrompt(skill, a), `/${skill.name}${a ? ' ' + a : ''}`);
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
const ext = (!builtin && !skill) ? getExtCommand(tok.slice(1)) : undefined;
|
|
485
|
+
if (ext) {
|
|
486
|
+
const a = v.slice(tok.length).trim();
|
|
487
|
+
runTurn(buildExtCommandPrompt(ext, a), `/${ext.name}${a ? ' ' + a : ''}`);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
406
490
|
runCommand(v);
|
|
407
491
|
return;
|
|
408
492
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
493
|
+
// @dosya mention — referanslanan dosya içeriğini modele enjekte et, kullanıcıya orijinali göster
|
|
494
|
+
const at = resolveAtMentions(v);
|
|
495
|
+
if (at.files.length) {
|
|
496
|
+
runTurn(at.augmented, v);
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
runTurn(v, v);
|
|
412
500
|
return;
|
|
413
501
|
}
|
|
414
502
|
if (key && key.name === 'backspace') {
|