natureco-cli 2.19.1 → 2.20.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "2.19.1",
3
+ "version": "2.20.0",
4
4
  "description": "NatureCo AI Bot Terminal Interface",
5
5
  "main": "bin/natureco.js",
6
6
  "bin": {
@@ -2,6 +2,7 @@ const path = require('path');
2
2
  const os = require('os');
3
3
  const fs = require('fs');
4
4
  const readline = require('readline');
5
+ const { execSync } = require('child_process');
5
6
  const inquirer = require('inquirer');
6
7
  const chalk = require('chalk');
7
8
  const { getApiKey, getConfig } = require('../utils/config');
@@ -37,6 +38,109 @@ function stopSpinner(timer, label, success = true) {
37
38
  process.stdout.write(`\r ${success ? chalk.green('✓') : chalk.red('✗')} ${chalk.cyan(label)}\n`);
38
39
  }
39
40
 
41
+ // ── Proje indexing ────────────────────────────────────────────────────────────
42
+ const IGNORE_DIRS = new Set(['node_modules', '.git', 'dist', 'build', '.next', '__pycache__', '.venv', 'venv', 'target', '.wrangler']);
43
+
44
+ function scanDir(dir, maxDepth, depth = 0) {
45
+ const results = [];
46
+ if (depth > maxDepth) return results;
47
+ let entries;
48
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return results; }
49
+ for (const entry of entries) {
50
+ if (entry.name.startsWith('.') && depth > 0) continue;
51
+ if (IGNORE_DIRS.has(entry.name)) continue;
52
+ const rel = depth === 0 ? entry.name : path.join(path.relative(dir.split(path.sep).slice(0, -depth).join(path.sep) || dir, dir), entry.name).replace(/\\/g, '/');
53
+ if (entry.isDirectory()) {
54
+ const sub = scanDir(path.join(dir, entry.name), maxDepth, depth + 1);
55
+ results.push(...sub.map(f => entry.name + '/' + f));
56
+ } else {
57
+ results.push(entry.name);
58
+ }
59
+ }
60
+ return results;
61
+ }
62
+
63
+ async function indexProject(projectDir) {
64
+ const index = {
65
+ dir: projectDir,
66
+ files: [],
67
+ type: 'unknown',
68
+ mainFiles: [],
69
+ packageJson: null,
70
+ gitBranch: null,
71
+ gitStatus: null,
72
+ };
73
+
74
+ // Dosyaları tara (max 2 seviye, ignore listesi hariç)
75
+ index.files = scanDir(projectDir, 2);
76
+
77
+ // Proje tipini tespit et
78
+ const fileSet = new Set(index.files);
79
+ if (fileSet.has('package.json')) {
80
+ try {
81
+ const pkg = JSON.parse(fs.readFileSync(path.join(projectDir, 'package.json'), 'utf8'));
82
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
83
+ index.packageJson = {
84
+ name: pkg.name || path.basename(projectDir),
85
+ version: pkg.version || '0.0.0',
86
+ scripts: pkg.scripts || {},
87
+ dependencies: Object.keys(pkg.dependencies || {}).slice(0, 15),
88
+ };
89
+ if (deps.react || deps['react-dom']) index.type = 'react';
90
+ else if (deps.next) index.type = 'nextjs';
91
+ else if (deps.vue) index.type = 'vue';
92
+ else if (deps.express || deps.fastify || deps.koa) index.type = 'node-server';
93
+ else index.type = 'node';
94
+ } catch {}
95
+ } else if (fileSet.has('requirements.txt') || index.files.some(f => f.endsWith('.py'))) {
96
+ index.type = 'python';
97
+ } else if (fileSet.has('Cargo.toml')) {
98
+ index.type = 'rust';
99
+ } else if (fileSet.has('go.mod')) {
100
+ index.type = 'go';
101
+ } else if (index.files.some(f => f.endsWith('.ts') || f.endsWith('.tsx'))) {
102
+ index.type = 'typescript';
103
+ }
104
+
105
+ // Ana dosyaları bul
106
+ const mainCandidates = [
107
+ 'index.js', 'index.ts', 'main.js', 'main.ts', 'app.js', 'app.ts',
108
+ 'server.js', 'server.ts', 'src/index.js', 'src/index.ts',
109
+ 'src/main.ts', 'src/App.tsx', 'src/app.tsx',
110
+ ];
111
+ index.mainFiles = mainCandidates.filter(f => fileSet.has(f));
112
+
113
+ // Git bilgisi
114
+ try {
115
+ index.gitBranch = execSync('git branch --show-current', { cwd: projectDir, stdio: 'pipe' }).toString().trim();
116
+ const statusRaw = execSync('git status --short', { cwd: projectDir, stdio: 'pipe' }).toString().trim();
117
+ index.gitStatus = statusRaw ? statusRaw.split('\n').slice(0, 5) : [];
118
+ } catch {}
119
+
120
+ return index;
121
+ }
122
+
123
+ function buildIndexPrompt(index) {
124
+ const lines = [
125
+ `Proje Bilgisi:`,
126
+ `- Tip: ${index.type.toUpperCase()}`,
127
+ `- Dizin: ${index.dir}`,
128
+ `- Dosyalar (${index.files.length} toplam): ${index.files.slice(0, 40).join(', ')}`,
129
+ `- Ana dosyalar: ${index.mainFiles.join(', ') || 'bulunamadı'}`,
130
+ ];
131
+ if (index.packageJson) {
132
+ lines.push(`- Paket: ${index.packageJson.name} v${index.packageJson.version}`);
133
+ const scripts = Object.keys(index.packageJson.scripts);
134
+ if (scripts.length) lines.push(`- Scripts: ${scripts.join(', ')}`);
135
+ if (index.packageJson.dependencies.length) lines.push(`- Bağımlılıklar: ${index.packageJson.dependencies.join(', ')}`);
136
+ }
137
+ if (index.gitBranch) {
138
+ lines.push(`- Git branch: ${index.gitBranch}`);
139
+ lines.push(`- Değişiklikler: ${index.gitStatus?.length ? index.gitStatus.join(', ') : 'temiz'}`);
140
+ }
141
+ return lines.join('\n');
142
+ }
143
+
40
144
  // ── Streaming fetch ───────────────────────────────────────────────────────────
41
145
  async function streamMessage(providerConfig, messages, tools) {
42
146
  const endpoint = `${providerConfig.url}/chat/completions`;
@@ -67,17 +171,15 @@ async function streamMessage(providerConfig, messages, tools) {
67
171
  const reader = response.body.getReader();
68
172
  const decoder = new TextDecoder();
69
173
  let fullText = '';
70
- let toolCallsBuffer = {}; // id -> { name, arguments }
174
+ let toolCallsBuffer = {};
71
175
 
72
176
  process.stdout.write('\n');
73
177
 
74
178
  while (true) {
75
179
  const { done, value } = await reader.read();
76
180
  if (done) break;
77
-
78
181
  const chunk = decoder.decode(value);
79
182
  const lines = chunk.split('\n').filter(l => l.startsWith('data: '));
80
-
81
183
  for (const line of lines) {
82
184
  const data = line.slice(6).trim();
83
185
  if (data === '[DONE]') continue;
@@ -85,20 +187,11 @@ async function streamMessage(providerConfig, messages, tools) {
85
187
  const json = JSON.parse(data);
86
188
  const delta = json.choices?.[0]?.delta;
87
189
  if (!delta) continue;
88
-
89
- // Text content
90
- if (delta.content) {
91
- process.stdout.write(delta.content);
92
- fullText += delta.content;
93
- }
94
-
95
- // Tool call deltas
190
+ if (delta.content) { process.stdout.write(delta.content); fullText += delta.content; }
96
191
  if (delta.tool_calls) {
97
192
  for (const tc of delta.tool_calls) {
98
193
  const idx = tc.index ?? 0;
99
- if (!toolCallsBuffer[idx]) {
100
- toolCallsBuffer[idx] = { id: tc.id || '', name: '', arguments: '' };
101
- }
194
+ if (!toolCallsBuffer[idx]) toolCallsBuffer[idx] = { id: tc.id || '', name: '', arguments: '' };
102
195
  if (tc.id) toolCallsBuffer[idx].id = tc.id;
103
196
  if (tc.function?.name) toolCallsBuffer[idx].name += tc.function.name;
104
197
  if (tc.function?.arguments) toolCallsBuffer[idx].arguments += tc.function.arguments;
@@ -121,12 +214,11 @@ async function streamMessage(providerConfig, messages, tools) {
121
214
  return { text: fullText, toolCalls };
122
215
  }
123
216
 
124
- // ── Tool execution with spinner + confirm ─────────────────────────────────────
217
+ // ── Tool execution ────────────────────────────────────────────────────────────
125
218
  const DANGEROUS = ['rm ', 'rmdir', 'delete', 'DROP', 'truncate'];
126
219
 
127
220
  async function runToolCall(toolCall, stats) {
128
221
  const inputPreview = JSON.stringify(toolCall.input).slice(0, 60);
129
- const label = `${toolCall.name} ${chalk.gray(inputPreview)}`;
130
222
 
131
223
  const needsConfirm =
132
224
  toolCall.name === 'write_file' ||
@@ -149,7 +241,11 @@ async function runToolCall(toolCall, stats) {
149
241
  stopSpinner(spinner, toolCall.name, result.success !== false);
150
242
 
151
243
  if (result.success !== false) {
152
- if (toolCall.name === 'write_file') stats.filesChanged++;
244
+ if (toolCall.name === 'write_file') {
245
+ stats.filesChanged++;
246
+ stats.changedFiles.push(toolCall.input.path || '?');
247
+ console.log(chalk.green(` ✓ ${toolCall.input.path} güncellendi`));
248
+ }
153
249
  if (toolCall.name === 'bash') stats.commandsRun++;
154
250
  stats.toolCallCount++;
155
251
  }
@@ -157,6 +253,40 @@ async function runToolCall(toolCall, stats) {
157
253
  return result;
158
254
  }
159
255
 
256
+ // ── Test runner ───────────────────────────────────────────────────────────────
257
+ async function runTests(projectIndex, conversationMessages, tools, providerConfig, displayBotName) {
258
+ const testScript = projectIndex.packageJson?.scripts?.test;
259
+ if (!testScript || testScript.includes('no test')) return;
260
+
261
+ const { confirm } = await inquirer.prompt([{
262
+ type: 'confirm', name: 'confirm',
263
+ message: chalk.yellow(' 🧪 Testleri çalıştıralım mı?'), default: true,
264
+ }]);
265
+ if (!confirm) return;
266
+
267
+ console.log(chalk.gray('\n npm test çalışıyor...\n'));
268
+ try {
269
+ const output = execSync('npm test', {
270
+ cwd: process.cwd(), timeout: 30000, stdio: 'pipe',
271
+ }).toString();
272
+ console.log(chalk.green(' ✅ Testler geçti!\n'));
273
+ } catch (err) {
274
+ const errOutput = (err.stdout?.toString() || err.message || '').slice(0, 500);
275
+ console.log(chalk.red(' ❌ Test hatası:\n'));
276
+ console.log(chalk.gray(' ' + errOutput.split('\n').join('\n ')));
277
+ console.log();
278
+
279
+ const { fix } = await inquirer.prompt([{
280
+ type: 'confirm', name: 'fix',
281
+ message: chalk.yellow(' Agent test hatasını düzeltsin mi?'), default: true,
282
+ }]);
283
+ if (fix) {
284
+ return `Test hatası oluştu:\n${errOutput}\nBu hatayı düzelt.`;
285
+ }
286
+ }
287
+ return null;
288
+ }
289
+
160
290
  // ── Main ──────────────────────────────────────────────────────────────────────
161
291
  async function code(targetFile, options = {}) {
162
292
  const apiKey = getApiKey();
@@ -202,25 +332,23 @@ async function code(targetFile, options = {}) {
202
332
 
203
333
  const shortModel = providerConfig.model.split('/').pop().split('-').slice(0, 3).join('-');
204
334
 
205
- // ── Proje context — dizini otomatik tara ────────────────────────────────────
206
- let projectContext = '';
207
- try {
208
- const dirResult = await executeTool('list_dir', { path: process.cwd() });
209
- if (dirResult.success !== false) {
210
- projectContext = `\nMevcut proje dizini: ${process.cwd()}\nDosyalar:\n${(dirResult.output || '').slice(0, 800)}`;
211
- }
212
- } catch {}
335
+ // ── Proje indexing ───────────────────────────────────────────────────────────
336
+ const indexSpinner = startSpinner('Proje indexleniyor...');
337
+ const projectIndex = await indexProject(process.cwd());
338
+ stopSpinner(indexSpinner, `${projectIndex.type.toUpperCase()} projesi — ${projectIndex.files.length} dosya`, true);
213
339
 
214
340
  // ── Sistem prompt ────────────────────────────────────────────────────────────
215
341
  const agentsPrompt = getAgentsPrompt();
216
342
  const memoryPrompt = getMemoryPrompt(bot.id);
343
+ const indexPrompt = buildIndexPrompt(projectIndex);
217
344
 
218
345
  let systemPrompt = `Sen NatureCo Code Agent'sın — güçlü bir AI coding asistanı.
219
346
  Kullanıcının projesinde gerçekten çalış: dosyaları oku, değiştir, komutları çalıştır.
220
347
  Her adımı Türkçe açıkla. Değişiklik yapmadan önce ne yapacağını söyle.
221
348
  Hataları yakala ve düzelt. İş bitince özet sun.
222
- Mevcut dizin: ${process.cwd()}
223
- Kullanıcı: ${userName}${projectContext}`;
349
+ Kullanıcı: ${userName}
350
+
351
+ ${indexPrompt}`;
224
352
 
225
353
  if (agentsPrompt) systemPrompt += `\n\n## Project Instructions\n${agentsPrompt}`;
226
354
  if (memoryPrompt) systemPrompt += '\n\n' + memoryPrompt;
@@ -238,12 +366,10 @@ Kullanıcı: ${userName}${projectContext}`;
238
366
  // ── Session ──────────────────────────────────────────────────────────────────
239
367
  const session = createSession(bot.id, bot.name);
240
368
  const conversationMessages = [{ role: 'system', content: systemPrompt }];
241
- const stats = { filesChanged: 0, commandsRun: 0, toolCallCount: 0, messageCount: 0 };
369
+ const stats = { filesChanged: 0, commandsRun: 0, toolCallCount: 0, messageCount: 0, changedFiles: [] };
242
370
 
243
- // ── MCP sunucularını başlat ──────────────────────────────────────────────────
244
371
  await startMcpServers().catch(() => {});
245
372
 
246
- // ── Tool definitions ─────────────────────────────────────────────────────────
247
373
  const localTools = getToolDefinitions();
248
374
  const tools = localTools.map(t => ({
249
375
  type: 'function',
@@ -271,6 +397,11 @@ Kullanıcı: ${userName}${projectContext}`;
271
397
  console.log();
272
398
  console.log(centerText(chalk.cyan(`(\\_/) ${displayBotName} hazır`) + chalk.gray(` · v${version}`)));
273
399
  console.log(sep());
400
+ console.log(centerText(chalk.gray(`📁 ${projectIndex.type.toUpperCase()} projesi · ${projectIndex.files.length} dosya`)));
401
+ if (projectIndex.gitBranch) {
402
+ const changes = projectIndex.gitStatus?.length || 0;
403
+ console.log(centerText(chalk.gray(`🌿 ${projectIndex.gitBranch} · ${changes} değişiklik`)));
404
+ }
274
405
  if (targetFile) console.log(centerText(chalk.gray(`📄 ${targetFile}`)));
275
406
  console.log(centerText(chalk.gray(`${shortModel} · /help · Ctrl+C çıkış`)));
276
407
  console.log(sep());
@@ -278,11 +409,21 @@ Kullanıcı: ${userName}${projectContext}`;
278
409
 
279
410
  // ── Session özeti ─────────────────────────────────────────────────────────
280
411
  function showSummary() {
281
- console.log(chalk.gray('\n─── Agent Session Özeti ───'));
412
+ const w = process.stdout.columns || 120;
413
+ console.log(chalk.gray('\n' + '─'.repeat(w)));
414
+ console.log(chalk.gray(' Agent Session Özeti'));
415
+ console.log(chalk.gray('─'.repeat(w)));
282
416
  console.log(` ${chalk.green('✓')} ${stats.filesChanged} dosya değiştirildi`);
283
417
  console.log(` ${chalk.green('✓')} ${stats.commandsRun} komut çalıştırıldı`);
284
418
  console.log(` ${chalk.green('✓')} ${stats.toolCallCount} tool çağrısı yapıldı`);
285
- console.log(` ${chalk.cyan('◉')} ${stats.messageCount} mesaj\n`);
419
+ console.log(` ${chalk.cyan('◉')} ${stats.messageCount} mesaj`);
420
+ if (stats.changedFiles.length > 0) {
421
+ console.log();
422
+ console.log(chalk.cyan(` 📝 Değiştirilen dosyalar (${stats.changedFiles.length}):`));
423
+ stats.changedFiles.forEach(f => console.log(chalk.white(` ${f}`)));
424
+ }
425
+ console.log(chalk.gray('─'.repeat(w)));
426
+ console.log();
286
427
  }
287
428
 
288
429
  // ── Mesaj gönder + tool loop ──────────────────────────────────────────────
@@ -290,7 +431,6 @@ Kullanıcı: ${userName}${projectContext}`;
290
431
  userMessage = userMessage.trim();
291
432
  if (!userMessage) return;
292
433
 
293
- // Slash komutları
294
434
  if (userMessage.startsWith('/')) {
295
435
  const [cmd] = userMessage.slice(1).split(' ');
296
436
  switch (cmd.toLowerCase()) {
@@ -300,6 +440,7 @@ Kullanıcı: ${userName}${projectContext}`;
300
440
  ['/clear', 'Ekranı temizle'],
301
441
  ['/summary', 'Session özetini göster'],
302
442
  ['/done', 'Bitir ve özet göster'],
443
+ ['/index', 'Projeyi yeniden indexle'],
303
444
  ['/help', 'Bu yardım'],
304
445
  ].forEach(([c, d]) => console.log(' ' + chalk.cyan(c.padEnd(12)) + chalk.gray(d)));
305
446
  console.log(chalk.gray(' Ctrl+C'.padEnd(14) + 'Çıkış'));
@@ -313,6 +454,17 @@ Kullanıcı: ${userName}${projectContext}`;
313
454
  showSummary();
314
455
  if (cmd === 'done') process.exit(0);
315
456
  return;
457
+ case 'index': {
458
+ const sp = startSpinner('Proje yeniden indexleniyor...');
459
+ const newIndex = await indexProject(process.cwd());
460
+ stopSpinner(sp, `${newIndex.files.length} dosya indexlendi`, true);
461
+ // Sistem prompt'u güncelle
462
+ conversationMessages[0].content = conversationMessages[0].content.replace(
463
+ /Proje Bilgisi:[\s\S]*?(?=\n\n|$)/,
464
+ buildIndexPrompt(newIndex)
465
+ );
466
+ return;
467
+ }
316
468
  default:
317
469
  console.log(chalk.red(`Bilinmeyen komut: /${cmd}\n`));
318
470
  return;
@@ -326,10 +478,10 @@ Kullanıcı: ${userName}${projectContext}`;
326
478
 
327
479
  stats.messageCount++;
328
480
  console.log(chalk.white('You ') + userMessage);
329
-
330
- // Mesajı history'e ekle
331
481
  conversationMessages.push({ role: 'user', content: userMessage });
332
482
 
483
+ const prevFilesChanged = stats.filesChanged;
484
+
333
485
  // ── Streaming + tool loop (max 20 iter) ──────────────────────────────────
334
486
  let iter = 0;
335
487
  while (iter < 20) {
@@ -341,11 +493,10 @@ Kullanıcı: ${userName}${projectContext}`;
341
493
  streamResult = await streamMessage(providerConfig, conversationMessages, tools);
342
494
  } catch (err) {
343
495
  console.log(chalk.red(`\nError: ${err.message}\n`));
344
- conversationMessages.pop(); // başarısız user mesajını geri al
496
+ conversationMessages.pop();
345
497
  return;
346
498
  }
347
499
 
348
- // Assistant mesajını history'e ekle
349
500
  const assistantMsg = { role: 'assistant', content: streamResult.text || '' };
350
501
  if (streamResult.toolCalls?.length) {
351
502
  assistantMsg.tool_calls = streamResult.toolCalls.map(tc => ({
@@ -356,10 +507,8 @@ Kullanıcı: ${userName}${projectContext}`;
356
507
  }
357
508
  conversationMessages.push(assistantMsg);
358
509
 
359
- // Tool call yoksa bitti
360
510
  if (!streamResult.toolCalls?.length) break;
361
511
 
362
- // Tool call'ları çalıştır
363
512
  console.log(chalk.yellow(`\n🔧 ${streamResult.toolCalls.length} tool çalıştırılıyor...\n`));
364
513
 
365
514
  for (const toolCall of streamResult.toolCalls) {
@@ -385,6 +534,15 @@ Kullanıcı: ${userName}${projectContext}`;
385
534
  addToHistory(bot.id, userMessage, lastAssistant.content, null);
386
535
  addMessageToSession(bot.id, session.id, userMessage, lastAssistant.content);
387
536
  }
537
+
538
+ // ── Test runner — dosya değiştiyse sor ───────────────────────────────────
539
+ if (stats.filesChanged > prevFilesChanged && projectIndex.packageJson?.scripts?.test) {
540
+ const fixPrompt = await runTests(projectIndex, conversationMessages, tools, providerConfig, displayBotName);
541
+ if (fixPrompt) {
542
+ // Test hatasını agent'a gönder
543
+ await handleMessage(fixPrompt);
544
+ }
545
+ }
388
546
  }
389
547
 
390
548
  // ── Input loop ───────────────────────────────────────────────────────────────
@@ -24,46 +24,61 @@ async function memoryCmd(args) {
24
24
 
25
25
  function statusMemory() {
26
26
  const config = getConfig();
27
+ const w = process.stdout.columns || 120;
28
+ const line = chalk.gray('─'.repeat(w));
27
29
 
28
30
  console.log('');
29
- console.log(chalk.gray(' ' + '─'.repeat(48)));
30
- console.log(chalk.cyan.bold('\n Hafıza Durumu\n'));
31
+ console.log(line);
32
+ console.log(chalk.bold.cyan(' Hafıza Durumu'));
33
+ console.log(line);
31
34
 
32
35
  if (!fs.existsSync(MEMORY_DIR)) {
33
- console.log(chalk.gray(' Hafıza dizini bulunamadı.\n'));
36
+ console.log(chalk.gray('\n Hafıza dizini bulunamadı.\n'));
37
+ console.log(line);
34
38
  return;
35
39
  }
36
40
 
37
41
  const files = fs.readdirSync(MEMORY_DIR).filter(f => f.endsWith('.json'));
38
42
 
39
43
  if (files.length === 0) {
40
- console.log(chalk.gray(' Kayıtlı hafıza yok.\n'));
44
+ console.log(chalk.gray('\n Kayıtlı hafıza yok.\n'));
45
+ console.log(line);
41
46
  return;
42
47
  }
43
48
 
44
49
  let totalFacts = 0;
50
+ const activeBotName = config.botName || '';
51
+
52
+ console.log('');
45
53
  files.forEach(file => {
46
54
  const botId = file.replace('.json', '');
47
55
  try {
48
56
  const mem = loadMemory(botId);
49
57
  const factCount = (mem.facts || []).length;
58
+ const prefCount = (mem.preferences || []).length;
50
59
  totalFacts += factCount;
51
- const active = config.botName && (mem.botName === config.botName) ? chalk.green(' ← aktif') : '';
52
- console.log(chalk.white(` ${mem.botName || botId}`) + active);
53
- if (mem.name) console.log(chalk.gray(` Kullanıcı : ${mem.name}`));
54
- if (mem.nickname) console.log(chalk.gray(` Lakap : ${mem.nickname}`));
55
- console.log(chalk.gray(` Bilgi : ${factCount} kayıt`));
60
+ const isActive = activeBotName && (mem.botName === activeBotName);
61
+ const bar = isActive ? chalk.cyan('▌') : chalk.gray('▌');
62
+
63
+ console.log(bar + ' ' + chalk.bold.white(mem.botName || botId) + (isActive ? chalk.cyan(' ← aktif') : ''));
64
+ console.log(bar + ' ' + chalk.gray('Kullanıcı : ') + chalk.white(mem.name || '—'));
65
+ if (mem.nickname) {
66
+ console.log(bar + ' ' + chalk.gray('Lakap : ') + chalk.white(mem.nickname));
67
+ }
68
+ console.log(bar + ' ' + chalk.gray('Bilgi : ') + chalk.white(`${factCount} kayıt`));
69
+ console.log(bar + ' ' + chalk.gray('Tercihler : ') + chalk.white(`${prefCount} kayıt`));
56
70
  if (mem.lastSeen) {
57
71
  const d = new Date(mem.lastSeen);
58
- console.log(chalk.gray(` Son görüş : ${d.toLocaleDateString('tr-TR')}`));
72
+ console.log(bar + ' ' + chalk.gray('Son görüş : ') + chalk.white(d.toLocaleDateString('tr-TR')));
59
73
  }
60
74
  console.log('');
61
75
  } catch {}
62
76
  });
63
77
 
64
- console.log(chalk.gray(' ' + '─'.repeat(48)));
65
- console.log(chalk.gray(` ${files.length} bot · ${totalFacts} toplam bilgi`));
66
- console.log(chalk.gray(' Aramak için: ') + chalk.cyan('natureco memory search <sorgu>\n'));
78
+ console.log(line);
79
+ console.log(chalk.gray(` ${files.length} bot · ${totalFacts} toplam bilgi · natureco memory search <sorgu>`));
80
+ console.log(line);
81
+ console.log('');
67
82
  }
68
83
 
69
84
  function listMemory() {