natureco-cli 5.30.0 → 5.31.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,16 @@
2
2
 
3
3
  All notable changes to NatureCo CLI will be documented in this file.
4
4
 
5
+ ## [5.31.0] - 2026-07-04 — "CHAT/CODE ARAYÜZ ZENGİNLEŞTİRME (araç görünürlüğü + düşünme + input alanı)"
6
+
7
+ ### ✨ Yeni (UX)
8
+ - **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.
9
+ - **Düsünme gostergesi**: model yanit uretirken "💭 düşünüyor…", ilk token gelince temizlenir.
10
+ - **Gorunur input alani**: REPL prompt'u "💬 Sen ▸" + her girdiden once ince ayirici cizgi (cikti/girdi net ayrilir; readline tek-satir → satir duzenleme bozulmaz).
11
+ - **Gorunur acma yonlendirmesi (full mod)**: "kendi tarayicimda ac / dinlemek istiyorum" → headless `browser` yerine gorunur `open`/`start`/`mac_app_open` kullanilir.
12
+
13
+ Doğrulama: streaming UI E2E (💭 + 🔧 + ✓ gorunuyor); REPL regresyon; **492 test yeşil**.
14
+
5
15
  ## [5.30.0] - 2026-07-04 — "TAM KONTROL MODU (sahip opt-in: tüm araç+skill + computer-use)"
6
16
 
7
17
  ### ✨ Yeni
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.30.0",
3
+ "version": "5.31.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
 
@@ -326,7 +326,7 @@ async function executeCall(call, opts = {}) {
326
326
  * callModel(messages) => Promise<{ content, toolCalls }>
327
327
  * Donus: { records, reply, iterations }
328
328
  */
329
- async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, maxIterations = 15 }) {
329
+ async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, onEvent, maxIterations = 15 }) {
330
330
  const messages = [{ role: 'system', content: systemPrompt }];
331
331
  for (const mm of historyMessages || []) messages.push({ role: mm.role, content: mm.content || '' });
332
332
  messages.push({ role: 'user', content: task });
@@ -349,7 +349,9 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
349
349
  messages.push({ role: 'assistant', content: content || '' });
350
350
  const feedbacks = [];
351
351
  for (const call of calls) {
352
+ if (onEvent) { try { onEvent({ phase: 'start', tool: call.tool, args: call.args }); } catch {} }
352
353
  const { records, feedback } = await executeCall(call, { toolsDir, loadTool, allowed: allowedSet, execFull });
354
+ if (onEvent) { try { onEvent({ phase: 'end', tool: call.tool, args: call.args, records }); } catch {} }
353
355
  allRecords.push(...records);
354
356
  feedbacks.push(feedback);
355
357
  }
@@ -231,11 +231,11 @@ async function workflow(params) {
231
231
  '\n\nTAM MOD ACIK — su araclara da ERISIMIN VAR; o an ne gerekiyorsa dogrudan cagir:',
232
232
  '- mac_app_open: macOS uygulamasi ac. parametre: appName (orn. "WhatsApp", "Google Chrome", "Spotify")',
233
233
  '- mac_app_quit: macOS uygulamasi kapat. parametre: appName',
234
- '- browser: tarayici otomasyonu (Playwright). parametreler: action ("open"/"screenshot"/"html"/"evaluate"), url, script',
234
+ '- browser: HEADLESS tarayici otomasyonu (icerik cek/screenshot — kullaniciya GORUNMEZ). parametreler: action, url, script',
235
235
  '- computer_use: GUI otomasyonu. parametreler: action ("screenshot"/"click"/"type"/"keypress"/"scroll"), x, y, text, key',
236
236
  '- social_open: muzik/video/sosyal ac. parametreler: query, platform (spotify/youtube...)',
237
237
  '- macos_screenshot: ekran goruntusu al',
238
- '- bash ile de acabilirsin: "open -a WhatsApp", "open https://...", "start chrome https://..."',
238
+ '\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
239
  '\nTum arac listesi (isimle cagir; parametre yanlissa <tool_results> duzeltir): ' + allNames.join(', '),
240
240
  ].join('\n');
241
241
  }
@@ -279,11 +279,14 @@ async function workflow(params) {
279
279
 
280
280
  async function callModel(msgs) {
281
281
  if (streamOn) {
282
- const sani = makeSanitizeStream(botName, t => process.stdout.write(t));
282
+ let cleared = false;
283
+ const clearThinking = () => { if (!cleared) { process.stdout.write('\r\x1b[K'); cleared = true; } };
284
+ process.stdout.write('\x1b[2m 💭 düşünüyor…\x1b[0m');
285
+ const sani = makeSanitizeStream(botName, t => { clearThinking(); process.stdout.write(t); });
283
286
  const filter = makeStreamFilter(t => sani.push(t), null);
284
287
  const body = { model, stream: true, messages: msgs, temperature: 0.3, max_tokens: 16000 };
285
288
  const out = await apiCallStream(providerUrl, providerApiKey, body, d => filter.push(d));
286
- filter.end(); sani.end();
289
+ filter.end(); sani.end(); clearThinking();
287
290
  return { content: out.content || '', toolCalls: out.toolCalls || [] };
288
291
  }
289
292
  const body = { model, stream: false, messages: msgs, temperature: 0.3, max_tokens: 16000 };
@@ -292,10 +295,28 @@ async function workflow(params) {
292
295
  return { content: msg.content || '', toolCalls: msg.tool_calls || [] };
293
296
  }
294
297
 
298
+ // Araç aktivitesi gösterimi (TTY streaming): her araç icin "🔧 label · hint ✓/✗"
299
+ 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ü' };
300
+ function briefHint(args) {
301
+ if (!args || typeof args !== 'object') return '';
302
+ const v = args.appName || args.query || args.name || args.url || args.command || args.pattern || args.path || args.action;
303
+ return v ? String(v).replace(/\s+/g, ' ').slice(0, 46) : '';
304
+ }
305
+ const onEvent = streamOn ? (ev) => {
306
+ if (ev.phase === 'start') {
307
+ const label = TOOL_LABEL[ev.tool] || ev.tool;
308
+ const hint = briefHint(ev.args);
309
+ process.stdout.write('\n\x1b[2m 🔧 ' + label + (hint ? ' · ' + hint : '') + '\x1b[0m');
310
+ } else {
311
+ const rec = (ev.records || [])[0] || {};
312
+ process.stdout.write(rec.status === 'done' ? ' \x1b[32m✓\x1b[0m' : ' \x1b[31m✗\x1b[0m');
313
+ }
314
+ } : null;
315
+
295
316
  try {
296
317
  const { records, reply } = await runAgentic({
297
318
  callModel, systemPrompt: sysMsg, historyMessages, task,
298
- toolsDir: __dirname, execFull, maxIterations: 15,
319
+ toolsDir: __dirname, execFull, onEvent, maxIterations: 15,
299
320
  });
300
321
  const fileWrites = records.filter(r => r.tool === 'write_file' && r.status === 'done');
301
322
  let finalReply = reply || '';