natureco-cli 5.29.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,25 @@
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
+
15
+ ## [5.30.0] - 2026-07-04 — "TAM KONTROL MODU (sahip opt-in: tüm araç+skill + computer-use)"
16
+
17
+ ### ✨ Yeni
18
+ - **Tam kontrol modu** (`natureco config set agentExec full` veya `NATURECO_AGENT_EXEC=full`): sahibi opt-in yapinca ajan **TUM araclara + skill'lere** erisir ve **her shell komutunu** calistirabilir (yikici komutlar — rm -rf — tam modda bile bloklu kalir). Computer-use araclari: `mac_app_open` (WhatsApp/Chrome/Spotify ac), `browser` (Playwright otomasyonu), `computer_use` (GUI: tikla/yaz/ekran goruntusu), `social_open` (muzik/video), `macos_screenshot`. Full modda sistem prompt'u bu araclari modele tanitir ("yapamam deme, ilgili araci cagir").
19
+ - Safe modda (varsayilan) `open`/`start`/`xdg-open` (uygulama/URL ac) da izinli — asistan temel acma islerini yapar.
20
+ - **Iki kademeli guvenlik**: Safe (varsayilan, milyonlar) = dosya araclari + guvenli shell. Full (sahip opt-in) = her sey. Katmanli: yikici-komut guard her modda aktif.
21
+
22
+ Doğrulama: full modda `browser` araci E2E (example.com → "Example Domain"); **492 test yeşil**, ESLint temiz.
23
+
5
24
  ## [5.29.0] - 2026-07-04 — "ask KUTU-ÇIKIŞI DÜZELTMESİ + çekirdek komut denetimi"
6
25
 
7
26
  ### 🐛 Düzeltme
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.29.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
 
@@ -62,13 +62,18 @@ const AGENT_EXEC_ALLOW = new Set([
62
62
  'ls', 'dir', 'cat', 'type', 'head', 'tail', 'pwd', 'echo', 'printf', 'grep', 'findstr',
63
63
  'find', 'wc', 'which', 'where', 'whereis', 'tree', 'stat', 'file', 'sort', 'uniq', 'diff',
64
64
  'mkdir', 'touch', 'cp', 'copy', 'mv', 'move', 'git', 'jq', 'sed', 'awk', 'date', 'env', 'whoami',
65
+ 'open', 'start', 'xdg-open', 'explorer', // uygulama/URL/dosya ac (dusuk risk, asistan icin gerekli)
65
66
  ]);
66
67
  const AGENT_EXEC_BLOCK_PATTERNS = [
67
68
  /\bnpm\s+publish\b/i, /\byarn\s+publish\b/i, /\bpnpm\s+publish\b/i,
68
69
  /\bgit\s+push\b/i, /\bgit\s+remote\s+(add|set-url)\b/i,
69
70
  /\bnpm\s+(un)?deprecate\b/i, /\bnpm\s+owner\b/i,
70
71
  ];
