wormclaude 1.0.48 → 1.0.50

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 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.48';
19
+ export const VERSION = '1.0.50';
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,8 +56,11 @@ 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;
@@ -94,12 +104,18 @@ export async function runTui() {
94
104
  const inputHistory = []; // gönderilen mesajlar (↑/↓ ile geri çağrılır)
95
105
  let histIdx = -1; // -1 = geçmişte gezinmiyor
96
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
+ ];
97
113
  // Slash menü: "/" ile başlayan girişe uyan komutlar
98
114
  const cmdMatches = () => {
99
115
  if (busy || !inputBuf.startsWith('/'))
100
116
  return [];
101
117
  const tok = inputBuf.split(' ')[0];
102
- return COMMANDS.filter((c) => c.name.startsWith(tok));
118
+ return allCmds().filter((c) => c.name.startsWith(tok));
103
119
  };
104
120
  // Slash komutları için bağlam (cli.tsx ile aynı arayüz) — /clear,/config,/model,/memory,/program…
105
121
  const cmdCtx = {
@@ -108,7 +124,7 @@ export async function runTui() {
108
124
  setHistory: (h) => { history.length = 0; history.push(...h); },
109
125
  note: (text) => printItem({ kind: 'note', text }),
110
126
  assistant: (text) => printItem({ kind: 'assistant', text }),
111
- clearConv: () => { history.length = 1; displayItems.length = 1; redrawAll(); }, // sistem msg + banner kalır
127
+ clearConv: () => { history.length = _sysCount; displayItems.length = 1; redrawAll(); }, // sistem mesajları + banner kalır
112
128
  exit: () => quit(),
113
129
  };
114
130
  async function runCommand(v) {
@@ -170,11 +186,13 @@ export async function runTui() {
170
186
  const inputLines = inputBoxLines(W); // çok-satırlı giriş (sarılmış)
171
187
  const m = cmdMatches();
172
188
  if (m.length) {
173
- // DİKEY slash menü (alt alta), seçili kırmızı kutunun üstünde.
174
- const items = m.slice(0, 8);
175
- const sel = Math.min(cmdSel, items.length - 1);
176
- const menu = items.map((c, i) => {
177
- const on = i === sel;
189
+ // DİKEY slash menü — KAYAN pencere (8 satır, seçimi takip eder), seçili kırmızı.
190
+ const sel = Math.min(cmdSel, m.length - 1);
191
+ const WIN = 8;
192
+ const start = Math.max(0, Math.min(sel - 3, m.length - WIN));
193
+ const view = m.slice(start, start + WIN);
194
+ const menu = view.map((c, i) => {
195
+ const on = (start + i) === sel;
178
196
  return ' ' + paint((on ? '✶ ' : ' ') + c.name.padEnd(13), on ? theme.redBright : theme.grey) + paint(cmdDesc(c.name) || c.desc || '', theme.greyDim);
179
197
  });
180
198
  body = [...menu, line, ...inputLines, line];
@@ -208,16 +226,28 @@ export async function runTui() {
208
226
  const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); refresh(); };
209
227
  // ── Agent döngüsü (M2: araçlar + izin). Model tool çağırırsa izin sorulup çalıştırılır,
210
228
  // sonuç geçmişe eklenip döngü devam eder; tool yoksa biter. ──
211
- async function runTurn(userText) {
229
+ async function runTurn(userText, displayText) {
212
230
  busy = true;
213
231
  streamChars = 0;
232
+ if (displayText !== undefined)
233
+ printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
214
234
  history.push({ role: 'user', content: userText });
215
235
  const timer = setInterval(() => { spin++; if (busy && !perm)
216
236
  refresh(); }, 120);
217
- let lastSig = '', sameCount = 0;
237
+ let lastSig = '', sameCount = 0, usedWeb = false, lastAnswer = '';
218
238
  try {
219
239
  for (let iter = 0; iter < 25; iter++) {
220
240
  streamChars = 0;
241
+ // Oto-compact: bağlam eşiği aşılınca otomatik özetle (uzun sohbet patlamasın)
242
+ if (shouldAutoCompact(history)) {
243
+ try {
244
+ const { history: nh } = await runCompact(history, config);
245
+ history.length = 0;
246
+ history.push(...nh);
247
+ printItem({ kind: 'note', text: '✎ bağlam otomatik özetlendi' });
248
+ }
249
+ catch { }
250
+ }
221
251
  let answer = '';
222
252
  let toolCalls = [];
223
253
  for await (const ev of streamChat(history, allToolSchemas(), config)) {
@@ -237,8 +267,10 @@ export async function runTui() {
237
267
  if (toolCalls.length)
238
268
  am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
239
269
  history.push(am);
240
- if (answer)
270
+ if (answer) {
271
+ lastAnswer = answer;
241
272
  printItem({ kind: 'assistant', text: answer });
273
+ }
242
274
  if (!toolCalls.length)
243
275
  break;
244
276
  // takılma koruması: aynı araç-çağrısı 3 kez üst üste → dur
@@ -260,7 +292,8 @@ export async function runTui() {
260
292
  perm = { label: toolLabel(c.name, args), name: c.name, resolve };
261
293
  refresh();
262
294
  }),
263
- onResult: (c, _i, res) => printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }),
295
+ onResult: (c, _i, res) => { if ((c.name === 'WebSearch' || c.name === 'WebFetch') && res.ok)
296
+ usedWeb = true; printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }); },
264
297
  });
265
298
  for (let i = 0; i < toolCalls.length; i++)
266
299
  history.push({ role: 'tool', tool_call_id: toolCalls[i].id, content: (results[i].output || '').slice(0, 8000) });
@@ -273,6 +306,15 @@ export async function runTui() {
273
306
  busy = false;
274
307
  perm = null;
275
308
  refresh();
309
+ // Web-öğrenme: web'de arayıp cevap ürettiyse {soru→cevap}'ı eğitim datasına ekle
310
+ if (usedWeb && lastAnswer) {
311
+ const sources = (lastAnswer.match(/https?:\/\/[^\s<>"')\]]+/g) || []).slice(0, 8);
312
+ if (recordLearned(userText, lastAnswer, sources, config))
313
+ printItem({ kind: 'note', text: '✎ eğitim datasına eklendi' });
314
+ }
315
+ // Oto-hafıza: eşik geçildiyse arka planda hafızayı güncelle
316
+ if (shouldExtract(history))
317
+ triggerMemory(history, config);
276
318
  }
277
319
  // ── Kurulum ──
278
320
  readline.emitKeypressEvents(process.stdin);
@@ -288,6 +330,8 @@ export async function runTui() {
288
330
  account = a;
289
331
  redrawAll();
290
332
  } }).catch(() => { });
333
+ connectMcpServers().then((srv) => { if (srv && srv.length)
334
+ printItem({ kind: 'note', text: `🔌 ${srv.length} MCP sunucusu bağlandı` }); }).catch(() => { }); // MCP araçları (varsa)
291
335
  const quit = () => { process.stdout.write('\x1b[r\x1b[?25h\x1b[2J\x1b[3J\x1b[H'); process.exit(0); };
292
336
  process.on('exit', () => { try {
293
337
  process.stdout.write('\x1b[r\x1b[?25h');
@@ -394,18 +438,35 @@ export async function runTui() {
394
438
  refresh();
395
439
  return;
396
440
  }
397
- // Slash komutu: tam eşleşme yoksa menüde SEÇİLİ olanı kullan
441
+ inputHistory.push(v);
442
+ // ! shell modu — LLM'siz doğrudan shell komutu; çıktıyı modele bağlam olarak ekle
443
+ if (v.startsWith('!') && v.length > 1) {
444
+ const cmd = v.slice(1).trim();
445
+ printItem({ kind: 'user', text: v });
446
+ busy = true;
447
+ refresh();
448
+ executeTool('Bash', { command: cmd }).then((res) => {
449
+ printItem({ kind: 'tool', label: `! ${cmd.slice(0, 60)}`, result: sanitizeOutput(res.output), ok: res.ok });
450
+ 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\`\`\`` });
451
+ }).catch((e) => printItem({ kind: 'note', text: 'Komut hatası: ' + (e?.message || e) }))
452
+ .finally(() => { busy = false; refresh(); });
453
+ return;
454
+ }
398
455
  if (v.startsWith('/')) {
399
- const tok = v.split(' ')[0];
400
- const matches = COMMANDS.filter((c) => c.name.startsWith(tok));
401
- if (!COMMANDS.some((c) => c.name === tok) && matches.length)
402
- v = matches[Math.min(cmdSel, matches.length - 1)].name;
456
+ const tok0 = v.split(' ')[0];
457
+ // tam eşleşme yoksa menüde SEÇİLİ olanı kullan (skill/ext dahil), argümanları koru
458
+ if (!allCmds().some((c) => c.name === tok0)) {
459
+ const ms = allCmds().filter((c) => c.name.startsWith(tok0));
460
+ if (ms.length)
461
+ v = ms[Math.min(cmdSel, ms.length - 1)].name + v.slice(tok0.length);
462
+ }
403
463
  cmdSel = 0;
404
- if (v === '/cikis' || v === '/exit' || v === '/quit') {
464
+ const tok = v.split(' ')[0];
465
+ if (tok === '/cikis' || tok === '/exit' || tok === '/quit') {
405
466
  quit();
406
467
  return;
407
468
  }
408
- if (v === '/kopyala' || v === '/copy') {
469
+ if (tok === '/kopyala' || tok === '/copy') {
409
470
  const last = [...history].reverse().find((mm) => mm.role === 'assistant');
410
471
  if (last) {
411
472
  process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
@@ -415,13 +476,29 @@ export async function runTui() {
415
476
  printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
416
477
  return;
417
478
  }
418
- inputHistory.push(v);
479
+ const builtin = COMMANDS.some((c) => c.name === tok);
480
+ const skill = !builtin ? getSkill(tok.slice(1)) : undefined;
481
+ if (skill) {
482
+ const a = v.slice(tok.length).trim();
483
+ runTurn(buildSkillPrompt(skill, a), `/${skill.name}${a ? ' ' + a : ''}`);
484
+ return;
485
+ }
486
+ const ext = (!builtin && !skill) ? getExtCommand(tok.slice(1)) : undefined;
487
+ if (ext) {
488
+ const a = v.slice(tok.length).trim();
489
+ runTurn(buildExtCommandPrompt(ext, a), `/${ext.name}${a ? ' ' + a : ''}`);
490
+ return;
491
+ }
419
492
  runCommand(v);
420
493
  return;
421
494
  }
422
- inputHistory.push(v); // geçmişe ekle (↑ ile geri çağrılır)
423
- printItem({ kind: 'user', text: v });
424
- runTurn(v);
495
+ // @dosya mention referanslanan dosya içeriğini modele enjekte et, kullanıcıya orijinali göster
496
+ const at = resolveAtMentions(v);
497
+ if (at.files.length) {
498
+ runTurn(at.augmented, v);
499
+ return;
500
+ }
501
+ runTurn(v, v);
425
502
  return;
426
503
  }
427
504
  if (key && key.name === 'backspace') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wormclaude",
3
- "version": "1.0.48",
3
+ "version": "1.0.50",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {