natureco-cli 2.17.1 → 2.17.4

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.
@@ -3,13 +3,12 @@ const os = require('os');
3
3
  const fs = require('fs');
4
4
  const inquirer = require('inquirer');
5
5
  const chalk = require('chalk');
6
- const readline = require('readline');
7
- const { getApiKey } = require('../utils/config');
6
+ const { getApiKey, getConfig } = require('../utils/config');
8
7
  const { getBots, sendMessage, _sendMessage } = require('../utils/api');
9
8
  const { getSkillPrompts, getSkills } = require('../utils/skills');
10
9
  const { getAgentsPrompt } = require('../utils/agents');
11
- const { addToHistory, getCommandHistory, clearHistory } = require('../utils/history');
12
- const { getMemoryPrompt, extractMemoryFromMessage, loadMemory, clearMemory } = require('../utils/memory');
10
+ const { addToHistory, getCommandHistory } = require('../utils/history');
11
+ const { getMemoryPrompt, extractMemoryFromMessage, loadMemory, clearMemory, addMemoryEntry } = require('../utils/memory');
13
12
  const { getCommands, getCommandContent } = require('../utils/commands');
14
13
  const { runHooks } = require('../utils/hooks');
15
14
  const { createSession, loadSession, getLatestSession, addMessageToSession } = require('../utils/sessions');