71
- function agentExecAllowed(command) {
72
+ // Full modda ("agentExec: full") acilan computer-use araclari: tarayici otomasyonu,
73
+ // uygulama ac/kapat, GUI (tikla/yaz/ekran goruntusu). Safe modda (varsayilan) KAPALI.
74
+ const FULL_TOOLS = ['browser', 'browser_use', 'mac_app_open', 'mac_app_quit', 'social_open', 'computer_use', 'macos_screenshot'];
75
+ function agentExecAllowed(command, opts) {
76
+ if (opts && opts.full) return true;
72
77
  if (String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full') return true;
73
78
  const cmd = String(command || '').trim();
74
79
  if (!cmd) return false;
@@ -262,9 +267,10 @@ async function executeCall(call, opts = {}) {
262
267
  return { records, feedback: feedbacks.join('\n') };
263
268
  }
264
269
 
265
- if (!allowed.has(norm)) {
266
- records.push({ tool: rawTool, status: 'error', error: 'Bu modda kullanilamayan arac: ' + rawTool });
267
- return { records, feedback: `${rawTool}: bu arac bu modda kullanilamaz (izin verilenler: ${[...allowed].join(', ')})` };
270
+ // Full modda (sahibin opt-in'i) TUM araclar acik; safe modda sadece allowlist.
271
+ if (!opts.execFull && !allowed.has(norm)) {
272
+ records.push({ tool: rawTool, status: 'error', error: 'Guvenli modda kapali arac: ' + rawTool });
273
+ return { records, feedback: `${rawTool}: guvenli modda kapali. Tum arac+skill'ler icin tam mod: "natureco config set agentExec full" (veya NATURECO_AGENT_EXEC=full). Guvenli modda acik: ${[...allowed].join(', ')}` };
268
274
  }
269
275
 
270
276
  const args = { ...(call.args || {}) };
@@ -285,10 +291,10 @@ async function executeCall(call, opts = {}) {
285
291
  records.push({ tool: 'bash', status: 'error', args: { command: cmd }, error: 'Yikici/tehlikeli komut ajan modunda engellendi' });
286
292
  return { records, feedback: `bash: "${cmd.slice(0, 80)}" yikici/tehlikeli goruldugu icin ajan tarafindan CALISTIRILMADI. Gerekirse kullanici komutu kendisi calistirabilir.` };
287
293
  }
288
- const execOk = opts.agentExecAllowed || agentExecAllowed;
294
+ const execOk = opts.agentExecAllowed || ((c) => agentExecAllowed(c, { full: opts.execFull }));
289
295
  if (!execOk(cmd)) {
290
- records.push({ tool: 'bash', status: 'error', args: { command: cmd }, error: 'Ajan exec politikasi disinda' });
291
- return { records, feedback: `bash: "${cmd.slice(0, 80)}" ajan guvenlik politikasi disinda (ag/yayin/sistem komutu olabilir). Otomatik CALISTIRILMADI. Kullanici manuel calistirabilir; tum komutlara izin: NATURECO_AGENT_EXEC=full.` };
296
+ records.push({ tool: 'bash', status: 'error', args: { command: cmd }, error: 'Guvenli mod exec politikasi disinda' });
297
+ return { records, feedback: `bash: "${cmd.slice(0, 80)}" guvenli mod politikasi disinda (ag/yayin/sistem). Tam kontrol icin: "natureco config set agentExec full" (veya NATURECO_AGENT_EXEC=full). Yikici komutlar (rm -rf) tam modda bile bloklu.` };
292
298
  }
293
299
  }
294
300
 
@@ -320,12 +326,12 @@ async function executeCall(call, opts = {}) {
320
326
  * callModel(messages) => Promise<{ content, toolCalls }>
321
327
  * Donus: { records, reply, iterations }
322
328
  */
323
- async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, maxIterations = 15 }) {
329
+ async function runAgentic({ callModel, systemPrompt, historyMessages, task, toolsDir, loadTool, allowed, execFull, onEvent, maxIterations = 15 }) {
324
330
  const messages = [{ role: 'system', content: systemPrompt }];
325
331
  for (const mm of historyMessages || []) messages.push({ role: mm.role, content: mm.content || '' });
326
332
  messages.push({ role: 'user', content: task });
327
333
 
328
- const allowedSet = allowed ? new Set(allowed) : new Set(DEFAULT_ALLOWED);
334
+ const allowedSet = allowed ? new Set(allowed) : new Set([...DEFAULT_ALLOWED, ...(execFull ? FULL_TOOLS : [])]);
329
335
  const allRecords = [];
330
336
  let finalReply = '';
331
337
  let iterations = 0;
@@ -343,7 +349,9 @@ async function runAgentic({ callModel, systemPrompt, historyMessages, task, tool
343
349
  messages.push({ role: 'assistant', content: content || '' });
344
350
  const feedbacks = [];
345
351
  for (const call of calls) {
346
- const { records, feedback } = await executeCall(call, { toolsDir, loadTool, allowed: allowedSet });
352
+ if (onEvent) { try { onEvent({ phase: 'start', tool: call.tool, args: call.args }); } catch {} }
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 {} }
347
355
  allRecords.push(...records);
348
356
  feedbacks.push(feedback);
349
357
  }
@@ -222,6 +222,23 @@ 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
+ // Tam mod (sahibin opt-in'i): tum arac+skill'ler acilir, keyfi shell (yikici haric).
226
+ const execFull = cfg.agentExec === 'full' || cfg.computerUse === true || String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full';
227
+ let fullToolsBlock = '';
228
+ if (execFull) {
229
+ const allNames = tools.filter(t => t !== 'workflow');
230
+ fullToolsBlock = [
231
+ '\n\nTAM MOD ACIK — su araclara da ERISIMIN VAR; o an ne gerekiyorsa dogrudan cagir:',
232
+ '- mac_app_open: macOS uygulamasi ac. parametre: appName (orn. "WhatsApp", "Google Chrome", "Spotify")',
233
+ '- mac_app_quit: macOS uygulamasi kapat. parametre: appName',
234
+ '- browser: HEADLESS tarayici otomasyonu (icerik cek/screenshot — kullaniciya GORUNMEZ). parametreler: action, url, script',
235
+ '- computer_use: GUI otomasyonu. parametreler: action ("screenshot"/"click"/"type"/"keypress"/"scroll"), x, y, text, key',
236
+ '- social_open: muzik/video/sosyal ac. parametreler: query, platform (spotify/youtube...)',
237
+ '- macos_screenshot: ekran goruntusu al',
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
+ '\nTum arac listesi (isimle cagir; parametre yanlissa <tool_results> duzeltir): ' + allNames.join(', '),
240
+ ].join('\n');
241
+ }
225
242
  const sysMsg = [
226
243
  'Sen NatureCo adli, arac kullanabilen bir yapay zeka ajanisin. Kullanicinin istegini SADECE anlatarak degil, ARACLARI cagirarak fiilen gerceklestir.',
227
244
  memCtx ? '\nKullanici bilgisi:\n' + memCtx : '',
@@ -244,6 +261,8 @@ async function workflow(params) {
244
261
  '- Goreceli yol yerine TAM yol kullan.',
245
262
  '- Arac sonuclari <tool_results> icinde geri gelir; gorev bitince ARAC CAGIRMADAN tek cumlelik ozet yaz.',
246
263
  '- Basit sohbet/selamlasma ise arac cagirma, dogrudan kisa yanit ver.',
264
+ execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
265
+ fullToolsBlock,
247
266
  skillsIndexBlock ? '\n\n' + skillsIndexBlock : '',
248
267
  ].filter(Boolean).join('\n');
249
268
 
@@ -260,11 +279,14 @@ async function workflow(params) {
260
279
 
261
280
  async function callModel(msgs) {
262
281
  if (streamOn) {
263
- 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); });
264
286
  const filter = makeStreamFilter(t => sani.push(t), null);
265
287
  const body = { model, stream: true, messages: msgs, temperature: 0.3, max_tokens: 16000 };
266
288
  const out = await apiCallStream(providerUrl, providerApiKey, body, d => filter.push(d));
267
- filter.end(); sani.end();
289
+ filter.end(); sani.end(); clearThinking();
268
290
  return { content: out.content || '', toolCalls: out.toolCalls || [] };
269
291
  }
270
292
  const body = { model, stream: false, messages: msgs, temperature: 0.3, max_tokens: 16000 };
@@ -273,10 +295,28 @@ async function workflow(params) {
273
295
  return { content: msg.content || '', toolCalls: msg.tool_calls || [] };
274
296
  }
275
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
+
276
316
  try {
277
317
  const { records, reply } = await runAgentic({
278
318
  callModel, systemPrompt: sysMsg, historyMessages, task,
279
- toolsDir: __dirname, maxIterations: 15,
319
+ toolsDir: __dirname, execFull, onEvent, maxIterations: 15,
280
320
  });
281
321
  const fileWrites = records.filter(r => r.tool === 'write_file' && r.status === 'done');
282
322
  let finalReply = reply || '';