natureco-cli 5.30.0 → 5.32.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,26 @@
2
2
 
3
3
  All notable changes to NatureCo CLI will be documented in this file.
4
4
 
5
+ ## [5.32.0] - 2026-07-04 — "AĞAÇ-HAFIZA + OTURUMLAR ARASI KALICILIK (Theseus mimarisi)"
6
+
7
+ ### ✨ Yeni (Hafıza)
8
+ - **Oturumlar arası hafiza DUZELDI**: eski persist dar regex kaliplariyla (sadece "adim X") fact cikariyordu; keyfi bilgi ("parolam X, hatirla") kayboluyordu. Artik ajan `memory_write` ile ANINDA kaydeder → yeni oturumda hatirlanir (E2E: parola S1→S2 ✓).
9
+ - **Agac-hafiza (`memory_tree`)**: kullanicinin OpenCode/Theseus icin tasarladigi tree-memory mimarisinden uyarlandi. Kok (1-kisisel/2-teknik/3-kararlar) → dal (## baslik) → yaprak. action: index|read|search|append. Duz `facts[]` listesinin "coplুğe donme" sorununu cozer; kategorize + logaritmik erisim.
10
+ - **Proaktif yukleme (Theseus deseni)**: her istekte hafiza agaci sistem prompt'una otomatik enjekte edilir ("BILDIGIN KALICI HAFIZA") → ajan on-demand aramaya guvenmeden baglami ZATEN bilir. (E2E: "NatureCoPixel neyle yapiliyor?"→"Godot" ✓; hafiza ile dosya sistemini ayirt eder.)
11
+ - Tek-primary + "bkz:" capraz referans; credential/secret asla duz metin.
12
+
13
+ Doğrulama: memory_write + memory_tree E2E (S1 kaydet → S2 yeni oturum hatirla); 6 yeni tree testi; **498 test yeşil**, ESLint temiz.
14
+
15
+ ## [5.31.0] - 2026-07-04 — "CHAT/CODE ARAYÜZ ZENGİNLEŞTİRME (araç görünürlüğü + düşünme + input alanı)"
16
+
17
+ ### ✨ Yeni (UX)
18
+ - **Araç görünürlugu**: agentic akista her arac ekranda gorunur — "🔧 <etiket> · <ozet> ✓/✗" (write_file/edit_file/bash/browser/mac_app_open...). Onceden streaming tool XML'ini gizlerken arac aktivitesi de gorunmuyordu; artik gorunur.
19
+ - **Düsünme gostergesi**: model yanit uretirken "💭 düşünüyor…", ilk token gelince temizlenir.
20
+ - **Gorunur input alani**: REPL prompt'u "💬 Sen ▸" + her girdiden once ince ayirici cizgi (cikti/girdi net ayrilir; readline tek-satir → satir duzenleme bozulmaz).
21
+ - **Gorunur acma yonlendirmesi (full mod)**: "kendi tarayicimda ac / dinlemek istiyorum" → headless `browser` yerine gorunur `open`/`start`/`mac_app_open` kullanilir.
22
+
23
+ Doğrulama: streaming UI E2E (💭 + 🔧 + ✓ gorunuyor); REPL regresyon; **492 test yeşil**.
24
+
5
25
  ## [5.30.0] - 2026-07-04 — "TAM KONTROL MODU (sahip opt-in: tüm araç+skill + computer-use)"
6
26
 
7
27
  ### ✨ Yeni
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.30.0",
3
+ "version": "5.32.0",
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"
@@ -1178,14 +1178,20 @@ async function startRepl(args) {
1178
1178
  const rl = readline.createInterface({
1179
1179
  input: createPasteSafeInput(process.stdin),
1180
1180
  output: createOutputFilter(process.stdout),
1181
- prompt: tui.styled('\n You ', { color: tui.PALETTE.primary, bold: true }),
1181
+ prompt: tui.styled(' 💬 Sen ▸ ', { color: tui.PALETTE.primary, bold: true }),
1182
1182
  terminal: true,
1183
1183
  });
1184
1184
  // Pipe/script kullanımında EOF, yanıt hâlâ üretilirken gelir —
1185
1185
  // aktif işlem varken kapanışı bekletmek için sayaç + kapalı-rl koruması
1186
1186
  let _busy = 0;
1187
1187
  let _rlClosed = false;
1188
- const safePrompt = () => { if (!_rlClosed) rl.prompt(); };
1188
+ // Görünür input alanı: her prompttan önce ince ayırıcı çizgi (readline prompt'u tek-satır
1189
+ // kalir → satir duzenleme/gecmis bozulmaz). Cikti ile girdi arasini net ayirir.
1190
+ const safePrompt = () => {
1191
+ if (_rlClosed) return;
1192
+ try { process.stdout.write(tui.styled('\n ' + '─'.repeat(54) + '\n', { color: tui.PALETTE.muted })); } catch {}
1193
+ rl.prompt();
1194
+ };
1189
1195
  safePrompt();
1190
1196
 
1191
1197
  const cleanup = async (exitCode = 0) => {
@@ -1465,7 +1471,7 @@ async function startRepl(args) {
1465
1471
 
1466
1472
  // Çok satırlı (paste) mesajları gönderildikten sonra ekranda göster
1467
1473
  if (line.indexOf('\n') !== -1) {
1468
- process.stdout.write(tui.styled(' You ', { color: tui.PALETTE.primary, bold: true }));
1474
+ process.stdout.write(tui.styled(' 💬 Sen ▸ ', { color: tui.PALETTE.primary, bold: true }));
1469
1475
  process.stdout.write(line + '\n');
1470
1476
  }
1471
1477
 
@@ -21,7 +21,7 @@ const os = require('os');
21
21
  // icinde approvals politikasini uyguluyor (isSafeCommand → direkt; tehlikeli → red;
22
22
  // digerleri → allowlist/full moda gore). Yani keyfi/yikici komut calismaz.
23
23
  // Diger ~85 arac (discord, telegram, cron, browser...) bilerek DISARIDA.
24
- const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir'];
24
+ const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir', 'memory_write', 'memory_tree'];
25
25
 
26
26
  const TOOL_ALIASES = {
27
27
  write_file: 'write_file', create_file: 'write_file', writefile: 'write_file', write: 'write_file', create: 'write_file', save_file: 'write_file', new_file: 'write_file',
@@ -31,6 +31,8 @@ const TOOL_ALIASES = {
31
31
  bash: 'bash', run_command: 'bash', shell: 'bash', shell_command: 'bash', exec: 'bash', run_terminal: 'bash', terminal: 'bash', run: 'bash', command: 'bash',
32
32
  file_search: 'file_search', glob: 'file_search', find_files: 'file_search', search_files: 'file_search', find: 'file_search',
33
33
  list_dir: 'list_dir', ls: 'list_dir', dir: 'list_dir', list_directory: 'list_dir', filesystem: 'list_dir',
34
+ memory_write: 'memory_write', remember: 'memory_write', save_memory: 'memory_write', memorize: 'memory_write', memory_save: 'memory_write', save_fact: 'memory_write',
35
+ memory_tree: 'memory_tree', tree_memory: 'memory_tree', memory: 'memory_tree', hafiza: 'memory_tree',
34
36
  };
35
37
 
36
38
  function expandHome(p) {
@@ -326,7 +328,7 @@ async function executeCall(call, opts = {}) {
326
328
  * callModel(messages) => Promise<{ content, toolCalls }>
327
329
  * Donus: { records, reply, iterations }
328
330
  */
329
- async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, maxIterations = 15 }) {
331
+ async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, onEvent, maxIterations = 15 }) {
330
332
  const messages = [{ role: 'system', content: systemPrompt }];
331
333
  for (const mm of historyMessages || []) messages.push({ role: mm.role, content: mm.content || '' });
332
334
  messages.push({ role: 'user', content: task });
@@ -349,7 +351,9 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
349
351
  messages.push({ role: 'assistant', content: content || '' });
350
352
  const feedbacks = [];
351
353
  for (const call of calls) {
354
+ if (onEvent) { try { onEvent({ phase: 'start', tool: call.tool, args: call.args }); } catch {} }
352
355
  const { records, feedback } = await executeCall(call, { toolsDir, loadTool, allowed: allowedSet, execFull });
356
+ if (onEvent) { try { onEvent({ phase: 'end', tool: call.tool, args: call.args, records }); } catch {} }
353
357
  allRecords.push(...records);
354
358
  feedbacks.push(feedback);
355
359
  }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * memory_tree — Ağaç-hafıza (kök → dal → yaprak). Kullanıcının OpenCode için tasarladığı
3
+ * tree-memory mimarisinden uyarlandı (opencode-hafiza-mimarisi.md).
4
+ *
5
+ * Felsefe: "bilgi saklama" değil "hızlı bilgi bulma". Düz liste zamanla çöplüğe döner;
6
+ * ağaç logaritmik arama sağlar (kök seç → dal seç → yaprak oku, <300 token).
7
+ *
8
+ * Yapı: ~/.natureco/memory/tree/<user>/
9
+ * 0-index.md — yönlendirme (hangi konu hangi kökte)
10
+ * 1-kisisel.md — Kişisel & Tercihler (## dallar altında yapraklar)
11
+ * 2-teknik.md — Teknik & Projeler
12
+ * 3-kararlar.md— Kararlar, Kurallar & Dersler
13
+ *
14
+ * Kurallar (mimariden): tek primary + "bkz:" çapraz referans; yeni bilgi ilgili dalın
15
+ * altına (dosya sonuna değil); credential/secret ASLA düz metin ("bkz: secrets vault").
16
+ */
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const os = require('os');
20
+
21
+ const ROOTS = [
22
+ { id: '1-kisisel', title: 'Kişisel & Tercihler', branches: ['Kimlik', 'Tercihler', 'İletişim Kalıpları'] },
23
+ { id: '2-teknik', title: 'Teknik & Projeler', branches: ['Projeler', 'Kurulum & Sistem', 'Teknik Referans'] },
24
+ { id: '3-kararlar', title: 'Kararlar, Kurallar & Dersler', branches: ['Kararlar', 'Kurallar & Kısıtlar', 'Öğrenilen Dersler', 'Tarihli Olaylar'] },
25
+ ];
26
+
27
+ function treeDir(username) {
28
+ return path.join(os.homedir(), '.natureco', 'memory', 'tree', String(username || 'default').toLowerCase());
29
+ }
30
+ function rootPath(username, id) { return path.join(treeDir(username), id + '.md'); }
31
+ function readSafe(username, id) { try { return fs.readFileSync(rootPath(username, id), 'utf8'); } catch { return ''; } }
32
+ function resolveRoot(root) {
33
+ if (!root) return ROOTS[0];
34
+ const r = String(root).toLowerCase();
35
+ return ROOTS.find((x) => x.id === r || x.id.endsWith(r.replace(/^\d+-/, '')) || x.id.includes(r) || x.title.toLowerCase().includes(r)) || ROOTS[0];
36
+ }
37
+
38
+ function ensureTree(username) {
39
+ const dir = treeDir(username);
40
+ fs.mkdirSync(dir, { recursive: true });
41
+ for (const r of ROOTS) {
42
+ const p = rootPath(username, r.id);
43
+ if (!fs.existsSync(p)) fs.writeFileSync(p, `# ${r.title}\n\n` + r.branches.map((b) => `## ${b}\n`).join('\n'), 'utf8');
44
+ }
45
+ const idx = path.join(dir, '0-index.md');
46
+ if (!fs.existsSync(idx)) fs.writeFileSync(idx, '# Hafıza İndeksi (kök → dal)\n\n' + ROOTS.map((r) => `- **${r.id}** — ${r.title}: ${r.branches.join(', ')}`).join('\n') + '\n', 'utf8');
47
+ }
48
+
49
+ // Modele enjekte edilen kompakt indeks (dal başlıkları + yaprak sayısı).
50
+ function buildIndex(username) {
51
+ ensureTree(username);
52
+ const lines = [];
53
+ for (const r of ROOTS) {
54
+ const txt = readSafe(username, r.id);
55
+ const branches = (txt.match(/^##\s+(.+)$/gm) || []).map((s) => s.replace(/^##\s+/, '').trim());
56
+ const leaves = (txt.match(/^\s*-\s+\S/gm) || []).length;
57
+ lines.push(`- ${r.id} (${r.title}) — dallar: ${branches.join(' · ') || '(boş)'}${leaves ? ` [${leaves} kayıt]` : ''}`);
58
+ }
59
+ return lines.join('\n');
60
+ }
61
+
62
+ function readRoot(username, id) {
63
+ ensureTree(username);
64
+ try { return fs.readFileSync(rootPath(username, resolveRoot(id).id), 'utf8'); } catch { return ''; }
65
+ }
66
+
67
+ // Theseus deseni: oturum BAŞINDA hafızayı proaktif yükle. Tüm kökler-dallar-yapraklardan
68
+ // (boş dallar hariç) kompakt bir özet çıkar → sistem prompt'una gömülür ki ajan
69
+ // on-demand aramaya güvenmeden bilgiyi ZATEN bilsin. maxChars aşılırsa kırpılır.
70
+ function buildDigest(username, maxChars = 2600) {
71
+ ensureTree(username);
72
+ const parts = [];
73
+ for (const r of ROOTS) {
74
+ const txt = readSafe(username, r.id);
75
+ let branch = '';
76
+ const byBranch = {};
77
+ for (const line of txt.split('\n')) {
78
+ if (line.startsWith('## ')) branch = line.slice(3).trim();
79
+ else if (/^\s*-\s+\S/.test(line)) (byBranch[branch] = byBranch[branch] || []).push(line.trim().replace(/^-\s*/, ''));
80
+ }
81
+ const nonEmpty = Object.entries(byBranch).filter(([, l]) => l.length);
82
+ if (nonEmpty.length) {
83
+ parts.push(`[${r.title}]`);
84
+ for (const [b, l] of nonEmpty) parts.push(` ${b}: ${l.join('; ')}`);
85
+ }
86
+ }
87
+ let out = parts.join('\n');
88
+ if (out.length > maxChars) out = out.slice(0, maxChars) + '\n…(fazlası için memory_tree ile oku)';
89
+ return out;
90
+ }
91
+
92
+ function search(username, query) {
93
+ ensureTree(username);
94
+ const q = String(query || '').toLowerCase().trim();
95
+ if (!q) return [];
96
+ const hits = [];
97
+ for (const r of ROOTS) {
98
+ const txt = readSafe(username, r.id);
99
+ let branch = '';
100
+ for (const line of txt.split('\n')) {
101
+ if (line.startsWith('## ')) branch = line.slice(3).trim();
102
+ else if (line.trim() && !line.startsWith('#') && line.toLowerCase().includes(q)) hits.push(`${r.id}/${branch}: ${line.trim()}`);
103
+ }
104
+ }
105
+ return hits;
106
+ }
107
+
108
+ function append(username, root, branch, content) {
109
+ ensureTree(username);
110
+ const r = resolveRoot(root);
111
+ const p = rootPath(username, r.id);
112
+ const br = String(branch || 'Genel').trim();
113
+ const leaf = '- ' + String(content).replace(/\s+/g, ' ').trim();
114
+ let txt = fs.readFileSync(p, 'utf8');
115
+ const bRe = new RegExp(`^##[ \\t]+${br.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[ \\t]*$`, 'mi');
116
+ const m = txt.match(bRe);
117
+ if (m) {
118
+ const idx = txt.indexOf(m[0]) + m[0].length;
119
+ txt = txt.slice(0, idx) + '\n' + leaf + txt.slice(idx);
120
+ } else {
121
+ txt = txt.trimEnd() + `\n\n## ${br}\n${leaf}\n`;
122
+ }
123
+ fs.writeFileSync(p, txt, 'utf8');
124
+ return { success: true, root: r.id, branch: br, saved: leaf };
125
+ }
126
+
127
+ module.exports = {
128
+ name: 'memory_tree',
129
+ description: 'Ağaç-hafıza (kök→dal→yaprak): kalıcı bilgiyi kategorize SAKLA/OKU/ARA. Yeni oturumda hatırlanır. action: index|read|search|append',
130
+ inputSchema: {
131
+ type: 'object',
132
+ properties: {
133
+ action: { type: 'string', description: 'index | read | search | append' },
134
+ username: { type: 'string', description: 'kullanıcı adı' },
135
+ root: { type: 'string', description: '1-kisisel | 2-teknik | 3-kararlar' },
136
+ branch: { type: 'string', description: 'dal başlığı (append için)' },
137
+ content: { type: 'string', description: 'yaprak metni (append için)' },
138
+ query: { type: 'string', description: 'arama (search için)' },
139
+ },
140
+ required: ['action'],
141
+ },
142
+ async execute(p) {
143
+ const u = p.username || 'default';
144
+ try {
145
+ if (p.action === 'index') return { success: true, index: buildIndex(u) };
146
+ if (p.action === 'read') return { success: true, content: readRoot(u, p.root || '1-kisisel') };
147
+ if (p.action === 'search') return { success: true, results: search(u, p.query || p.content || '') };
148
+ if (p.action === 'append') {
149
+ if (!p.content) return { success: false, error: 'content (yaprak metni) gerekli' };
150
+ return append(u, p.root || '1-kisisel', p.branch || 'Genel', p.content);
151
+ }
152
+ return { success: false, error: 'bilinmeyen action: ' + p.action + ' (index|read|search|append)' };
153
+ } catch (e) { return { success: false, error: e.message }; }
154
+ },
155
+ _internal: { ensureTree, buildIndex, buildDigest, readRoot, search, append, treeDir, rootPath, ROOTS },
156
+ };
@@ -222,6 +222,11 @@ async function workflow(params) {
222
222
  const { runAgentic } = require('./agentic-runner');
223
223
  const memCtx = memoryContext();
224
224
  const desktop = path.join(os.homedir(), 'Desktop');
225
+ const memUser = cfg.userName || 'default';
226
+ // Theseus deseni: oturum basinda hafiza agacini PROAKTIF yukle (on-demand aramaya
227
+ // guvenme). Digest = kayitli bilgiler (icerik); Index = yapi (kok→dal).
228
+ let treeIndex = '', treeDigest = '';
229
+ try { const mt = require('./memory_tree')._internal; treeDigest = mt.buildDigest(memUser); treeIndex = mt.buildIndex(memUser); } catch {}
225
230
  // Tam mod (sahibin opt-in'i): tum arac+skill'ler acilir, keyfi shell (yikici haric).
226
231
  const execFull = cfg.agentExec === 'full' || cfg.computerUse === true || String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full';
227
232
  let fullToolsBlock = '';
@@ -231,11 +236,11 @@ async function workflow(params) {
231
236
  '\n\nTAM MOD ACIK — su araclara da ERISIMIN VAR; o an ne gerekiyorsa dogrudan cagir:',
232
237
  '- mac_app_open: macOS uygulamasi ac. parametre: appName (orn. "WhatsApp", "Google Chrome", "Spotify")',
233
238
  '- mac_app_quit: macOS uygulamasi kapat. parametre: appName',
234
- '- browser: tarayici otomasyonu (Playwright). parametreler: action ("open"/"screenshot"/"html"/"evaluate"), url, script',
239
+ '- browser: HEADLESS tarayici otomasyonu (icerik cek/screenshot — kullaniciya GORUNMEZ). parametreler: action, url, script',
235
240
  '- computer_use: GUI otomasyonu. parametreler: action ("screenshot"/"click"/"type"/"keypress"/"scroll"), x, y, text, key',
236
241
  '- social_open: muzik/video/sosyal ac. parametreler: query, platform (spotify/youtube...)',
237
242
  '- macos_screenshot: ekran goruntusu al',
238
- '- bash ile de acabilirsin: "open -a WhatsApp", "open https://...", "start chrome https://..."',
243
+ '\nGORUNUR ACMA (onemli): Kullanici "kendi tarayicimda ac / gorunur ac / dinlemek/izlemek istiyorum" derse `browser` (headless, gorunmez) DEGIL, GORUNUR ac: macOS bash ile `open "https://..."` ya da `open -a "Google Chrome" "https://..."`; Windows `start "" "https://..."`. Uygulama icin `open -a WhatsApp` / mac_app_open. Muzik/video icin dogrudan YouTube/Spotify URL\'sini `open` ile ac.',
239
244
  '\nTum arac listesi (isimle cagir; parametre yanlissa <tool_results> duzeltir): ' + allNames.join(', '),
240
245
  ].join('\n');
241
246
  }
@@ -252,7 +257,11 @@ async function workflow(params) {
252
257
  '- list_dir: dizin icerigini listele. parametre: path',
253
258
  '- bash: kabuk komutu calistir (npm, git, node, python, test, ls, grep/findstr, mkdir...). parametre: command. Guvenli komutlar dogrudan calisir; yikici/tehlikeli komutlar guvenlik politikasiyla engellenir. Icerik aramasi icin grep/findstr kullan.',
254
259
  '- skill_view: gorevle ilgili bir skill yukle. parametre: name',
260
+ '- memory_write: HIZLI tek bilgi kaydet (isim, kisa tercih). parametreler: username ("' + memUser + '"), fact, category. YENI oturumda hatirlanir.',
261
+ '- memory_tree: AGAC-HAFIZA — zengin/kategorize kalici bilgi (proje, karar, teknik not, kisisel detay). action: index|read|search|append; username="' + memUser + '"; append icin: root (1-kisisel|2-teknik|3-kararlar) + branch (dal basligi) + content (yaprak). YENI oturumda hatirlanir.',
255
262
  '\nKurallar:',
263
+ '- Kullanici kalici bilgi paylasirsa (isim, tercih, parola, proje, karar, onemli detay) ya da "hatirla / not al / kaydet" derse HEMEN KAYDET (username="' + memUser + '"): kisa tek bilgi → memory_write; zengin/kategorize bilgi → memory_tree(append, dogru root/branch). Trivial/gecici seyleri kaydetme.',
264
+ '- Kullaniciya ozel bir sey sorulunca (gecmis, proje, tercih, karar) once memory_tree(search/read) ile ilgili kok/dali OKU (hedefli, tum hafizayi degil). Tek primary: bilgi tek yerde yasar; digerine sadece "bkz:". Credential/secret ASLA duz metin.',
256
265
  '- MEVCUT bir dosyayi degistirmeden ONCE read_file ile oku; sonra edit_file ile hedefli degisiklik yap (tum dosyayi write_file ile ezme).',
257
266
  '- Bir seyi nerede oldugunu bilmiyorsan once file_search/list_dir/bash(grep) ile kesfet.',
258
267
  '- Kod yazdiktan/degistirdikten sonra gerektiginde bash ile calistir/test et (orn. "node dosya.js", "npm test"); hata cikarsa duzelt.',
@@ -263,6 +272,8 @@ async function workflow(params) {
263
272
  '- Basit sohbet/selamlasma ise arac cagirma, dogrudan kisa yanit ver.',
264
273
  execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
265
274
  fullToolsBlock,
275
+ treeDigest ? ('\n\nBILDIGIN KALICI HAFIZA (bu kullaniciya ait, onceki oturumlardan hatirladiklarin; kullaniciya ozel bir sey sorulursa ONCE BUNU KULLAN — dosya arama, uydurma):\n' + treeDigest) : '',
276
+ treeIndex ? ('\n\nHafiza agaci yapisi (yukarida olmayan detay icin memory_tree(action:read/search) ile ilgili kok/dali oku):\n' + treeIndex) : '',
266
277
  skillsIndexBlock ? '\n\n' + skillsIndexBlock : '',
267
278
  ].filter(Boolean).join('\n');
268
279
 
@@ -279,11 +290,14 @@ async function workflow(params) {
279
290
 
280
291
  async function callModel(msgs) {
281
292
  if (streamOn) {
282
- const sani = makeSanitizeStream(botName, t => process.stdout.write(t));
293
+ let cleared = false;
294
+ const clearThinking = () => { if (!cleared) { process.stdout.write('\r\x1b[K'); cleared = true; } };
295
+ process.stdout.write('\x1b[2m 💭 düşünüyor…\x1b[0m');
296
+ const sani = makeSanitizeStream(botName, t => { clearThinking(); process.stdout.write(t); });
283
297
  const filter = makeStreamFilter(t => sani.push(t), null);
284
298
  const body = { model, stream: true, messages: msgs, temperature: 0.3, max_tokens: 16000 };
285
299
  const out = await apiCallStream(providerUrl, providerApiKey, body, d => filter.push(d));
286
- filter.end(); sani.end();
300
+ filter.end(); sani.end(); clearThinking();
287
301
  return { content: out.content || '', toolCalls: out.toolCalls || [] };
288
302
  }
289
303
  const body = { model, stream: false, messages: msgs, temperature: 0.3, max_tokens: 16000 };
@@ -292,10 +306,28 @@ async function workflow(params) {
292
306
  return { content: msg.content || '', toolCalls: msg.tool_calls || [] };
293
307
  }
294
308
 
309
+ // Araç aktivitesi gösterimi (TTY streaming): her araç icin "🔧 label · hint ✓/✗"
310
+ const TOOL_LABEL = { write_file: 'dosya yaz', read_file: 'oku', edit_file: 'düzenle', bash: 'komut', file_search: 'ara', list_dir: 'listele', skill_view: 'skill', browser: 'tarayıcı', browser_use: 'tarayıcı', mac_app_open: 'uygulama aç', mac_app_quit: 'uygulama kapat', computer_use: 'GUI', social_open: 'medya aç', macos_screenshot: 'ekran görüntüsü' };
311
+ function briefHint(args) {
312
+ if (!args || typeof args !== 'object') return '';
313
+ const v = args.appName || args.query || args.name || args.url || args.command || args.pattern || args.path || args.action;
314
+ return v ? String(v).replace(/\s+/g, ' ').slice(0, 46) : '';
315
+ }
316
+ const onEvent = streamOn ? (ev) => {
317
+ if (ev.phase === 'start') {
318
+ const label = TOOL_LABEL[ev.tool] || ev.tool;
319
+ const hint = briefHint(ev.args);
320
+ process.stdout.write('\n\x1b[2m 🔧 ' + label + (hint ? ' · ' + hint : '') + '\x1b[0m');
321
+ } else {
322
+ const rec = (ev.records || [])[0] || {};
323
+ process.stdout.write(rec.status === 'done' ? ' \x1b[32m✓\x1b[0m' : ' \x1b[31m✗\x1b[0m');
324
+ }
325
+ } : null;
326
+
295
327
  try {
296
328
  const { records, reply } = await runAgentic({
297
329
  callModel, systemPrompt: sysMsg, historyMessages, task,
298
- toolsDir: __dirname, execFull, maxIterations: 15,
330
+ toolsDir: __dirname, execFull, onEvent, maxIterations: 15,
299
331
  });
300
332
  const fileWrites = records.filter(r => r.tool === 'write_file' && r.status === 'done');
301
333
  let finalReply = reply || '';