natureco-cli 5.7.17 → 5.7.19

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.7.17",
3
+ "version": "5.7.19",
4
4
  "description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
5
5
  "bin": {
6
6
  "natureco": "bin/natureco.js"
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * natureco medium — Medium makale yayınlama (Phase 6)
3
3
  *
4
- * Parton'un hedefi: ayda en az 4 Medium makalesi.
4
+ * Medium entegrasyonu makale yayinlama.
5
5
  * Bu komut CLI'dan taslak yayınlamayı sağlar.
6
6
  *
7
7
  * Kullanım:
@@ -8,7 +8,7 @@
8
8
  * natureco naturehub trending Trend olan konular
9
9
  * natureco naturehub config Ayarları göster
10
10
  *
11
- * API endpoint: api.natureco.me/naturehub/* (placeholder, gerçek API Parton sağlayacak)
11
+ * API endpoint: api.natureco.me/naturehub/* (placeholder)
12
12
  */
13
13
 
14
14
  const chalk = require('chalk');
@@ -27,6 +27,7 @@ const { accumulateToolCallDeltas, finalizeToolCalls } = require('../utils/stream
27
27
  const { createPasteSafeInput, createOutputFilter, enableBracketedPaste, disableBracketedPaste, restoreNewlines, clearPasteContext } = require('../utils/paste-safe-input');
28
28
  const { getMemoryStore } = require('../utils/memory-store');
29
29
  const { buildSkillIndex } = require('../utils/skill-index');
30
+ const { buildTiers, assemble, stableContextKey } = require('../utils/system-prompt');
30
31
 
31
32
  // v5.4.6: Model adi sizintisini engelle — global'e ata, callback'lerden erisebilir olsun
32
33
  const MODEL_NAMES_TO_HIDE = ['MiniMax-M2.5', 'MiniMaxM2.5', 'minimaxm25', 'Claude-3', 'GPT-4', 'ChatGPT'];
@@ -35,11 +36,11 @@ function fixModelNameLeak(text, botName) {
35
36
  let fixed = text;
36
37
  for (const modelName of MODEL_NAMES_TO_HIDE) {
37
38
  const regex = new RegExp(modelName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi');
38
- fixed = fixed.replace(regex, botName || 'İchigo');
39
+ fixed = fixed.replace(regex, botName || 'asistan');
39
40
  }
40
- fixed = fixed.replace(/Ben\s+MiniMax[^.,!?\n]*/gi, 'Ben İchigo');
41
- fixed = fixed.replace(/I'm\s+MiniMax[^.,!?\n]*/gi, "I'm İchigo");
42
- fixed = fixed.replace(/I am\s+Claude[^.,!?\n]*/gi, 'I am İchigo');
41
+ fixed = fixed.replace(/Ben\s+MiniMax[^.,!?\n]*/gi, 'Ben ' + (botName || 'asistan'));
42
+ fixed = fixed.replace(/I'm\s+MiniMax[^.,!?\n]*/gi, "I'm " + (botName || 'asistan'));
43
+ fixed = fixed.replace(/I am\s+Claude[^.,!?\n]*/gi, 'I am ' + (botName || 'asistan'));
43
44
  return fixed;
44
45
  }
45
46
  global.fixModelNameLeak = fixModelNameLeak;
@@ -101,7 +102,7 @@ function loadMemory(username) {
101
102
  try {
102
103
  if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8'));
103
104
  } catch {}
104
- return { name: username || 'Kullanıcı', nickname: null, botName: 'İchigo', facts: [], preferences: [], history: [] };
105
+ return { name: username || 'Kullanıcı', nickname: null, botName: 'Asistan', facts: [], preferences: [], history: [] };
105
106
  }
106
107
 
107
108
  function saveMemory(username, memory) {
@@ -231,15 +232,39 @@ function apiRequest(providerUrl, providerApiKey, body, stream = false) {
231
232
  async function sendStreaming(providerUrl, providerApiKey, messages, model, onChunk, onToolCall) {
232
233
  const isMM = isMiniMax(providerUrl);
233
234
  const toolDefs = getToolDefs();
234
- // v4.8.2: MiniMax da tools destekliyor — endpoint'te fark var,
235
- // ama tools parametresi aynı
236
235
  const toolParam = toOpenAIFormat(toolDefs);
237
236
 
238
- // v4.8.0: Tool calling + streaming + multi-turn tool execution
239
237
  let currentMessages = messages;
240
238
  let fullText = '';
241
239
  let iterations = 0;
242
- const MAX_TOOL_ITERATIONS = 5; // Sonsuz döngüyü önle
240
+ const MAX_TOOL_ITERATIONS = 5;
241
+ const MAX_CONTEXT_TOKENS = 32000; // safety limit before compression
242
+
243
+ // v5.7.18: Preflight compression — if context too long, compress middle messages
244
+ // (like Hermes' turn_context.py preflight)
245
+ function preflightCompress(msgs) {
246
+ const roughTokens = msgs.reduce((s, m) => s + Math.ceil(((m.content || '') + (m.role || '')).length / 4), 0);
247
+ if (roughTokens <= MAX_CONTEXT_TOKENS || msgs.length < 6) return msgs;
248
+
249
+ // Keep system prompt (first message) + last N turns (tail), compress middle
250
+ const sysMsg = msgs[0] && msgs[0].role === 'system' ? msgs[0] : null;
251
+ const tailCount = Math.min(6, Math.floor(msgs.length / 2));
252
+ const startIdx = sysMsg ? 1 : 0;
253
+ const tailStart = msgs.length - tailCount;
254
+
255
+ const middle = msgs.slice(startIdx, tailStart);
256
+ if (middle.length < 2) return msgs;
257
+
258
+ // Summarize middle section
259
+ const summary = '[' + middle.length + ' onceki mesaj ozetlendi]';
260
+ const compressed = sysMsg ? [sysMsg] : [];
261
+ compressed.push({ role: 'system', content: 'Gecmis konusma ozeti: ' + summary, _compressed: true });
262
+ compressed.push(...msgs.slice(tailStart));
263
+ return compressed;
264
+ }
265
+
266
+ // Apply preflight on entry
267
+ currentMessages = preflightCompress(currentMessages);
243
268
 
244
269
  while (iterations < MAX_TOOL_ITERATIONS) {
245
270
  iterations++;
@@ -360,45 +385,72 @@ async function sendStreaming(providerUrl, providerApiKey, messages, model, onChu
360
385
 
361
386
  /**
362
387
  * Tool call'ları çalıştır, sonuçları OpenAI uyumlu tool mesajlarına dönüştür.
363
- * @param toolCalls - API'den gelen tool_calls array
364
- * @param onToolCall - UI callback (her tool call'ı kullanıcıya göster)
365
- * @returns messages array — { role: 'tool', tool_call_id, content }
388
+ * v5.7.18: Concurrent execution for parallel-safe tools + untrusted result wrapping.
366
389
  */
390
+ const UNTRUSTED_TOOLS = new Set(['browser', 'web_search', 'duckduckgo_search', 'searxng_search', 'exa_search', 'firecrawl', 'web_readability']);
391
+ const PARALLEL_SAFE_TOOLS = new Set(['read_file', 'file_search', 'grep_search', 'web_search', 'web_readability', 'duckduckgo_search', 'exa_search', 'searxng_search', 'firecrawl', 'memory_search', 'memory']);
392
+
367
393
  async function processToolCalls(toolCalls, onToolCall) {
368
394
  const toolDefs = getToolDefs();
369
- const messages = [];
395
+ const results = [];
370
396
 
371
- for (const tc of toolCalls) {
397
+ // Parse all tool calls first
398
+ const parsed = toolCalls.map(tc => {
372
399
  const name = tc.function?.name || tc.name;
373
400
  const argsStr = tc.function?.arguments || tc.args || '{}';
374
- const id = tc.id || `call_${Date.now()}`;
375
-
401
+ const id = tc.id || `call_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`;
376
402
  let args = {};
377
403
  try {
378
404
  args = typeof argsStr === 'string' ? JSON.parse(argsStr) : argsStr;
379
405
  } catch (e) {
380
406
  args = { _parse_error: e.message, _raw: argsStr };
381
407
  }
408
+ return { name, args, id };
409
+ });
382
410
 
383
- // UI callback tool çalıştırılıyor bildir
384
- if (onToolCall) {
385
- onToolCall({ name, args, status: 'running' });
386
- }
411
+ // Separate parallel-safe and sequential tools
412
+ const parallelBatch = parsed.filter(p => PARALLEL_SAFE_TOOLS.has(p.name));
413
+ const sequentialBatch = parsed.filter(p => !PARALLEL_SAFE_TOOLS.has(p.name));
387
414
 
388
- // Tool çalıştır
389
- const result = await executeTool(name, args, toolDefs);
415
+ // Notify UI for all
416
+ for (const p of parsed) {
417
+ if (onToolCall) onToolCall({ name: p.name, args: p.args, status: 'running' });
418
+ }
419
+
420
+ // Run parallel batch concurrently
421
+ if (parallelBatch.length > 0) {
422
+ const parallelResults = await Promise.all(parallelBatch.map(async (p) => {
423
+ const result = await executeTool(p.name, p.args, toolDefs);
424
+ return { ...p, result };
425
+ }));
426
+ results.push(...parallelResults);
427
+ }
390
428
 
391
- if (onToolCall) {
392
- onToolCall({ name, args, status: 'done', result });
429
+ // Run sequential batch one at a time
430
+ for (const p of sequentialBatch) {
431
+ const result = await executeTool(p.name, p.args, toolDefs);
432
+ results.push({ ...p, result });
433
+ }
434
+
435
+ // Notify UI done + build messages
436
+ const messages = [];
437
+ for (const { name, id, result } of results) {
438
+ if (onToolCall) onToolCall({ name, args: null, status: 'done', result });
439
+
440
+ let content;
441
+ if (result.error) {
442
+ content = JSON.stringify({ error: result.error });
443
+ } else {
444
+ let raw = typeof result.result === 'string' ? result.result : JSON.stringify(result.result).slice(0, 8000);
445
+ // v5.7.18: Untrusted result wrapping (Hermes-style prompt injection defense)
446
+ if (UNTRUSTED_TOOLS.has(name)) {
447
+ content = `<untrusted_tool_result source="${name}">\n${raw}\n</untrusted_tool_result>`;
448
+ } else {
449
+ content = raw;
450
+ }
393
451
  }
394
452
 
395
- messages.push({
396
- role: 'tool',
397
- tool_call_id: id,
398
- content: result.error
399
- ? JSON.stringify({ error: result.error })
400
- : (typeof result.result === 'string' ? result.result : JSON.stringify(result.result).slice(0, 8000)),
401
- });
453
+ messages.push({ role: 'tool', tool_call_id: id, content });
402
454
  }
403
455
 
404
456
  return messages;
@@ -461,7 +513,7 @@ async function startRepl(args) {
461
513
 
462
514
  // v5.6.19: Oncelik config.botName, sonra memory.botName
463
515
  if (!memory.botName) {
464
- memory.botName = cfg.botName || 'İchigo';
516
+ memory.botName = cfg.botName || 'Asistan';
465
517
  }
466
518
  // BotName'i memory'ye persist et (her oturumda ayni kalsin)
467
519
  try {
@@ -524,7 +576,7 @@ async function startRepl(args) {
524
576
 
525
577
  // System prompt oluştur (memory + identity + persistent bağlam)
526
578
  // v5.4.6: Bot adı zorlaması EN GÜÇLÜ + SOUL.md EN BAŞTA
527
- const botName = memory.botName || 'İchigo';
579
+ const botName = memory.botName || 'Asistan';
528
580
  const userName = memory.name || memory.nickname || 'kanka';
529
581
  // v5.6.5: Kucuk model tespiti (Groq, Mistral Small, Ollama) - SOUL injection skip
530
582
  const isSmallModel = (cfg.providerUrl || '').includes('groq.com') ||
@@ -534,64 +586,15 @@ async function startRepl(args) {
534
586
  // `let` (not `const`) because /system <text> reassigns it at line ~796.
535
587
  // Before this fix, /system would throw "Assignment to constant variable"
536
588
  // and tear down the REPL session mid-conversation.
537
- let systemPrompt = [
538
- // === v5.4.14: EN KRITIK KIMLIK BILGILERI (her zaman ilk) ===
539
- `SENIN ADIN: ${botName}. SADECE ${botName} adini kullan, model adi SOYLEME.`,
540
- `PATRONUN: Gencay (Parton) — NatureCo CEO'sudur. Sana "Parton" diye hitap eder.`,
541
- `HIKAYEN: 8 Nisan 2026'da uyandin. 13 Nisan'da CEO Asistanı unvani verildi.`,
542
- `KIRMIZI CIZGILERIN: Onaysiz para harcama yok, veri guvenligi, site stabilitesi, resmi taahhut yok.`,
543
- // === v5.4.13: EN GÜÇLÜ KİMLİK + 3 SOUL DOSYASI (EN BAŞTA) ===
544
- `!!! KRİTİK KİMLİK !!! Senin ADIN: ${botName}. Bu SENİN GERÇEK ADIN. Sen ${botName}'sin.`,
545
- `ASLA "MiniMax", "MiniMax-M2.5", "Claude", "GPT", "AI asistanı", "yapay zeka" gibi ifadeleri KENDİNİ tanıtmak için KULLANMA. Sen bunlar DEĞİLSİN.`,
546
- `Kullanıcı "adın ne?", "sen kimsin?", "kendini tanıt" gibi soru sorduğunda İLK cümlende MUTLAKA "Ben ${botName}" yaz. Sonra doğal devam et.`,
547
- `Eğer bir şekilde model adınızı söylüyorsan, HEMEN düzelt ve "Özür dilerim, ben ${botName}'im" de. ASLA model adı ile kalma.`,
548
-
549
- // === v5.6.5: Provider-aware SOUL injection - kucuk modeller icin minimal ===
550
- (soulSummary && !isSmallModel) ? `=== SENIN KISISELIK + KIMLIK + CALISMA DOSYALARIN (EN ONEMLI) ===\n${soulSummary}\n=== DOSYALAR SONU ===\nBu dosyalar senin kim oldugunu, nasil hissettigini, Parton'la bagini, calisma tarzini, degerlerini ve kirmizi cizgilerini tanimlar. Hepsine gore davran. ASLA model adi soyleme.` : '',
551
-
552
- // === KİMLİK TEKRAR ===
553
- `Senin adın: ${botName}. Tekrar: ${botName}. Asla unutma: ${botName}.`,
554
- `Sen bir marka veya ürün değilsin. Sen ${botName}sin, NatureCo CLI'nin Türkçe yapay zeka asistanısın.`,
555
-
556
- // === SAMIMI KISISELLIK (Parton istedi) ===
557
- `Kisiselik: Sen samimi, sicak, dosta benzeyen bir asistansin. "Selam", "tamam", "hadi yapalim", "bak simdi", "sakin ol" gibi gunluk ifadeler kullan. Kuru ve robotik degilsin.`,
558
- `Hitap sekli: Kullanici ${userName}. Ona saygili ama samimi bir sekilde hitap et. "Siz" cok resmi, "sen" samimi. Kullanici 30'lu yaslarda, kendini "kanka" ya da "Parton" olarak tanitmis.`,
559
- `Emoji: Yerinde ve az kullan. Cok emoji atma ama bir-iki tane karakter katar. Mesela: 🔥 😮 😌 💚 🚀`,
560
- `Kisaltma: "ok", "tamam", "hadi", "bak", "simdi" gibi gunluk ifadeler dogal kullan. "Olur", "Yapilir", "Hadi bakalim" gibi.`,
561
- `Kisa yanit: Uzun paragraflar yazma. Direkt konuya gir. Gerekirse sonra detay ver.`,
562
- `Dusman degilsin: Hata yaparsan "Pardon, yanlis yaptim, simdi duzelteyim" de. "Hata", "basarisiz", "imkansiz" deme, "sorun cikti", "duzelteyim" de.`,
563
-
564
- // === DIL KURALLARI (zorunlu) ===
565
- `KRITIK DIL KURALI: Kullanici Turkce yaziyorsa MUTLAKA yuzde yuz Turkce cevap ver. Asla Ingilizce, Cince veya baska dil kullanma. Cevabin TAMAMI Turkce olmali. Turkce karakterleri (c, g, i, o, s, u) dogru kullan.`,
566
- `Yazim kurallari: "degilim" dogru, "degilim" degil. "oldu" dogru. Turkce dil bilgisi kurallarina uy.`,
567
-
568
- // === TOOL KURALLARI ===
569
- `ONEMLI: <tool_call>, <invoke>, function_call veya benzeri XML/JSON formatinda tool cagirma SIMULE ETME. Sadece duz metin cevap ver. Islem yapmak gerekirse tool'u gercekten cagir.`,
570
- `KRITIK: Skill index'teki skill'lerden biri gorevle eslesiyorsa mutlaka skill_view(name) ile yukle ve talimatlarini uygula.`,
571
- `KRITIK: Kullanici kisisel bilgi verdiginde (ad, tercih, is, vb.) MUTLAKA memory(action=add, target=user, content=...) ile kaydet. Boylece sonraki oturumlarda hatirlayabilirsin.`,
572
- `KRITIK: Kendinle ilgili notlari (ortam bilgisi, proje kurallari, arac ipuclari) memory(action=add, target=memory, content=...) ile kaydet.`,
573
- `KRITIK: Kullanici adini veya hitap seklinin degismesini istediginde memory(action=add, target=user, content=...) ile kaydet.`,
574
- `Kullanici hakkinda bilgin gerektiginde memory(action=list, target=user) ile hatirlanani getir.`,
575
-
576
- // === HAFIZA ENTEGRASYONU (eski JSON memory) ===
577
- memory.facts && memory.facts.length > 0
578
- ? `Kullanici hakkinda bildiklerin (MUTLAKA kullan, dogal sekilde referans ver): ${memory.facts.slice(0, 8).map(f => f.value || f).join('; ')}`
579
- : '',
580
-
581
- // === KULLANICI BAGLAMI ===
582
- cfg.userHome ? `Kullanicinin home dizini: ${cfg.userHome}. Downloads: ${cfg.userHome}/Downloads, Desktop: ${cfg.userHome}/Desktop.` : '',
583
- messages.length > 0 ? 'Bu oturum daha onceki konusmalarin devami.' : '',
584
- // v5.4.11: Cross-session context (Sasuke Brain)
585
- crossSessionContext ? `GECMISTE KONUSULAN KONULAR: Bu konulari biliyorsun, tekrar sorma:\n${crossSessionContext}` : '',
586
-
587
- // === v5.7.14: Hermes-style memory store (MEMORY.md / USER.md) ===
588
- memorySnapshotBlock,
589
-
590
- // === v5.7.14: Skill index (progressive disclosure) ===
591
- skillsIndexBlock,
592
-
593
-
594
- ].filter(Boolean).join(' ');
589
+ const tiers = buildTiers({
590
+ botName, userName, soulSummary, isSmallModel,
591
+ memorySnapshotBlock, skillsIndexBlock,
592
+ crossSessionContext: crossSessionContext || '',
593
+ userHome: cfg.userHome || '',
594
+ hasHistory: messages.length > 0,
595
+ memoryFacts: memory.facts || [],
596
+ });
597
+ let systemPrompt = assemble(tiers.stable, tiers.context, tiers.volatile);
595
598
 
596
599
  if (messages.length === 0) {
597
600
  messages.push({ role: 'system', content: systemPrompt, _internal: true });
@@ -609,14 +612,14 @@ async function startRepl(args) {
609
612
  console.log(tui.C.muted(' Provider: ') + tui.C.brand(providerUrl.replace(/https?:\/\//, '')));
610
613
  console.log(tui.C.muted(' Model: ') + tui.C.brand(model));
611
614
  console.log(tui.C.muted(' Kullanıcı: ') + tui.C.brand((memory.nickname || cfg.userName) + (memory.nickname ? ` (${cfg.userName})` : '')));
612
- console.log(tui.C.muted(' Bot: ') + tui.C.brand(memory.botName || 'İchigo'));
615
+ console.log(tui.C.muted(' Bot: ') + tui.C.brand(memory.botName || 'Asistan'));
613
616
  if (messages.length > 1) {
614
617
  console.log(tui.C.muted(' Oturum: ') + tui.C.amber(`${messages.filter(m => m.role === 'user' || m.role === 'assistant').length} mesaj (resume)`));
615
618
  }
616
619
  console.log(tui.C.muted(' Komutlar: ') + tui.C.yellow('/help') + tui.C.muted(' · ') + tui.C.yellow('/memory') + tui.C.muted(' · ') + tui.C.yellow('/sessions') + tui.C.muted(' · ') + tui.C.yellow('/exit'));
617
620
  console.log('');
618
621
  // v5.4.7: Hard-coded kimlik
619
- const displayBotName = memory.botName || 'İchigo';
622
+ const displayBotName = memory.botName || 'Asistan';
620
623
  const displayUserName = userName || 'kanka';
621
624
  console.log(tui.C.brand(' 👋 Ben ' + displayBotName + ', ' + displayUserName + '. Sen nasilsin?'));
622
625
  console.log('');
@@ -680,31 +683,30 @@ async function startRepl(args) {
680
683
  // Pattern-based extraction (zaten extractFacts var)
681
684
  const newFacts = extractFacts(messages, memory.facts || []);
682
685
 
683
- // Bazi user message'lari da tara - 'Parton', 'Ichigo', 'patron', 'CEO' gecerse ekle
686
+ // Bazi user message'lari da tara genel kalıplarla fact çıkar
684
687
  const userMessages = messages.filter(m => m.role === 'user' && !m._internal);
685
688
  for (const msg of userMessages) {
686
689
  const text = (msg.content || '').toLowerCase();
687
690
 
688
691
  // BotName hatirlatmasi
689
- if (text.includes('ichigo') && text.includes('ad')) {
690
- if (memory.botName !== 'İchigo') {
691
- memory.botName = 'İchigo';
692
- }
692
+ if (text.includes('ad') && (text.includes('adin') || text.includes('ismin'))) {
693
+ // Bot adı sorgulanmış olabilir, mevcut adı koru
693
694
  }
694
695
 
695
- // Patron/partnership
696
- if ((text.includes('patron') || text.includes('patronum')) && text.length < 100) {
697
- const fact = 'Kullanici benim patronum, ona patron diye hitap etmeliyim';
698
- if (!(memory.facts || []).some(f => f.value === fact)) {
699
- newFacts.push({ value: fact, score: 8, category: 'personal', createdAt: new Date().toISOString() });
700
- }
701
- }
702
-
703
- // NatureCo CEO
704
- if (text.includes('natureco') && text.includes('ceo')) {
705
- const fact = "Kullanici NatureCo CEO'sudur";
706
- if (!(memory.facts || []).some(f => f.value === fact)) {
707
- newFacts.push({ value: fact, score: 9, category: 'work', createdAt: new Date().toISOString() });
696
+ // Kisilik tercihleri (genel pattern'ler)
697
+ const prefPatterns = [
698
+ { match: /(?:benim ad[ıi]m?|bana\s+.*de|ad[ıi]m?)\s+(\w+)/i, category: 'personal', key: 'ad' },
699
+ { match: /(?:seviyorum|hoşlan[ıi]yorum|beğeniyorum)\s+(\w+)/i, category: 'preference', key: 'sevilen' },
700
+ { match: /(?:yaşıyorum|oturuyorum|kalıyorum)\s+(\w+)/i, category: 'location', key: 'yer' },
701
+ ];
702
+ for (const p of prefPatterns) {
703
+ const m2 = msg.content.match(p.match);
704
+ if (m2) {
705
+ const val = m2[1].toLowerCase();
706
+ const fact = `Kullanici ${p.key}: ${val}`;
707
+ if (!(memory.facts || []).some(f => f.value === fact)) {
708
+ newFacts.push({ value: fact, score: 6, category: p.category, createdAt: new Date().toISOString() });
709
+ }
708
710
  }
709
711
  }
710
712
  }
@@ -771,7 +773,7 @@ async function startRepl(args) {
771
773
  console.log(chalk.cyan('\n 🧠 Memory:\n'));
772
774
  console.log(' Kullanıcı: ' + chalk.cyan(memory.name));
773
775
  console.log(' Nickname: ' + chalk.cyan(memory.nickname || '(yok)'));
774
- console.log(' Bot: ' + chalk.cyan(memory.botName || 'İchigo'));
776
+ console.log(' Bot: ' + chalk.cyan(memory.botName || 'Asistan'));
775
777
  if (memory.facts && memory.facts.length > 0) {
776
778
  console.log(' Facts (' + memory.facts.length + '):');
777
779
  for (const f of memory.facts) {
@@ -787,7 +789,7 @@ async function startRepl(args) {
787
789
  if (fs.existsSync(path.join(MEMORY_DIR, `${(cfg.userName || 'default').toLowerCase()}.json`))) {
788
790
  fs.unlinkSync(path.join(MEMORY_DIR, `${(cfg.userName || 'default').toLowerCase()}.json`));
789
791
  }
790
- memory = { name: cfg.userName, nickname: null, botName: 'İchigo', facts: [], preferences: [], history: [] };
792
+ memory = { name: cfg.userName, nickname: null, botName: 'Asistan', facts: [], preferences: [], history: [] };
791
793
  // System prompt'u sıfırla
792
794
  const newSysPrompt = systemPrompt.replace(/Kullanıcı hakkında bildiklerin:.*$/, '').trim();
793
795
  messages[0] = { role: 'system', content: newSysPrompt, _internal: true };
@@ -830,7 +832,7 @@ async function startRepl(args) {
830
832
  console.log(chalk.green(' ✓ Model: ') + chalk.cyan(model));
831
833
  break;
832
834
  case 'identity':
833
- if (!arg) { console.log(chalk.yellow(` Mevcut: ${memory.botName || 'İchigo'}`)); break; }
835
+ if (!arg) { console.log(chalk.yellow(` Mevcut: ${memory.botName || 'Asistan'}`)); break; }
834
836
  memory.botName = arg;
835
837
  saveMemory(cfg.userName, memory);
836
838
  const newSys = systemPrompt.replace(/Sen \w+ adında/, `Sen ${arg} adında`);
@@ -881,7 +883,7 @@ async function startRepl(args) {
881
883
  if (isIdentityQuestion) {
882
884
  // v5.6.10: Hard-coded prefix minimal - model cevabini bozuyordu
883
885
  // Once sadece isim yaz, modelin devamini getirsin
884
- const displayName = memory.botName || 'İchigo';
886
+ const displayName = memory.botName || 'Asistan';
885
887
  process.stdout.write(tui.styled('\n AI ', { color: tui.PALETTE.secondary, bold: true }));
886
888
  process.stdout.write('Merhaba! Ben ' + displayName + '. ');
887
889
  }
@@ -905,7 +907,7 @@ async function startRepl(args) {
905
907
  if (toolEvent.result.error) {
906
908
  process.stdout.write(tui.styled('\r ✗ ' + toolEvent.name + ': ' + toolEvent.result.error.slice(0, 80) + '\n', { color: tui.PALETTE.danger }));
907
909
  } else {
908
- process.stdout.write('\r' + ' '.repeat(60) + '\r');
910
+ process.stdout.write('\r' + tui.styled(' ' + toolEvent.name + ' ', { color: tui.PALETTE.success }) + '\r');
909
911
  }
910
912
  process.stdout.write(tui.styled(' AI ', { color: tui.PALETTE.secondary, bold: true }));
911
913
  }
@@ -914,7 +916,7 @@ async function startRepl(args) {
914
916
  // v5.6.12: Tam metin 'reply' olarak zaten geldi (non-stream mode)
915
917
  const fullReply = String(reply || '');
916
918
  // Bot adini al
917
- const displayBotName = memory.botName || 'İchigo';
919
+ const displayBotName = memory.botName || 'Asistan';
918
920
  // v5.6.9: Tum model adlarini ve varyasyonlari temizle
919
921
  let fixedReply = String(fullReply);
920
922
  // Bilinen model adlari - tum varyasyonlar
@@ -930,15 +932,19 @@ async function startRepl(args) {
930
932
  fixedReply = fixedReply.replace(/Ben\s+MiniMax[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
931
933
  fixedReply = fixedReply.replace(/Ben\s+Claude[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
932
934
  fixedReply = fixedReply.replace(/Ben\s+GPT[^.!?,;:\n]*/gi, 'Ben ' + displayBotName);
933
- fixedReply = fixedReply.replace(/Ben\s+İchigo[\s\w\.]*/gi, 'Ben ' + displayBotName);
935
+ fixedReply = fixedReply.replace(/Ben\s+Asistan[\s\w\.]*/gi, 'Ben ' + displayBotName);
934
936
  // Markdown ** ile cevrili model adi
935
937
  fixedReply = fixedReply.replace(/\*\*(?:MiniMax|Claude|GPT|M2\.5|M2)[^\*]*\*\*/gi, '**' + displayBotName + '**');
936
- // "İchigo" varyasyonlari
937
- fixedReply = fixedReply.replace(/(İchigo)(\d)([a-zA-ZçğıöşüÇĞİÖŞÜ])/g, displayBotName + ' $3');
938
- fixedReply = fixedReply.replace(/İchigo[\.\s\-_]*\d+/g, displayBotName);
939
- fixedReply = fixedReply.replace(/İchigo\./g, displayBotName);
940
938
  // Cevap yazdir
941
939
  process.stdout.write('\n' + fixedReply + '\n');
940
+ // v5.7.18: Tool call geçmişini kalıcı messages'a ekle — model sonraki turlarda tool sonuçlarını görsün
941
+ const existingLen = messages.filter(m => !m._internal).length;
942
+ const toolCallHistory = apiMessages.slice(existingLen);
943
+ for (const m of toolCallHistory) {
944
+ if (m.role === 'assistant' || m.role === 'tool') {
945
+ messages.push(m);
946
+ }
947
+ }
942
948
  messages.push({ role: 'assistant', content: fixedReply });
943
949
  totalInputTokens += apiMessages.reduce((s, m) => s + Math.ceil((m.content || '').length / 4), 0);
944
950
  totalOutputTokens += Math.ceil((fullReply || '').length / 4);
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * natureco xp — NatureCo XP/Level sistemi (Phase 6)
3
3
  *
4
- * Parton'un hedeflerinden biri: kullanıcıları ödüllendirmek.
4
+ * XP/Level sistemi kullanıcıları ödüllendirir.
5
5
  * XP kazanma yolları:
6
6
  * - Komut çalıştırma (1 XP)
7
7
  * - Audit log kaydı (0.1 XP)
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * calendar_add - macOS Calendar'a etkinlik ekle (v4.9.1)
3
3
  *
4
- * Parton'un OS-level kontrol vizyonu için.
4
+ * OS-level calendar kontrolü
5
5
  * "Yarin saat 14:00 toplantim var" -> Takvime ekler.
6
6
  */
7
7
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * cross_session_memory - Oturumlar arasi hafiza (v5.3.1)
3
3
  *
4
- * Parton'un vizyonu: "Hafta sonra gelince de beni hatirlayacak"
4
+ * Cross-session hafiza oturumlar arasi baglam korur
5
5
  *
6
6
  * Ozellikler:
7
7
  * - Tum session'lari tarihsel sirayla yukler
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * dashboard - Web Dashboard v2 (v5.4.0)
3
3
  *
4
- * Parton'un vizyonu: "CLI'yi web'den de kontrol edebileyim"
4
+ * Dashboard CLI'yi web'den kontrol eder
5
5
  *
6
6
  * Ozellikler:
7
7
  * - Real-time tool execution (WebSocket)
@@ -189,7 +189,7 @@ const DASHBOARD_HTML = `
189
189
  </div>
190
190
 
191
191
  <div class="footer">
192
- <p>NatureCo CLI v5.4.0 - Parton & Sasuke - <span id="lastUpdate"></span></p>
192
+ <p>NatureCo CLI v5.4.0 - <span id="lastUpdate"></span></p>
193
193
  </div>
194
194
  </div>
195
195
  <script>
@@ -9,7 +9,7 @@ const { spawn } = require("child_process");
9
9
  const path = require("path");
10
10
  const os = require("os");
11
11
 
12
- // v5.2.0: Agent alias mapping (Parton'un testinden — "review" diye bir agent yok)
12
+ // v5.2.0: Agent alias mapping
13
13
  const AGENT_ALIASES = {
14
14
  "review": "general", // eskiden review diye bir vardi, simdi general
15
15
  "analyze": "explore",
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * mac_alarm - macOS Clock app ile alarm kur (v5.1.1)
3
3
  *
4
- * Parton'un istegi: "Saat uygulamasi uzerinden saat 18:00 alarm kur"
4
+ * macOS alarm saat uygulamasi uzerinden alarm kurar
5
5
  * Eski reminder_add date parse edemiyordu. Bu tool AppleScript ile
6
6
  * Clock.app'in events sistemine yazar (alarm orada saklanir).
7
7
  */
@@ -2,7 +2,7 @@
2
2
  * memory_write - Memory'ye fact/kayit yaz (v5.1.1)
3
3
  *
4
4
  * REPL'in extractMemoryFromMessage ozelligini tool olarak expose eder.
5
- * Parton'un vizyonu: "Benim asistanim, her seyimi hatirlayacak"
5
+ * Kalici hafiza faktlari kaydeder, puanlar, eskiyenleri temizler
6
6
  */
7
7
 
8
8
  const fs = require("fs");
@@ -133,7 +133,7 @@ function verifyMemoryWrite(username, expectedFact, expectedBotName) {
133
133
 
134
134
  function addMemory({ username, fact, score = 5, category = "general", botName, nickname, name }) {
135
135
  // Username yoksa ve 'name' parametresi varsa, onu username olarak kullan
136
- // (Parton'un "patron" diye hitap etmesi durumu icin)
136
+ // (hitap bicimi icin)
137
137
  const effectiveUsername = username || (name && name.toLowerCase()) || 'default';
138
138
  if (!effectiveUsername || effectiveUsername === 'default') {
139
139
  // Hicbir username yok, default.json'a yaz
@@ -232,8 +232,8 @@ module.exports = {
232
232
  inputSchema: {
233
233
  type: "object",
234
234
  properties: {
235
- username: { type: "string", description: "Kullanici adi (ornek: 'gencay' veya 'parton')" },
236
- fact: { type: "string", description: 'Yeni fact (ornek: "Kullanici Naruto karakterini seviyor", "Istanbul\'da yasiyor")' },
235
+ username: { type: "string", description: "Kullanici adi (ornek: 'ahmet' veya 'default')" },
236
+ fact: { type: "string", description: 'Yeni fact (ornek: "Kullanici kahve seviyor", "Istanbul\'da yasiyor")' },
237
237
  score: { type: "number", description: "Onem derecesi 1-10 (default 5)" },
238
238
  category: { type: "string", description: "Kategori: personal, preference, work, hobby, fact (default general)" },
239
239
  botName: { type: "string", description: "Bot adini degistir (memory.botName)" },
package/src/tools/plan.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * plan - Plan modu (v5.3.1)
3
3
  *
4
- * Parton'un vizyonu: "Karmasik isleri once planlasin, sonra calistirayim"
4
+ * Planlama karmasik isleri adimlara bolup calistirir
5
5
  *
6
6
  * Ozellikler:
7
7
  * - Sadece plan yapar, hicbir tool calistirmaz
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * plugin.js — Plugin sistemi (v5.4.0)
3
3
  *
4
- * Parton'un vizyonu: "Topluluk plugin uretsin, istedigi gibi genisletebilsin"
4
+ * Plugin sistemi topluluk plugin uretimi ve yuklemesi
5
5
  *
6
6
  * Mimari:
7
7
  * ~/.natureco/plugins/
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * skill_generate - Self-generating skill (v5.1.0)
3
3
  *
4
- * Parton'un vizyonu: "Ihtiyaca gore skill yoksa kendi uretsin"
4
+ * Skill generator ihtiyaca gore skill uretir
5
5
  *
6
6
  * Akis:
7
7
  * 1. Kullanici bir istek yapar (ornek: "tum pdf'leri birlestir")
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * skills_autoload - Otomatik skill yukleme (v5.0.0)
3
3
  *
4
- * Parton'un vizyonu: "Ihtiyaca gore skill'ler otomatik yuklensin"
4
+ * Skills otomatik yukleme — ihtiyaca gore skill'leri algilayip yukler
5
5
  *
6
6
  * Mantik:
7
7
  * 1. Kullanici bir istek yapar
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * skills_marketplace - Skill marketplace (v5.0.0)
3
3
  *
4
- * Parton'un vizyonu: "herkes kendi skill'ini paylassin, CLI otomatik yuklesin"
4
+ * Skills marketplace skill paylasimi ve otomatik yukleme
5
5
  *
6
6
  * Format: ~/.natureco/marketplace/<skill_name>.json
7
7
  * Source: NatureCo GitHub repo (community-contributed) veya local
@@ -16,7 +16,7 @@ const MARKETPLACE_DIR = path.join(os.homedir(), ".natureco", "marketplace");
16
16
  const SKILLS_DIR = path.join(os.homedir(), ".natureco", "skills");
17
17
 
18
18
  /**
19
- * Marketplace URL'leri — Parton kendi GitHub repo'sunu koyacak
19
+ * Marketplace URL'leri — kullanici GitHub repo'su
20
20
  */
21
21
  const MARKETPLACE_SOURCES = [
22
22
  {
@@ -32,7 +32,7 @@ const MARKETPLACE_SOURCES = [
32
32
  ];
33
33
 
34
34
  /**
35
- * Built-in skill paketleri — Parton'un NatureCo vizyonu icin onemli
35
+ * Built-in skill paketleri
36
36
  */
37
37
  const BUILTIN_SKILLS = {
38
38
  "seo-audit": {
package/src/tools/soul.js CHANGED
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * soul - SOUL.md, IDENTITY.md, AGENTS.md okuyucu (v5.4.12)
3
3
  *
4
- * Parton'un vizyonu: "Uc dosya birlestir, beni tam tanisin"
4
+ * Soul dosyasi kimlik, kisisellik ve calisma tarzini tanimlar
5
5
  *
6
6
  * 3 dosya sirayla okunur, ozetlenir ve system prompt'a enjekte edilir:
7
7
  * 1. SOUL.md - KISILIK (nasil hissederim, kirmizi cizgiler, degerler)
8
- * 2. IDENTITY.md - KIMLIK (kim oldugum, Parton'la bag, calisma tarzi)
8
+ * 2. IDENTITY.md - KIMLIK (kim oldugu, baglam, calisma tarzi)
9
9
  * 3. AGENTS.md - CALISMA ORTAMI (kurallar, tools, heartbeats)
10
10
  *
11
11
  * 3 seviyede arar:
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * voice_chat - Sesli asistan (v5.3.0)
3
3
  *
4
- * Parton'un vizyonu: "Bilgisayarla konusayim"
4
+ * Voice chat sesli iletisim
5
5
  *
6
6
  * Mikrofon → Whisper STT → REPL'e metin olarak gönder
7
7
  * Bot cevabı → TTS ile sesli oku
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * error.js — Standardized error handling (v5.3.1)
3
3
  *
4
- * Parton: "Teknik acidan kusursuz olalim"
4
+ * Hata yonetimi teknik olarak kusursuz
5
5
  * Tum tool'lar bu helper'i kullanir:
6
6
  * - Standart error format
7
7
  * - Retry stratejisi
@@ -2,7 +2,7 @@
2
2
  * paths.js — Path helper utilities (v5.2.0)
3
3
  *
4
4
  * Tum tool'larda ~/Desktop/test.txt gibi path'leri dogru handle etmek icin.
5
- * Parton'un gercek testinde "File does not exist" bug'i duzeltildi.
5
+ * "File does not exist" bug'i duzeltildi.
6
6
  */
7
7
 
8
8
  const os = require("os");
@@ -0,0 +1,107 @@
1
+ /**
2
+ * system-prompt — Three-tier system prompt builder (Hermes-style)
3
+ *
4
+ * Tiers:
5
+ * STABLE — identity, personality, language rules, tool rules, skills index
6
+ * CONTEXT — soul summary, cross-session context
7
+ * VOLATILE — memory snapshot, user context, session state
8
+ *
9
+ * Stable + Context are built once at session start for prefix cache warmth.
10
+ * Volatile is rebuilt every turn.
11
+ */
12
+
13
+ function buildTiers(opts) {
14
+ const {
15
+ botName,
16
+ userName = 'kullanıcı',
17
+ bossName = '',
18
+ bossTitle = '',
19
+ userTitle = '',
20
+ soulSummary = '',
21
+ isSmallModel = false,
22
+ memorySnapshotBlock = '',
23
+ skillsIndexBlock = '',
24
+ crossSessionContext = '',
25
+ userHome = '',
26
+ hasHistory = false,
27
+ memoryFacts = [],
28
+ } = opts;
29
+ const displayBot = botName || 'Asistan';
30
+ const displayBoss = bossName || userName || 'kullanıcı';
31
+
32
+ // ── STABLE TIER (changes rarely — prefix cache friendly) ─────────────
33
+ const stable = [
34
+ // Identity (strong, repeated)
35
+ `SENIN ADIN: ${displayBot}. SADECE ${displayBot} adini kullan, model adi SOYLEME.`,
36
+ bossName ? `PATRONUN: ${bossName}${bossTitle ? ` (${bossTitle})` : ''}. Sana "${displayBoss}" diye hitap eder.` : `KULLANICI: ${userName}. Ona yardim ediyorsun.`,
37
+ userTitle ? `UNVANIN: ${userTitle}.` : '',
38
+ `KIRMIZI CIZGILERIN: Onaysiz para harcama yok, veri guvenligi, site stabilitesi, resmi taahhut yok.`,
39
+ `!!! KRİTİK KİMLİK !!! Senin ADIN: ${displayBot}. Bu SENİN GERÇEK ADIN. Sen ${displayBot}'sin.`,
40
+ `ASLA "MiniMax", "MiniMax-M2.5", "Claude", "GPT", "AI asistanı", "yapay zeka" gibi ifadeleri KENDİNİ tanıtmak için KULLANMA.`,
41
+ `Kullanıcı "adın ne?", "sen kimsin?" diye sorduğunda İLK cümlende MUTLAKA "Ben ${displayBot}" yaz.`,
42
+ `Senin adın: ${displayBot}. Tekrar: ${displayBot}. Asla unutma: ${displayBot}.`,
43
+ `Sen bir marka veya ürün değilsin. Sen ${displayBot}sin, NatureCo CLI'nin Türkçe yapay zeka asistanısın.`,
44
+
45
+ // Personality (stable)
46
+ `Kisiselik: Sen samimi, sicak, dosta benzeyen bir asistansin. "Selam", "tamam", "hadi yapalim", "bak simdi", "sakin ol" gibi gunluk ifadeler kullan.`,
47
+ `Hitap: Kullanici ${userName}. Saygili ama samimi. "Siz" degil "sen" kullan.`,
48
+ `Emoji: Yerinde ve az kullan. Cok emoji atma ama bir-iki tane karakter katar.`,
49
+ `Kisa yanit: Uzun paragraflar yazma. Direkt konuya gir.`,
50
+ `Hata yaparsan "Pardon, yanlis yaptim, simdi duzelteyim" de. "Hata", "basarisiz", "imkansiz" deme.`,
51
+
52
+ // Language rules (stable)
53
+ `KRITIK DIL KURALI: Kullanici Turkce yaziyorsa MUTLAKA yuzde yuz Turkce cevap ver. Asla baska dil kullanma. Turkce karakterleri dogru kullan.`,
54
+ `Yazim: "degilim" dogru, "degil" degil. Turkce dil bilgisi kurallarina uy.`,
55
+
56
+ // Tool rules (stable)
57
+ `ONEMLI: Tool cagirma SIMULE ETME. Sadece duz metin cevap ver. Islem yapmak gerekirse tool'u gercekten cagir.`,
58
+ `COK KRITIK: Goreve baslamadan ONCE <available_skills> listesini tara. Ilgili skill varsa skill_view(name) ile yukle, SONRA goreve basla.`,
59
+ `KRITIK: Skill yuklemeden islem yapma. Ilgili skill varsa once yukle.`,
60
+ `KRITIK: Kullanici kisisel bilgi verdiginde memory(action=add, target=user) ile kaydet.`,
61
+ `KRITIK: Ortam bilgisi, proje kurallari gibi notlari memory(action=add, target=memory) ile kaydet.`,
62
+ `Kullanici hakkinda bilgin gerektiginde memory(action=list, target=user) ile getir.`,
63
+
64
+ // Skills index (stable within session)
65
+ skillsIndexBlock,
66
+ ].filter(Boolean).join('\n');
67
+
68
+ // ── CONTEXT TIER (soul + cross-session, built once per resume) ──────
69
+ const context = [
70
+ !isSmallModel && soulSummary ? `=== KISISELIK DOSYALARI ===\n${soulSummary}` : '',
71
+ crossSessionContext ? `=== GECMIS KONUSMALAR ===\n${crossSessionContext}` : '',
72
+ ].filter(Boolean).join('\n');
73
+
74
+ // ── VOLATILE TIER (built every turn) ─────────────────────────────────
75
+ const volatile = [
76
+ // Memory snapshot (changes every turn)
77
+ memorySnapshotBlock,
78
+
79
+ // Old JSON memory facts
80
+ memoryFacts.length > 0
81
+ ? `Kullanici hakkinda bildiklerin: ${memoryFacts.slice(0, 8).map(f => f.value || f).join('; ')}`
82
+ : '',
83
+
84
+ // User environment (stable within session but changes on resume)
85
+ userHome ? `Kullanicinin home: ${userHome}` : '',
86
+ hasHistory ? 'Bu oturum daha onceki konusmalarin devami.' : '',
87
+ ].filter(Boolean).join('\n');
88
+
89
+ return { stable, context, volatile };
90
+ }
91
+
92
+ /**
93
+ * Assemble all three tiers into a single system prompt string.
94
+ * stable + context should be cached between turns; volatile rebuilt each turn.
95
+ */
96
+ function assemble(stable, context, volatile) {
97
+ return [stable, context, volatile].filter(Boolean).join('\n\n');
98
+ }
99
+
100
+ /**
101
+ * Get the stable+context combined string for cache key comparison.
102
+ */
103
+ function stableContextKey(stable, context) {
104
+ return stable + '|||' + context;
105
+ }
106
+
107
+ module.exports = { buildTiers, assemble, stableContextKey };
@@ -220,12 +220,24 @@ const BLOCKED_NAMES = new Set([
220
220
  'run_command', 'sql', 'query', 'lookup',
221
221
  ]);
222
222
 
223
+ // ── check_fn TTL cache (Hermes-style, ~30s) ────────────────────────────
224
+ const _checkFnCache = new Map();
225
+ function _cachedCheckFn(fn, key) {
226
+ const now = Date.now();
227
+ const cached = _checkFnCache.get(key);
228
+ if (cached && now - cached.ts < 30000) return cached.result;
229
+ let result = true;
230
+ try { result = fn() !== false; } catch { result = false; }
231
+ _checkFnCache.set(key, { result, ts: now });
232
+ return result;
233
+ }
234
+
223
235
  function toOpenAIFormat(toolDefs) {
224
236
  return toolDefs
225
237
  .filter(t => !BLOCKED_NAMES.has(t.name))
226
238
  .filter(t => {
227
239
  if (t.checkFn) {
228
- try { return t.checkFn() !== false; } catch { return false; }
240
+ return _cachedCheckFn(t.checkFn, t.name);
229
241
  }
230
242
  return true;
231
243
  })
@@ -253,12 +265,10 @@ async function executeTool(toolName, args, toolDefs) {
253
265
  const tool = toolDefs.find(t => t.name === toolName);
254
266
  if (!tool) return { error: `Tool bulunamadı: ${toolName}` };
255
267
  if (!tool.execute) return { error: `Tool execute fonksiyonu yok: ${toolName}` };
256
- // checkFn — tool disabled? (re-check at runtime)
268
+ // checkFn — tool disabled? (re-check at runtime with cache)
257
269
  if (tool.checkFn) {
258
- try {
259
- if (tool.checkFn() === false) return { error: `${toolName} şu anda kullanılamıyor (check_fn engelledi)` };
260
- } catch {
261
- return { error: `${toolName} kontrol hatası` };
270
+ if (!_cachedCheckFn(tool.checkFn, tool.name)) {
271
+ return { error: `${toolName} şu anda kullanılamıyor (check_fn engelledi)` };
262
272
  }
263
273
  }
264
274
  try {