wormclaude 1.0.48 → 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 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.49';
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) {
@@ -208,16 +224,28 @@ export async function runTui() {
208
224
  const printItem = (it) => { displayItems.push(it); process.stdout.write(itemAnsi(it, cols()) + '\n'); refresh(); };
209
225
  // ── Agent döngüsü (M2: araçlar + izin). Model tool çağırırsa izin sorulup çalıştırılır,
210
226
  // sonuç geçmişe eklenip döngü devam eder; tool yoksa biter. ──
211
- async function runTurn(userText) {
227
+ async function runTurn(userText, displayText) {
212
228
  busy = true;
213
229
  streamChars = 0;
230
+ if (displayText !== undefined)
231
+ printItem({ kind: 'user', text: displayText }); // skill/@mention: gösterim farklı
214
232
  history.push({ role: 'user', content: userText });
215
233
  const timer = setInterval(() => { spin++; if (busy && !perm)
216
234
  refresh(); }, 120);
217
- let lastSig = '', sameCount = 0;
235
+ let lastSig = '', sameCount = 0, usedWeb = false, lastAnswer = '';
218
236
  try {
219
237
  for (let iter = 0; iter < 25; iter++) {
220
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
+ }
221
249
  let answer = '';
222
250
  let toolCalls = [];
223
251
  for await (const ev of streamChat(history, allToolSchemas(), config)) {
@@ -237,8 +265,10 @@ export async function runTui() {
237
265
  if (toolCalls.length)
238
266
  am.tool_calls = toolCalls.map((t) => ({ id: t.id, type: 'function', function: { name: t.name, arguments: t.args || '{}' } }));
239
267
  history.push(am);
240
- if (answer)
268
+ if (answer) {
269
+ lastAnswer = answer;
241
270
  printItem({ kind: 'assistant', text: answer });
271
+ }
242
272
  if (!toolCalls.length)
243
273
  break;
244
274
  // takılma koruması: aynı araç-çağrısı 3 kez üst üste → dur
@@ -260,7 +290,8 @@ export async function runTui() {
260
290
  perm = { label: toolLabel(c.name, args), name: c.name, resolve };
261
291
  refresh();
262
292
  }),
263
- onResult: (c, _i, res) => printItem({ kind: 'tool', label: toolLabel(c.name, res.args), result: sanitizeOutput(res.output), ok: res.ok }),
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 }); },
264
295
  });
265
296
  for (let i = 0; i < toolCalls.length; i++)
266
297
  history.push({ role: 'tool', tool_call_id: toolCalls[i].id, content: (results[i].output || '').slice(0, 8000) });
@@ -273,6 +304,15 @@ export async function runTui() {
273
304
  busy = false;
274
305
  perm = null;
275
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);
276
316
  }
277
317
  // ── Kurulum ──
278
318
  readline.emitKeypressEvents(process.stdin);
@@ -288,6 +328,8 @@ export async function runTui() {
288
328
  account = a;
289
329
  redrawAll();
290
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)
291
333
  const quit = () => { process.stdout.write('\x1b[r\x1b[?25h\x1b[2J\x1b[3J\x1b[H'); process.exit(0); };
292
334
  process.on('exit', () => { try {
293
335
  process.stdout.write('\x1b[r\x1b[?25h');
@@ -394,18 +436,35 @@ export async function runTui() {
394
436
  refresh();
395
437
  return;
396
438
  }
397
- // Slash komutu: tam eşleşme yoksa menüde SEÇİLİ olanı kullan
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
+ }
398
453
  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;
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
+ }
403
461
  cmdSel = 0;
404
- if (v === '/cikis' || v === '/exit' || v === '/quit') {
462
+ const tok = v.split(' ')[0];
463
+ if (tok === '/cikis' || tok === '/exit' || tok === '/quit') {
405
464
  quit();
406
465
  return;
407
466
  }
408
- if (v === '/kopyala' || v === '/copy') {
467
+ if (tok === '/kopyala' || tok === '/copy') {
409
468
  const last = [...history].reverse().find((mm) => mm.role === 'assistant');
410
469
  if (last) {
411
470
  process.stdout.write(`\x1b]52;c;${Buffer.from(last.content, 'utf8').toString('base64')}\x07`);
@@ -415,13 +474,29 @@ export async function runTui() {
415
474
  printItem({ kind: 'note', text: 'Kopyalanacak yanıt yok.' });
416
475
  return;
417
476
  }
418
- inputHistory.push(v);
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
+ }
419
490
  runCommand(v);
420
491
  return;
421
492
  }
422
- inputHistory.push(v); // geçmişe ekle (↑ ile geri çağrılır)
423
- printItem({ kind: 'user', text: v });
424
- runTurn(v);
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);
425
500
  return;
426
501
  }
427
502
  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.49",
4
4
  "description": "WormClaude CLI - uncensored security+code assistant (ink TUI, Claude-style)",
5
5
  "type": "module",
6
6
  "bin": {