@@ -17,745 +16,451 @@ const { addBackgroundTask, updateBackgroundTask } = require('../utils/background
17
16
  const { getToolDefinitions, executeToolCalls } = require('../utils/tool-runner');
18
17
  const { extractToolCalls } = require('../utils/tool-adapter');
19
18
 
19
+ // ── ASCII Logo ────────────────────────────────────────────────────────────────
20
+ const ASCII_LOGO = [
21
+ '{green-fg}███╗ ██╗ █████╗ ████████╗██╗ ██╗██████╗ ███████╗ ██████╗ ██████╗{/}',
22
+ '{green-fg}████╗ ██║██╔══██╗╚══██╔══╝██║ ██║██╔══██╗██╔════╝██╔════╝ ██╔═══██╗{/}',
23
+ '{green-fg}██╔██╗ ██║███████║ ██║ ██║ ██║██████╔╝█████╗ ██║ ██║ ██║{/}',
24
+ '{green-fg}██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗██╔══╝ ██║ ██║ ██║{/}',
25
+ '{green-fg}██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████╗╚██████╗ ╚██████╔╝{/}',
26
+ '{gray-fg}╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝{/}',
27
+ ].join('\n');
28
+
29
+ // ── What's New ────────────────────────────────────────────────────────────────
30
+ const CHANGELOG = [
31
+ 'Eklendi: Blessed TUI — 4 bölgeli tam ekran arayüz',
32
+ 'Eklendi: agents, plugins, pairing, uninstall komutları',
33
+ 'Eklendi: channels, models, memory, logs, status, security, reset',
34
+ 'Düzeltildi: Telegram/WhatsApp cevap vermeme sorunu',
35
+ 'Düzeltildi: Token optimizasyonu — sistem prompt sıkıştırıldı',
36
+ ];
37
+
20
38
  async function chat(botName, options = {}) {
21
39
  const apiKey = getApiKey();
40
+ const config = getConfig();
41
+ const version = require('../../package.json').version;
22
42
 
23
- if (!apiKey) {
24
- console.log(chalk.red('\n❌ Not logged in. Run "natureco login" first.\n'));
25
- process.exit(1);
26
- }
27
-
28
- console.log(chalk.yellow('\n⏳ Loading bots...\n'));
29
-
43
+ // ── Bot seçimi ──────────────────────────────────────────────────────────────
30
44
  let botList;
31
45
  try {
32
- botList = await getBots(apiKey);
46
+ botList = await getBots(apiKey || config.providerApiKey || '');
33
47
  } catch (err) {
34
48
  console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
35
49
  process.exit(1);
36
50
  }
37
51
 
38
- if (!botList || !botList.bots || botList.bots.length === 0) {
52
+ if (!botList?.bots?.length) {
39
53
  console.log(chalk.gray('No bots found. Create one at https://developers.natureco.me\n'));
40
54
  process.exit(1);
41
55
  }
42
56
 
43
- // Bot adı verilmediyse config'den defaultBot'u kullan veya interaktif seçim sun
44
57
  let bot;
45
58
  if (!botName) {
46
- const { getConfig } = require('../utils/config');
47
- const config = getConfig();
48
-
49
- if (config.defaultBot) {
50
- bot = botList.bots.find(b => b.name.toLowerCase() === config.defaultBot.toLowerCase());
51
- if (!bot && config.defaultBotId) {
52
- bot = botList.bots.find(b => b.id === config.defaultBotId);
53
- }
59
+ if (config.botName) {
60
+ bot = botList.bots.find(b => b.name && b.name.toLowerCase() === config.botName.toLowerCase());
54
61
  }
55
-
56
62
  if (!bot) {
57
- // Interaktif bot seçimi
58
- console.log(chalk.yellow('Varsayılan bot bulunamadı. Lütfen bir bot seçin:\n'));
59
- botList.bots.forEach((b, i) => {
60
- console.log(chalk.cyan(` ${i + 1}. ${b.name}`));
61
- });
62
- console.log('');
63
-
64
63
  process.stdin.resume();
65
- const { selectedBot } = await inquirer.prompt([
66
- {
67
- type: 'list',
68
- name: 'selectedBot',
69
- message: 'Bot seçin:',
70
- choices: botList.bots.map((b, i) => ({ name: b.name, value: b.id })),
71
- },
72
- ]);
73
-
64
+ const { selectedBot } = await inquirer.prompt([{
65
+ type: 'list',
66
+ name: 'selectedBot',
67
+ message: 'Bot seçin:',
68
+ choices: botList.bots.map(b => ({ name: b.name, value: b.id })),
69
+ }]);
74
70
  bot = botList.bots.find(b => b.id === selectedBot);
75
71
  }
76
72
  } else {
77
- // Bot adı ile eşleşen botu bul
78
73
  bot = botList.bots.find(b => b.name && botName && b.name.toLowerCase() === botName.toLowerCase());
79
-
80
74
  if (!bot) {
81
75
  console.log(chalk.red(`\n❌ Bot "${botName}" not found.\n`));
82
- console.log(chalk.gray('Available bots:'));
83
- botList.bots.forEach(b => console.log(chalk.cyan(` - ${b.name}`)));
84
- console.log('');
85
76
  process.exit(1);
86
77
  }
87
78
  }
88
79
 
89
- // Startup animation
90
- console.clear();
91
-
92
- // ASCII art rabbit
93
- console.log('');
94
- console.log(chalk.cyan(' (\\\_/)'));
95
- console.log(chalk.cyan(' (•ᴥ•)'));
96
- console.log(chalk.cyan(' />🌿'));
97
- console.log('');
98
-
99
- // Helper function for progress bar
100
- function progressBar(percent, width = 20) {
101
- const filled = Math.round(width * percent);
102
- return chalk.green('█'.repeat(filled)) + chalk.gray('░'.repeat(width - filled));
103
- }
104
-
105
- // Helper function to check gateway status
106
- function checkGateway() {
107
- try {
108
- const fs2 = require('fs');
109
- const gatewayPidFile = path.join(os.homedir(), '.natureco', 'gateway.pid');
110
- if (fs2.existsSync(gatewayPidFile)) {
111
- const pid = parseInt(fs2.readFileSync(gatewayPidFile, 'utf8'));
112
- process.kill(pid, 0);
113
- return true;
114
- }
115
- } catch {
116
- return false;
117
- }
118
- return false;
119
- }
120
-
121
- // Startup steps with animation
122
- const steps = [
123
- { label: 'Memory', fn: () => loadMemory(bot.id) },
124
- { label: 'Skills', fn: () => getSkillPrompts() },
125
- { label: 'Gateway', fn: () => checkGateway() },
126
- ];
127
-
128
- // Execute steps with progress animation
129
- const results = {};
130
- for (let i = 0; i < steps.length; i++) {
131
- const step = steps[i];
132
- const percent = (i + 1) / steps.length;
133
-
134
- process.stdout.write(`[${progressBar(percent)}] ${step.label}...`);
135
-
136
- // Execute step
137
- const result = step.fn();
138
- results[step.label.toLowerCase()] = result;
139
-
140
- // Show result
141
- let info = '';
142
- if (step.label === 'Memory' && result) {
143
- const factCount = (result.facts || []).length;
144
- info = factCount > 0 ? ` · ${factCount} facts` : '';
145
- } else if (step.label === 'Skills' && result) {
146
- const skillCount = getSkills().length;
147
- info = skillCount > 0 ? ` · ${skillCount} skills` : '';
148
- } else if (step.label === 'Gateway') {
149
- info = result ? ' · running' : ' · offline';
150
- }
151
-
152
- process.stdout.write(`\r[${progressBar(percent)}] ${step.label} loaded${info}\n`);
153
-
154
- // Wait 300ms
155
- await new Promise(resolve => setTimeout(resolve, 300));
156
- }
157
-
158
- console.log('');
159
-
160
- // Wait a bit before clearing
161
- await new Promise(resolve => setTimeout(resolve, 500));
162
-
163
- // Chat başlat
164
- console.clear();
165
-
166
- // Get botName from memory and bot
80
+ // ── Hafıza & sistem prompt ──────────────────────────────────────────────────
167
81
  const mem = loadMemory(bot.id);
168
82
  const displayBotName = mem.botName || bot.name || 'NatureCo';
169
-
170
- // Get provider model (short name)
171
- const { getConfig } = require('../utils/config');
172
- const config = getConfig();
83
+ const userName = mem.name || config.userName || 'User';
173
84
  const providerModel = config.providerModel || 'unknown';
174
- const shortModel = providerModel.split('/').pop().split('-').slice(0, 2).join('-'); // e.g., "llama-3.1" from "llama-3.1-8b-instant"
175
-
176
- // Get terminal width
177
- const terminalWidth = process.stdout.columns || 80;
178
- const separator = '─'.repeat(terminalWidth);
179
-
180
- // Rabbit ASCII art with fixed unicode
181
- const rabbit = chalk.cyan(' (\\\_/)') + '\n' +
182
- chalk.cyan(' (•ᴥ•)') + '\n' +
183
- chalk.cyan(' />') + chalk.green('🌿');
184
-
185
- // Full UI header with rabbit
186
- console.log(rabbit);
187
- console.log('');
188
- console.log(chalk.gray(separator));
189
-
190
- // Line 1: NatureCo · botName · model · timezone
191
- const timezone = mem.facts?.find(f => {
192
- const val = typeof f === 'string' ? f : f.value;
193
- return val?.includes('Timezone:');
194
- });
195
- const tzStr = timezone ? (typeof timezone === 'string' ? timezone : timezone.value).replace('Timezone:', '').trim() : '';
196
- const line1 = chalk.white.bold('NatureCo') +
197
- chalk.gray(' · ') +
198
- chalk.cyan(displayBotName) +
199
- chalk.gray(' · ') +
200
- chalk.gray(shortModel) +
201
- (tzStr ? chalk.gray(' · ') + chalk.gray(tzStr) : '');
202
- console.log(line1);
203
-
204
- console.log(chalk.gray(separator));
205
-
206
- // Line 2: Session · Commands · Ctrl+C
207
- const line2 = chalk.gray('Session') +
208
- chalk.gray(' · ') +
209
- chalk.gray('/clear /bot /skills /memory /help') +
210
- chalk.gray(' · ') +
211
- chalk.gray('Ctrl+C to exit');
212
- console.log(line2);
213
-
214
- console.log(chalk.gray(separator));
215
-
216
- // Line 3: Memory info · Skills · Crons
217
- const userName = mem.name || 'User';
218
- const factCount = (mem.facts || []).length;
219
- const skillCount = getSkills().length;
220
-
221
- // Get active cron count with correct path
222
- let cronCount = 0;
223
- try {
224
- const fs2 = require('fs');
225
- const cronsFile = path.join(os.homedir(), '.natureco', 'crons.json');
226
- if (fs2.existsSync(cronsFile)) {
227
- const crons = JSON.parse(fs2.readFileSync(cronsFile, 'utf8'));
228
- cronCount = crons.filter(c => c.enabled !== false).length;
229
- }
230
- } catch (e) {
231
- cronCount = 0;
232
- }
233
-
234
- const memoryInfo = `Memory: ${userName}`;
235
- const line3 = chalk.gray(memoryInfo + (factCount > 0 ? ` · ${factCount} facts` : '')) +
236
- chalk.gray(' ') +
237
- chalk.gray(`Skills: ${skillCount}`) +
238
- chalk.gray(' ') +
239
- chalk.gray(`Crons: ${cronCount} active`);
240
- console.log(line3);
241
-
242
- console.log(chalk.gray(separator));
243
- console.log('');
244
-
245
- // Session management
246
- let session;
247
- if (options.resume) {
248
- if (options.resume === true) {
249
- // Resume latest session
250
- session = getLatestSession(bot.id);
251
- if (session) {
252
- console.log(chalk.blue(`Resumed: ${session.id}\n`));
253
- // Display last few messages
254
- const lastMessages = (session.messages || []).slice(-3);
255
- lastMessages.forEach(msg => {
256
- console.log(chalk.gray('You ') + msg.user);
257
- console.log(chalk.cyan(displayBotName + ' ') + msg.bot);
258
- console.log('');
259
- });
260
- } else {
261
- console.log(chalk.gray('No previous session. Starting new.\n'));
262
- session = createSession(bot.id, bot.name);
263
- }
264
- } else {
265
- // Resume specific session
266
- session = loadSession(bot.id, options.resume);
267
- if (session) {
268
- console.log(chalk.blue(`Resumed: ${session.id}\n`));
269
- // Display last few messages
270
- const lastMessages = (session.messages || []).slice(-3);
271
- lastMessages.forEach(msg => {
272
- console.log(chalk.gray('You ') + msg.user);
273
- console.log(chalk.cyan(displayBotName + ' ') + msg.bot);
274
- console.log('');
275
- });
276
- } else {
277
- console.log(chalk.red(`Session not found: ${options.resume}\n`));
278
- process.exit(1);
279
- }
280
- }
281
- } else {
282
- // Create new session
283
- session = createSession(bot.id, bot.name);
284
- }
285
-
286
- // Run on-start hooks
287
- await runHooks('on-start', null, { botId: bot.id, botName: bot.name });
85
+ const shortModel = providerModel.split('/').pop().split('-').slice(0, 3).join('-');
288
86
 
289
- // Skill prompts, AGENTS.md ve hafızayı yükle
290
87
  const skillPrompts = getSkillPrompts();
291
88
  const agentsPrompt = getAgentsPrompt();
292
89
  const memoryPrompt = getMemoryPrompt(bot.id);
293
-
294
- // Sistem promptunu birleştir
90
+
295
91
  let systemPrompt = '';
296
92
  if (skillPrompts) systemPrompt += skillPrompts;
297
- if (agentsPrompt) {
298
- if (systemPrompt) systemPrompt += '\n\n';
299
- systemPrompt += `## Project Instructions\n${agentsPrompt}`;
300
- }
301
- if (memoryPrompt) {
302
- if (systemPrompt) systemPrompt += '\n\n';
303
- systemPrompt += memoryPrompt;
93
+ if (agentsPrompt) systemPrompt += `\n\n## Project Instructions\n${agentsPrompt}`;
94
+ if (memoryPrompt) systemPrompt += '\n\n' + memoryPrompt;
95
+
96
+ // ── Session ─────────────────────────────────────────────────────────────────
97
+ let session;
98
+ if (options.resume) {
99
+ session = options.resume === true
100
+ ? getLatestSession(bot.id) || createSession(bot.id, bot.name)
101
+ : loadSession(bot.id, options.resume) || createSession(bot.id, bot.name);
102
+ } else {
103
+ session = createSession(bot.id, bot.name);
304
104
  }
305
105
 
306
106
  let conversationId = null;
307
107
 
308
- // Komut geçmişi
309
- const commandHistory = getCommandHistory(bot.id);
310
- let historyIndex = commandHistory.length;
108
+ // ── What's New kontrolü ─────────────────────────────────────────────────────
109
+ const lastVersionFile = path.join(os.homedir(), '.natureco', 'lastVersion');
110
+ let lastVersion = '';
111
+ try { lastVersion = fs.readFileSync(lastVersionFile, 'utf8').trim(); } catch {}
112
+ const isNewVersion = lastVersion !== version;
113
+
114
+ // ── Blessed TUI ─────────────────────────────────────────────────────────────
115
+ let blessed;
116
+ try {
117
+ blessed = require('blessed');
118
+ } catch {
119
+ // blessed yüklü değilse fallback
120
+ return chatFallback(bot, botList, displayBotName, userName, shortModel, apiKey, config, systemPrompt, session, conversationId, options);
121
+ }
311
122
 
312
- // Readline interface for arrow key support
313
- const rl = readline.createInterface({
314
- input: process.stdin,
315
- output: process.stdout,
316
- prompt: chalk.gray('You '),
317
- historySize: 100,
123
+ const screen = blessed.screen({
124
+ smartCSR: true,
125
+ title: `NatureCo · ${displayBotName}`,
126
+ fullUnicode: true,
318
127
  });
319
128
 
320
- // Load command history
321
- commandHistory.forEach(cmd => rl.history.unshift(cmd));
322
-
323
- // Ctrl+B handler for background tasks
324
- let currentMessage = '';
325
-
326
- process.stdin.on('keypress', (str, key) => {
327
- if (key && key.ctrl && key.name === 'b') {
328
- if (currentMessage && currentMessage.trim()) {
329
- const task = addBackgroundTask({
330
- botId: bot.id,
331
- botName: bot.name,
332
- message: currentMessage,
333
- });
334
- console.log(chalk.yellow(`\n📦 Task moved to background: ${task.id}`));
335
- console.log(chalk.gray('View with: natureco tasks show ' + task.id + '\n'));
336
-
337
- // Run task in background
338
- sendMessage(apiKey, bot.id, currentMessage, conversationId, systemPrompt)
339
- .then(response => {
340
- const reply = response.reply || response.message || 'No response';
341
- updateBackgroundTask(task.id, {
342
- status: 'completed',
343
- result: reply,
344
- completedAt: new Date().toISOString(),
345
- });
346
- })
347
- .catch(err => {
348
- updateBackgroundTask(task.id, {
349
- status: 'failed',
350
- error: err.message,
351
- completedAt: new Date().toISOString(),
352
- });
353
- });
354
-
355
- currentMessage = '';
356
- rl.prompt();
357
- }
358
- }
129
+ // ── Logo kutusu (üst %30) ───────────────────────────────────────────────────
130
+ const rabbitLine = `{cyan-fg}(\\_{/}{cyan-fg}/) {/}{white-fg}Hoş geldin, ${userName}{/} {gray-fg}·{/} {cyan-fg}${displayBotName} hazır{/} {gray-fg}·{/} {gray-fg}v${version}{/}`;
131
+ const logoContent = ASCII_LOGO + '\n\n' + rabbitLine;
132
+
133
+ const logoBox = blessed.box({
134
+ top: 0,
135
+ left: 0,
136
+ width: '100%',
137
+ height: '28%',
138
+ content: logoContent,
139
+ align: 'center',
140
+ valign: 'middle',
141
+ tags: true,
142
+ style: { fg: 'green', bg: 'black' },
359
143
  });
360
-
361
- // Enable keypress events
362
- if (process.stdin.isTTY) {
363
- process.stdin.setRawMode(true);
144
+
145
+ // ── Mesaj alanı (%58) ───────────────────────────────────────────────────────
146
+ const messageBox = blessed.log({
147
+ top: '28%',
148
+ left: 0,
149
+ width: '100%',
150
+ height: '58%',
151
+ scrollable: true,
152
+ alwaysScroll: true,
153
+ tags: true,
154
+ padding: { left: 2, right: 2, top: 1 },
155
+ scrollbar: { ch: '│', style: { fg: 'gray' } },
156
+ style: { bg: 'black' },
157
+ });
158
+
159
+ // ── Input kutusu (3 satır) ──────────────────────────────────────────────────
160
+ const inputBox = blessed.textbox({
161
+ bottom: 1,
162
+ left: 0,
163
+ width: '100%',
164
+ height: 3,
165
+ inputOnFocus: true,
166
+ padding: { left: 2 },
167
+ border: { type: 'line' },
168
+ style: {
169
+ border: { fg: 'green' },
170
+ fg: 'white',
171
+ bg: 'black',
172
+ focus: { border: { fg: 'cyan' } },
173
+ },
174
+ });
175
+
176
+ // ── Status bar (en alt 1 satır) ─────────────────────────────────────────────
177
+ const cwd = process.cwd().replace(os.homedir(), '~');
178
+ const statusBar = blessed.box({
179
+ bottom: 0,
180
+ left: 0,
181
+ width: '100%',
182
+ height: 1,
183
+ content: ` {cyan-fg}${displayBotName}{/} {gray-fg}·{/} {gray-fg}${shortModel}{/}{|}${cwd} `,
184
+ tags: true,
185
+ style: { bg: 'black', fg: 'gray' },
186
+ });
187
+
188
+ screen.append(logoBox);
189
+ screen.append(messageBox);
190
+ screen.append(inputBox);
191
+ screen.append(statusBar);
192
+
193
+ inputBox.focus();
194
+ screen.render();
195
+
196
+ // ── What's New ──────────────────────────────────────────────────────────────
197
+ if (isNewVersion) {
198
+ messageBox.log(`{yellow-fg}─── v${version} yenilikleri ───{/}`);
199
+ CHANGELOG.forEach(c => messageBox.log(` {gray-fg}·{/} ${c}`));
200
+ messageBox.log('');
201
+ try { fs.writeFileSync(lastVersionFile, version); } catch {}
364
202
  }
365
- readline.emitKeypressEvents(process.stdin);
366
203
 
367
- async function processMessage(userMessage) {
368
- userMessage = userMessage.trim();
369
- currentMessage = userMessage;
204
+ // Önceki session mesajlarını göster
205
+ if (options.resume && session.messages?.length) {
206
+ const last = session.messages.slice(-5);
207
+ last.forEach(msg => {
208
+ messageBox.log(`{white-fg}You{/} ${msg.user}`);
209
+ messageBox.log(`{cyan-fg}${displayBotName}{/} ${msg.bot}`);
210
+ messageBox.log('');
211
+ });
212
+ }
370
213
 
371
- if (!userMessage || userMessage.length === 0) {
372
- rl.prompt();
373
- return;
374
- }
214
+ // ── Yükleme animasyonu ──────────────────────────────────────────────────────
215
+ let loadingTimer = null;
216
+ let loadingFrame = 0;
217
+ const loadingFrames = ['●○○', '○●○', '○○●'];
218
+
219
+ function startLoading() {
220
+ loadingFrame = 0;
221
+ loadingTimer = setInterval(() => {
222
+ statusBar.setContent(` {cyan-fg}${displayBotName}{/} {gray-fg}·{/} {gray-fg}${loadingFrames[loadingFrame]}{/}{|}${cwd} `);
223
+ loadingFrame = (loadingFrame + 1) % loadingFrames.length;
224
+ screen.render();
225
+ }, 300);
226
+ }
375
227
 
376
- // Chat komutları
377
- if (userMessage.startsWith('/')) {
378
- const [command, ...args] = userMessage.slice(1).split(' ');
228
+ function stopLoading() {
229
+ if (loadingTimer) { clearInterval(loadingTimer); loadingTimer = null; }
230
+ statusBar.setContent(` {cyan-fg}${displayBotName}{/} {gray-fg}·{/} {gray-fg}${shortModel}{/}{|}${cwd} `);
231
+ screen.render();
232
+ }
379
233
 
380
- switch (command.toLowerCase()) {
234
+ // ── Mesaj gönderme ──────────────────────────────────────────────────────────
235
+ async function handleMessage(userMessage) {
236
+ userMessage = userMessage.trim();
237
+ if (!userMessage) { inputBox.focus(); screen.render(); return; }
238
+
239
+ // /komutlar
240
+ if (userMessage.startsWith('/')) {
241
+ const [cmd, ...args] = userMessage.slice(1).split(' ');
242
+ switch (cmd.toLowerCase()) {
381
243
  case 'clear':
382
- console.clear();
383
-
384
- // Rabbit with fixed unicode
385
- const rabbitClear = chalk.cyan(' (\\\_/)') + '\n' +
386
- chalk.cyan(' (•ᴥ•)') + '\n' +
387
- chalk.cyan(' />') + chalk.green('🌿');
388
- console.log(rabbitClear);
389
- console.log('');
390
-
391
- const terminalWidth2 = process.stdout.columns || 80;
392
- const separator2 = '─'.repeat(terminalWidth2);
393
-
394
- console.log(chalk.gray(separator2));
395
- console.log(chalk.white.bold('NatureCo') + chalk.gray(' · ') + chalk.cyan(displayBotName) + chalk.gray(' · ') + chalk.gray(shortModel));
396
- console.log(chalk.gray(separator2));
397
- console.log('');
398
- rl.prompt();
244
+ messageBox.setContent('');
245
+ screen.render();
399
246
  return;
400
-
401
247
  case 'bot':
402
- if (args.length === 0) {
403
- console.log(chalk.yellow('\nMevcut bot: ') + chalk.cyan(bot.name));
404
- console.log(chalk.gray('\nBot değiştirmek için: /bot <bot-adı>'));
405
- console.log(chalk.gray('Mevcut botlar:'));
248
+ if (!args.length) {
249
+ messageBox.log(`{yellow-fg}Aktif bot:{/} ${bot.name}`);
406
250
  botList.bots.forEach(b => {
407
- const marker = b.id === bot.id ? chalk.green('✓ ') : ' ';
408
- console.log(marker + chalk.cyan(b.name));
251
+ const mark = b.id === bot.id ? '{green-fg}{/} ' : ' ';
252
+ messageBox.log(`${mark}{cyan-fg}${b.name}{/}`);
409
253
  });
410
- console.log('');
411
- rl.prompt();
412
- return;
413
- }
414
-
415
- const newBotName = args.join(' ');
416
- const newBot = botList.bots.find(b => b.name.toLowerCase() === newBotName.toLowerCase());
417
-
418
- if (!newBot) {
419
- console.log(chalk.red(`\n❌ Bot "${newBotName}" bulunamadı.\n`));
420
- rl.prompt();
421
- return;
254
+ } else {
255
+ const newName = args.join(' ');
256
+ const newBot = botList.bots.find(b => b.name.toLowerCase() === newName.toLowerCase());
257
+ if (newBot) {
258
+ bot = newBot;
259
+ conversationId = null;
260
+ session = createSession(bot.id, bot.name);
261
+ messageBox.log(`{green-fg}Bot değişti: ${newBot.name}{/}`);
262
+ } else {
263
+ messageBox.log(`{red-fg}Bot bulunamadı: ${newName}{/}`);
264
+ }
422
265
  }
423
-
424
- bot = newBot;
425
- conversationId = null;
426
- session = createSession(bot.id, bot.name);
427
-
428
- // Update displayBotName
429
- const newMemory = loadMemory(bot.id);
430
- const newDisplayBotName = newMemory.botName || bot.name;
431
-
432
- console.log(chalk.green(`\nBot changed: ${newDisplayBotName}\n`));
433
- console.log(chalk.gray(`Session: ${session.id}\n`));
434
- rl.prompt();
266
+ screen.render();
435
267
  return;
436
-
437
268
  case 'skills':
438
269
  const skills = getSkills();
439
- if (skills.length === 0) {
440
- console.log(chalk.gray('\nYüklü skill yok.\n'));
441
- } else {
442
- console.log(chalk.yellow('\nAktif skill\'ler:\n'));
443
- skills.forEach(skill => {
444
- console.log(chalk.cyan(` • ${skill.name}`) + chalk.gray(` (${skill.source})`));
445
- console.log(chalk.gray(` ${skill.description}`));
446
- });
447
- console.log('');
448
- }
449
- rl.prompt();
270
+ if (!skills.length) { messageBox.log('{gray-fg}Yüklü skill yok.{/}'); }
271
+ else skills.forEach(s => messageBox.log(`{cyan-fg}· ${s.name}{/} {gray-fg}${s.description}{/}`));
272
+ screen.render();
450
273
  return;
451
-
452
274
  case 'memory':
453
- if (args.length > 0 && args[0] === 'clear') {
275
+ if (args[0] === 'clear') {
454
276
  clearMemory(bot.id);
455
- console.log(chalk.green('\n✅ Hafıza temizlendi\n'));
456
- rl.prompt();
457
- return;
458
- }
459
-
460
- const memory = loadMemory(bot.id);
461
- console.log(chalk.yellow('\nHafıza:\n'));
462
- if (memory.botName) {
463
- console.log(chalk.cyan(' Bot Adı:'), chalk.white(memory.botName));
464
- }
465
- if (memory.name) {
466
- console.log(chalk.cyan(' İsim:'), chalk.white(memory.name));
467
- }
468
- if (memory.preferences.length > 0) {
469
- const sortedPrefs = memory.preferences.sort((a, b) => b.score - a.score);
470
- console.log(chalk.cyan(' Tercihler:'));
471
- sortedPrefs.forEach(p => {
472
- console.log(chalk.white(` • ${p.value}`) + chalk.gray(` (skor: ${p.score})`));
473
- });
474
- }
475
- if (memory.facts.length > 0) {
476
- const sortedFacts = memory.facts.sort((a, b) => b.score - a.score);
477
- console.log(chalk.cyan(' Bilgiler:'));
478
- sortedFacts.forEach(f => {
479
- console.log(chalk.white(` • ${f.value}`) + chalk.gray(` (skor: ${f.score})`));
480
- });
481
- }
482
- if (!memory.botName && !memory.name && memory.preferences.length === 0 && memory.facts.length === 0) {
483
- console.log(chalk.gray(' Henüz hafıza yok'));
484
- }
485
- console.log('');
486
- rl.prompt();
487
- return;
488
-
489
- case 'commands':
490
- const commands = getCommands();
491
- if (commands.length === 0) {
492
- console.log(chalk.gray('\nÖzel komut yok.\n'));
493
- console.log(chalk.gray('Oluşturmak için: natureco commands create <ad>\n'));
277
+ messageBox.log('{green-fg}✓ Hafıza temizlendi{/}');
494
278
  } else {
495
- console.log(chalk.yellow('\nÖzel Komutlar:\n'));
496
- commands.forEach(cmd => {
497
- const sourceLabel = cmd.source === 'user' ? chalk.blue('[global]') : chalk.green('[project]');
498
- console.log(` ${sourceLabel} ${chalk.cyan('/' + cmd.name)}`);
279
+ const m = loadMemory(bot.id);
280
+ if (m.botName) messageBox.log(`{cyan-fg}Bot:{/} ${m.botName}`);
281
+ if (m.name) messageBox.log(`{cyan-fg}İsim:{/} ${m.name}`);
282
+ (m.facts || []).slice(0, 8).forEach(f => {
283
+ const v = typeof f === 'string' ? f : f.value;
284
+ messageBox.log(`{gray-fg}· ${v}{/}`);
499
285
  });
500
- console.log('');
501
286
  }
502
- rl.prompt();
287
+ screen.render();
503
288
  return;
504
-
505
289
  case 'help':
506
- console.log(chalk.yellow('\nChat Komutları:\n'));
507
- console.log(chalk.cyan(' /clear') + chalk.gray(' - Ekranı temizle'));
508
- console.log(chalk.cyan(' /bot [ad]') + chalk.gray(' - Bot değiştir veya listele'));
509
- console.log(chalk.cyan(' /skills') + chalk.gray(' - Aktif skill\'leri göster'));
510
- console.log(chalk.cyan(' /memory') + chalk.gray(' - Hafızayı göster'));
511
- console.log(chalk.cyan(' /memory clear') + chalk.gray(' - Hafızayı temizle'));
512
- console.log(chalk.cyan(' /commands') + chalk.gray(' - Özel komutları listele'));
513
- console.log(chalk.cyan(' /ultrareview') + chalk.gray(' - Son kod bloğunu detaylı incele'));
514
- console.log(chalk.cyan(' /help') + chalk.gray(' - Bu yardım mesajını göster'));
515
- console.log(chalk.cyan(' exit, quit') + chalk.gray(' - Chat\'ten çık'));
516
- console.log(chalk.gray('\nYukarı/aşağı ok tuşları ile komut geçmişinde gezin.'));
517
- console.log(chalk.gray('Ctrl+B: Görevi arka plana al\n'));
518
- rl.prompt();
290
+ [
291
+ '{yellow-fg}Chat Komutları:{/}',
292
+ '{cyan-fg}/clear{/} Ekranı temizle',
293
+ '{cyan-fg}/bot [ad]{/} Bot değiştir',
294
+ '{cyan-fg}/skills{/} Skill listesi',
295
+ '{cyan-fg}/memory{/} Hafızayı göster',
296
+ '{cyan-fg}/memory clear{/} Hafızayı temizle',
297
+ '{cyan-fg}/commands{/} Özel komutlar',
298
+ '{cyan-fg}/help{/} Bu yardım',
299
+ '{gray-fg}Ctrl+C Çıkış{/}',
300
+ ].forEach(l => messageBox.log(l));
301
+ screen.render();
519
302
  return;
520
-
521
- case 'ultrareview':
522
- // Extract last code block from conversation
523
- const historyData = require('../utils/history');
524
- const chatHistory = historyData.getHistory ? historyData.getHistory(bot.id) : [];
525
-
526
- let lastCodeBlock = null;
527
- for (let i = chatHistory.length - 1; i >= 0; i--) {
528
- const msg = chatHistory[i];
529
- const codeMatch = (msg.bot || '').match(/```[\s\S]*?\n([\s\S]*?)```/);
530
- if (codeMatch) {
531
- lastCodeBlock = codeMatch[1];
532
- break;
533
- }
534
- }
535
-
536
- if (!lastCodeBlock) {
537
- console.log(chalk.yellow('\n⚠️ Konuşmada kod bloğu bulunamadı\n'));
538
- rl.prompt();
539
- return;
540
- }
541
-
542
- console.log(chalk.yellow('\n⏳ Kod inceleniyor...\n'));
543
-
544
- const reviewPrompt = `Aşağıdaki kodu çok detaylı incele ve şu kategorilerde analiz et:
545
-
546
- 1. **Güvenlik Açıkları** (1-10 puan)
547
- 2. **Performans Sorunları** (1-10 puan)
548
- 3. **Kod Kalitesi** (1-10 puan)
549
- 4. **Hata Yönetimi** (1-10 puan)
550
- 5. **Best Practices** (1-10 puan)
551
- 6. **Potansiyel Bug'lar**
552
-
553
- Her kategori için puan ver ve sorunları listele.
554
-
555
- \`\`\`
556
- ${lastCodeBlock}
557
- \`\`\``;
558
-
559
- const loadingInterval = startLoadingAnimation();
560
-
561
- try {
562
- const response = await sendMessage(apiKey, bot.id, reviewPrompt, conversationId, systemPrompt);
563
-
564
- stopLoadingAnimation(loadingInterval);
565
-
566
- if (response.conversation_id) {
567
- conversationId = response.conversation_id;
568
- }
569
-
570
- const botReply = response.reply || response.message || 'No response';
571
- console.log(chalk.cyan(`${displayBotName} `) + botReply + '\n');
572
-
573
- addToHistory(bot.id, '/ultrareview', botReply, conversationId);
574
- addMessageToSession(bot.id, session.id, '/ultrareview', botReply);
575
- } catch (err) {
576
- stopLoadingAnimation(loadingInterval);
577
- // Extract clean error message
578
- const errorMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
579
- console.log(chalk.red(`Error: ${errorMsg}\n`));
580
- }
581
-
582
- rl.prompt();
303
+ case 'commands':
304
+ const cmds = getCommands();
305
+ if (!cmds.length) messageBox.log('{gray-fg}Özel komut yok.{/}');
306
+ else cmds.forEach(c => messageBox.log(`{cyan-fg}/${c.name}{/}`));
307
+ screen.render();
583
308
  return;
584
-
585
309
  default:
586
- // Check for custom commands
587
- const customCommand = getCommandContent(command);
588
- if (customCommand) {
589
- console.log(chalk.blue(`\n📝 Using custom command: /${command}\n`));
590
- // Add custom command to system prompt
591
- const customPrompt = systemPrompt + '\n\n## Custom Command\n' + customCommand;
592
-
593
- // If there are args, use them as the message
594
- const message = args.length > 0 ? args.join(' ') : 'Execute the custom command instruction';
595
-
596
- const loadingInterval = startLoadingAnimation();
597
-
310
+ const customCmd = getCommandContent(cmd);
311
+ if (customCmd) {
312
+ const customPrompt = systemPrompt + '\n\n## Custom Command\n' + customCmd;
313
+ const msg = args.length ? args.join(' ') : 'Execute the custom command instruction';
314
+ messageBox.log(`{white-fg}You{/} ${userMessage}`);
315
+ startLoading();
598
316
  try {
599
- const response = await sendMessage(apiKey, bot.id, message, conversationId, customPrompt);
600
-
601
- stopLoadingAnimation(loadingInterval);
602
-
603
- if (response.conversation_id) {
604
- conversationId = response.conversation_id;
605
- }
606
-
607
- const botReply = response.reply || response.message || 'No response';
608
- console.log(chalk.cyan(`${displayBotName} `) + botReply + '\n');
609
-
610
- addToHistory(bot.id, userMessage, botReply, conversationId);
611
- addMessageToSession(bot.id, session.id, userMessage, botReply);
612
- } catch (err) {
613
- stopLoadingAnimation(loadingInterval);
614
- // Extract clean error message
615
- const errorMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
616
- console.log(chalk.red(`Error: ${errorMsg}\n`));
617
- }
618
-
619
- rl.prompt();
620
- return;
317
+ const res = await sendMessage(apiKey || config.providerApiKey, bot.id, msg, conversationId, customPrompt);
318
+ stopLoading();
319
+ if (res.conversation_id) conversationId = res.conversation_id;
320
+ const reply = res.reply || res.message || '';
321
+ messageBox.log(`{cyan-fg}${displayBotName}{/} ${reply}`);
322
+ messageBox.log('');
323
+ addToHistory(bot.id, userMessage, reply, conversationId);
324
+ addMessageToSession(bot.id, session.id, userMessage, reply);
325
+ } catch (e) { stopLoading(); messageBox.log(`{red-fg}Error: ${e.message}{/}`); }
326
+ messageBox.setScrollPerc(100);
327
+ screen.render();
328
+ } else {
329
+ messageBox.log(`{red-fg}Bilinmeyen komut: /${cmd}{/}`);
330
+ screen.render();
621
331
  }
622
-
623
- console.log(chalk.red(`\n❌ Bilinmeyen komut: /${command}`));
624
- console.log(chalk.gray('Yardım için: /help\n'));
625
- rl.prompt();
626
332
  return;
627
333
  }
628
334
  }
629
335
 
630
- // Exit komutu
631
- if (userMessage.toLowerCase() === 'exit' || userMessage.toLowerCase() === 'quit') {
336
+ // exit/quit
337
+ if (userMessage === 'exit' || userMessage === 'quit') {
632
338
  await runHooks('on-exit', null, { botId: bot.id, botName: bot.name });
633
- console.log(chalk.gray('\n👋 Goodbye!\n'));
634
- rl.close();
339
+ screen.destroy();
635
340
  process.exit(0);
636
341
  }
637
342
 
638
- // Run pre-message hooks
343
+ // Normal mesaj
639
344
  userMessage = await runHooks('pre-message', userMessage, { botId: bot.id, botName: bot.name });
345
+ messageBox.log(`{white-fg}You{/} ${userMessage}`);
346
+ messageBox.setScrollPerc(100);
347
+ screen.render();
640
348
 
641
- // Loading animasyonu
642
- const loadingInterval = startLoadingAnimation();
349
+ startLoading();
643
350
 
644
351
  try {
645
- // Get tool definitions
646
352
  const toolDefinitions = getToolDefinitions();
647
-
648
- // Initial API call with tools
649
- let response = await _sendMessage(apiKey, bot.id, userMessage, conversationId, systemPrompt, toolDefinitions);
650
-
651
- stopLoadingAnimation(loadingInterval);
652
-
653
- if (response.conversation_id) {
654
- conversationId = response.conversation_id;
655
- }
353
+ let response = await _sendMessage(apiKey || config.providerApiKey, bot.id, userMessage, conversationId, systemPrompt, toolDefinitions);
354
+ stopLoading();
355
+
356
+ if (response.conversation_id) conversationId = response.conversation_id;
656
357
 
657
- // Tool execution loop
658
- let maxIterations = 5;
659
- let iteration = 0;
660
-
661
- while (iteration < maxIterations) {
662
- // Check for tool calls
358
+ // Tool loop
359
+ let iter = 0;
360
+ while (iter < 5) {
663
361
  const toolCalls = extractToolCalls(response);
664
-
665
- if (!toolCalls || toolCalls.length === 0) {
666
- // No more tool calls, break loop
667
- break;
668
- }
669
-
670
- console.log(chalk.yellow(`\n🔧 Executing ${toolCalls.length} tool(s)...\n`));
671
-
672
- // Execute tools
362
+ if (!toolCalls?.length) break;
363
+ messageBox.log(`{yellow-fg}🔧 ${toolCalls.length} tool çalıştırılıyor...{/}`);
364
+ screen.render();
673
365
  const toolResults = await executeToolCalls(toolCalls);
674
-
675
- // Format results as message
676
- const toolResultMessage = toolResults.map(tr => {
677
- const resultStr = tr.result.success
678
- ? (tr.result.output || JSON.stringify(tr.result))
679
- : `Error: ${tr.result.error}`;
680
- return `Tool: ${tr.name}\nResult: ${resultStr}`;
681
- }).join('\n\n');
682
-
683
- console.log(chalk.blue('\n📤 Sending tool results back to bot...\n'));
684
-
685
- // Send tool results back to API
686
- const loadingInterval2 = startLoadingAnimation();
687
- response = await _sendMessage(apiKey, bot.id, toolResultMessage, conversationId, systemPrompt, toolDefinitions);
688
- stopLoadingAnimation(loadingInterval2);
689
-
690
- if (response.conversation_id) {
691
- conversationId = response.conversation_id;
692
- }
693
-
694
- iteration++;
695
- }
696
-
697
- if (iteration >= maxIterations) {
698
- console.log(chalk.yellow('\n⚠️ Max tool iterations reached\n'));
366
+ const toolMsg = toolResults.map(tr => `Tool: ${tr.name}\nResult: ${tr.result.success ? (tr.result.output || '') : tr.result.error}`).join('\n\n');
367
+ startLoading();
368
+ response = await _sendMessage(apiKey || config.providerApiKey, bot.id, toolMsg, conversationId, systemPrompt, toolDefinitions);
369
+ stopLoading();
370
+ if (response.conversation_id) conversationId = response.conversation_id;
371
+ iter++;
699
372
  }
700
373
 
701
374
  let botReply = response.reply || response.message || 'No response';
702
-
703
- // Run post-message hooks
704
375
  botReply = await runHooks('post-message', botReply, { botId: bot.id, botName: bot.name });
705
-
706
- console.log(chalk.cyan(`${displayBotName} `) + botReply + '\n');
707
376
 
708
- // Save to history and session
377
+ messageBox.log(`{cyan-fg}${displayBotName}{/} ${botReply}`);
378
+ messageBox.log('');
379
+ messageBox.setScrollPerc(100);
380
+ screen.render();
381
+
709
382
  addToHistory(bot.id, userMessage, botReply, conversationId);
710
383
  addMessageToSession(bot.id, session.id, userMessage, botReply);
711
384
 
712
- // Extract and save memory
713
- const memoryEntries = extractMemoryFromMessage(userMessage);
714
- for (const entry of memoryEntries) {
715
- const { addMemoryEntry } = require('../utils/memory');
716
- addMemoryEntry(bot.id, entry.key, entry.value);
717
- }
385
+ const memEntries = extractMemoryFromMessage(userMessage);
386
+ for (const e of memEntries) addMemoryEntry(bot.id, e.key, e.value);
387
+
718
388
  } catch (err) {
719
- stopLoadingAnimation(loadingInterval);
720
- // Extract clean error message
721
- const errorMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
722
- console.log(chalk.red(`Error: ${errorMsg}\n`));
389
+ stopLoading();
390
+ const errMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
391
+ messageBox.log(`{red-fg}Error: ${errMsg}{/}`);
392
+ messageBox.setScrollPerc(100);
393
+ screen.render();
723
394
  }
724
395
 
725
- currentMessage = '';
726
- rl.prompt();
396
+ inputBox.focus();
727
397
  }
728
398
 
729
- rl.on('line', (line) => {
730
- processMessage(line);
399
+ // ── Enter tuşu ──────────────────────────────────────────────────────────────
400
+ inputBox.key('enter', async () => {
401
+ const msg = inputBox.getValue();
402
+ inputBox.clearValue();
403
+ screen.render();
404
+ await handleMessage(msg);
405
+ inputBox.focus();
406
+ screen.render();
731
407
  });
732
408
 
733
- rl.on('close', () => {
734
- runHooks('on-exit', null, { botId: bot.id, botName: bot.name }).then(() => {
735
- console.log(chalk.gray('\n👋 Goodbye!\n'));
736
- process.exit(0);
737
- });
409
+ // ── Ctrl+C çıkış ────────────────────────────────────────────────────────────
410
+ screen.key(['C-c'], async () => {
411
+ await runHooks('on-exit', null, { botId: bot.id, botName: bot.name });
412
+ screen.destroy();
413
+ process.exit(0);
738
414
  });
739
415
 
740
- rl.prompt();
741
- }
416
+ // ── on-start hooks ──────────────────────────────────────────────────────────
417
+ await runHooks('on-start', null, { botId: bot.id, botName: bot.name });
742
418
 
743
- function startLoadingAnimation() {
744
- const frames = ['●○○', '○●○', '○○●'];
745
- let i = 0;
746
-
747
- // Get displayBotName from current context (will be passed as parameter)
748
- process.stdout.write(chalk.cyan('... '));
749
-
750
- return setInterval(() => {
751
- process.stdout.write(`\r${chalk.cyan(frames[i] + ' ')}`);
752
- i = (i + 1) % frames.length;
753
- }, 300);
419
+ screen.render();
754
420
  }
755
421
 
756
- function stopLoadingAnimation(interval) {
757
- clearInterval(interval);
758
- process.stdout.write('\r');
422
+ // ── Fallback: blessed yoksa eski readline tabanlı chat ────────────────────────
423
+ async function chatFallback(bot, botList, displayBotName, userName, shortModel, apiKey, config, systemPrompt, session, conversationId, options) {
424
+ const readline = require('readline');
425
+ const chalk = require('chalk');
426
+
427
+ console.clear();
428
+ console.log(chalk.green.bold(' (\\_/)'));
429
+ console.log(chalk.green.bold(' (•ᴥ•)'));
430
+ console.log(chalk.green(' />🌿'));
431
+ console.log('');
432
+ const sep = chalk.gray('─'.repeat(process.stdout.columns || 80));
433
+ console.log(sep);
434
+ console.log(chalk.white.bold('NatureCo') + chalk.gray(' · ') + chalk.cyan(displayBotName) + chalk.gray(' · ') + chalk.gray(shortModel));
435
+ console.log(sep);
436
+ console.log('');
437
+
438
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: chalk.gray('You ') });
439
+ rl.prompt();
440
+
441
+ rl.on('line', async (line) => {
442
+ const msg = line.trim();
443
+ if (!msg) { rl.prompt(); return; }
444
+ if (msg === 'exit' || msg === 'quit') { console.log(chalk.gray('\n👋 Goodbye!\n')); process.exit(0); }
445
+
446
+ process.stdout.write(chalk.cyan('... '));
447
+ try {
448
+ const toolDefinitions = getToolDefinitions();
449
+ const response = await _sendMessage(apiKey || config.providerApiKey, bot.id, msg, conversationId, systemPrompt, toolDefinitions);
450
+ process.stdout.write('\r');
451
+ if (response.conversation_id) conversationId = response.conversation_id;
452
+ const reply = response.reply || response.message || '';
453
+ console.log(chalk.cyan(`${displayBotName} `) + reply + '\n');
454
+ addToHistory(bot.id, msg, reply, conversationId);
455
+ addMessageToSession(bot.id, session.id, msg, reply);
456
+ } catch (e) {
457
+ process.stdout.write('\r');
458
+ console.log(chalk.red(`Error: ${e.message}\n`));
459
+ }
460
+ rl.prompt();
461
+ });
462
+
463
+ rl.on('close', () => process.exit(0));
759
464
  }
760
465
 
761
466
  module.exports = chat;