natureco-cli 2.17.2 → 2.17.6

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.17.2",
3
+ "version": "2.17.6",
4
4
  "description": "NatureCo AI Bot Terminal Interface",
5
5
  "main": "bin/natureco.js",
6
6
  "bin": {
@@ -25,6 +25,7 @@
25
25
  "license": "MIT",
26
26
  "dependencies": {
27
27
  "@whiskeysockets/baileys": "^7.0.0-rc10",
28
+ "blessed": "^0.1.81",
28
29
  "boxen": "^5.1.2",
29
30
  "chalk": "^4.1.2",
30
31
  "commander": "^11.1.0",
@@ -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,452 @@ 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
+ terminal: 'xterm-256color',
127
+ fullUnicode: true,
318
128
  });
319
129
 
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
- }
130
+ // ── Logo kutusu (üst %30) ───────────────────────────────────────────────────
131
+ const rabbitLine = `{cyan-fg}(\\_{/}{cyan-fg}/) {/}{white-fg}Hoş geldin, ${userName}{/} {gray-fg}·{/} {cyan-fg}${displayBotName} hazır{/} {gray-fg}·{/} {gray-fg}v${version}{/}`;
132
+ const logoContent = ASCII_LOGO + '\n\n' + rabbitLine;
133
+
134
+ const logoBox = blessed.box({
135
+ top: 0,
136
+ left: 0,
137
+ width: '100%',
138
+ height: '28%',
139
+ content: logoContent,
140
+ align: 'center',
141
+ valign: 'middle',
142
+ tags: true,
143
+ style: { fg: 'green', bg: 'default' },
359
144
  });
360
-
361
- // Enable keypress events
362
- if (process.stdin.isTTY) {
363
- process.stdin.setRawMode(true);
145
+
146
+ // ── Mesaj alanı (%58) ───────────────────────────────────────────────────────
147
+ const messageBox = blessed.log({
148
+ top: '28%',
149
+ left: 0,
150
+ width: '100%',
151
+ height: '58%',
152
+ scrollable: true,
153
+ alwaysScroll: true,
154
+ tags: true,
155
+ padding: { left: 2, right: 2, top: 1 },
156
+ scrollbar: { ch: '│', style: { fg: 'gray' } },
157
+ style: { bg: 'default' },
158
+ });
159
+
160
+ // ── Input kutusu (3 satır) ──────────────────────────────────────────────────
161
+ const inputBox = blessed.textbox({
162
+ bottom: 1,
163
+ left: 0,
164
+ width: '100%',
165
+ height: 3,
166
+ inputOnFocus: true,
167
+ padding: { left: 2 },
168
+ border: { type: 'line' },
169
+ style: {
170
+ border: { fg: 'green' },
171
+ fg: 'white',
172
+ bg: 'default',
173
+ focus: { border: { fg: 'cyan' } },
174
+ },
175
+ });
176
+
177
+ // ── Status bar (en alt 1 satır) ─────────────────────────────────────────────
178
+ const cwd = process.cwd().replace(os.homedir(), '~');
179
+ const statusBar = blessed.box({
180
+ bottom: 0,
181
+ left: 0,
182
+ width: '100%',
183
+ height: 1,
184
+ content: ` {cyan-fg}${displayBotName}{/} {gray-fg}·{/} {gray-fg}${shortModel}{/}{|}${cwd} `,
185
+ tags: true,
186
+ style: { bg: 'default', fg: 'gray' },
187
+ });
188
+
189
+ screen.append(logoBox);
190
+ screen.append(messageBox);
191
+ screen.append(inputBox);
192
+ screen.append(statusBar);
193
+
194
+ inputBox.focus();
195
+ screen.render();
196
+
197
+ // ── What's New ──────────────────────────────────────────────────────────────
198
+ if (isNewVersion) {
199
+ messageBox.log(`{yellow-fg}─── v${version} yenilikleri ───{/}`);
200
+ CHANGELOG.forEach(c => messageBox.log(` {gray-fg}·{/} ${c}`));
201
+ messageBox.log('');
202
+ try { fs.writeFileSync(lastVersionFile, version); } catch {}
364
203
  }
365
- readline.emitKeypressEvents(process.stdin);
366
204
 
367
- async function processMessage(userMessage) {
368
- userMessage = userMessage.trim();
369
- currentMessage = userMessage;
205
+ // Önceki session mesajlarını göster
206
+ if (options.resume && session.messages?.length) {
207
+ const last = session.messages.slice(-5);
208
+ last.forEach(msg => {
209
+ messageBox.log(`{white-fg}You{/} ${msg.user}`);
210
+ messageBox.log(`{cyan-fg}${displayBotName}{/} ${msg.bot}`);
211
+ messageBox.log('');
212
+ });
213
+ }
370
214
 
371
- if (!userMessage || userMessage.length === 0) {
372
- rl.prompt();
373
- return;
374
- }
215
+ // ── Yükleme animasyonu ──────────────────────────────────────────────────────
216
+ let loadingTimer = null;
217
+ let loadingFrame = 0;
218
+ const loadingFrames = ['●○○', '○●○', '○○●'];
219
+
220
+ function startLoading() {
221
+ loadingFrame = 0;
222
+ loadingTimer = setInterval(() => {
223
+ statusBar.setContent(` {cyan-fg}${displayBotName}{/} {gray-fg}·{/} {gray-fg}${loadingFrames[loadingFrame]}{/}{|}${cwd} `);
224
+ loadingFrame = (loadingFrame + 1) % loadingFrames.length;
225
+ screen.render();
226
+ }, 300);
227
+ }
375
228
 
376
- // Chat komutları
377
- if (userMessage.startsWith('/')) {
378
- const [command, ...args] = userMessage.slice(1).split(' ');
229
+ function stopLoading() {
230
+ if (loadingTimer) { clearInterval(loadingTimer); loadingTimer = null; }
231
+ statusBar.setContent(` {cyan-fg}${displayBotName}{/} {gray-fg}·{/} {gray-fg}${shortModel}{/}{|}${cwd} `);
232
+ screen.render();
233
+ }
379
234
 
380
- switch (command.toLowerCase()) {
235
+ // ── Mesaj gönderme ──────────────────────────────────────────────────────────
236
+ async function handleMessage(userMessage) {
237
+ userMessage = userMessage.trim();
238
+ if (!userMessage) { inputBox.focus(); screen.render(); return; }
239
+
240
+ // /komutlar
241
+ if (userMessage.startsWith('/')) {
242
+ const [cmd, ...args] = userMessage.slice(1).split(' ');
243
+ switch (cmd.toLowerCase()) {
381
244
  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();
245
+ messageBox.setContent('');
246
+ screen.render();
399
247
  return;
400
-
401
248
  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:'));
249
+ if (!args.length) {
250
+ messageBox.log(`{yellow-fg}Aktif bot:{/} ${bot.name}`);
406
251
  botList.bots.forEach(b => {
407
- const marker = b.id === bot.id ? chalk.green('✓ ') : ' ';
408
- console.log(marker + chalk.cyan(b.name));
252
+ const mark = b.id === bot.id ? '{green-fg}{/} ' : ' ';
253
+ messageBox.log(`${mark}{cyan-fg}${b.name}{/}`);
409
254
  });
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;
255
+ } else {
256
+ const newName = args.join(' ');
257
+ const newBot = botList.bots.find(b => b.name.toLowerCase() === newName.toLowerCase());
258
+ if (newBot) {
259
+ bot = newBot;
260
+ conversationId = null;
261
+ session = createSession(bot.id, bot.name);
262
+ messageBox.log(`{green-fg}Bot değişti: ${newBot.name}{/}`);
263
+ } else {
264
+ messageBox.log(`{red-fg}Bot bulunamadı: ${newName}{/}`);
265
+ }
422
266
  }
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();
267
+ screen.render();
435
268
  return;
436
-
437
269
  case 'skills':
438
270
  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();
271
+ if (!skills.length) { messageBox.log('{gray-fg}Yüklü skill yok.{/}'); }
272
+ else skills.forEach(s => messageBox.log(`{cyan-fg}· ${s.name}{/} {gray-fg}${s.description}{/}`));
273
+ screen.render();
450
274
  return;
451
-
452
275
  case 'memory':
453
- if (args.length > 0 && args[0] === 'clear') {
276
+ if (args[0] === 'clear') {
454
277
  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'));
278
+ messageBox.log('{green-fg}✓ Hafıza temizlendi{/}');
494
279
  } 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)}`);
280
+ const m = loadMemory(bot.id);
281
+ if (m.botName) messageBox.log(`{cyan-fg}Bot:{/} ${m.botName}`);
282
+ if (m.name) messageBox.log(`{cyan-fg}İsim:{/} ${m.name}`);
283
+ (m.facts || []).slice(0, 8).forEach(f => {
284
+ const v = typeof f === 'string' ? f : f.value;
285
+ messageBox.log(`{gray-fg}· ${v}{/}`);
499
286
  });
500
- console.log('');
501
287
  }
502
- rl.prompt();
288
+ screen.render();
503
289
  return;
504
-
505
290
  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();
291
+ [
292
+ '{yellow-fg}Chat Komutları:{/}',
293
+ '{cyan-fg}/clear{/} Ekranı temizle',
294
+ '{cyan-fg}/bot [ad]{/} Bot değiştir',
295
+ '{cyan-fg}/skills{/} Skill listesi',
296
+ '{cyan-fg}/memory{/} Hafızayı göster',
297
+ '{cyan-fg}/memory clear{/} Hafızayı temizle',
298
+ '{cyan-fg}/commands{/} Özel komutlar',
299
+ '{cyan-fg}/help{/} Bu yardım',
300
+ '{gray-fg}Ctrl+C Çıkış{/}',
301
+ ].forEach(l => messageBox.log(l));
302
+ screen.render();
519
303
  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();
304
+ case 'commands':
305
+ const cmds = getCommands();
306
+ if (!cmds.length) messageBox.log('{gray-fg}Özel komut yok.{/}');
307
+ else cmds.forEach(c => messageBox.log(`{cyan-fg}/${c.name}{/}`));
308
+ screen.render();
583
309
  return;
584
-
585
310
  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
-
311
+ const customCmd = getCommandContent(cmd);
312
+ if (customCmd) {
313
+ const customPrompt = systemPrompt + '\n\n## Custom Command\n' + customCmd;
314
+ const msg = args.length ? args.join(' ') : 'Execute the custom command instruction';
315
+ messageBox.log(`{white-fg}You{/} ${userMessage}`);
316
+ startLoading();
598
317
  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;
318
+ const res = await sendMessage(apiKey || config.providerApiKey, bot.id, msg, conversationId, customPrompt);
319
+ stopLoading();
320
+ if (res.conversation_id) conversationId = res.conversation_id;
321
+ const reply = res.reply || res.message || '';
322
+ messageBox.log(`{cyan-fg}${displayBotName}{/} ${reply}`);
323
+ messageBox.log('');
324
+ addToHistory(bot.id, userMessage, reply, conversationId);
325
+ addMessageToSession(bot.id, session.id, userMessage, reply);
326
+ } catch (e) { stopLoading(); messageBox.log(`{red-fg}Error: ${e.message}{/}`); }
327
+ messageBox.setScrollPerc(100);
328
+ screen.render();
329
+ } else {
330
+ messageBox.log(`{red-fg}Bilinmeyen komut: /${cmd}{/}`);
331
+ screen.render();
621
332
  }
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
333
  return;
627
334
  }
628
335
  }
629
336
 
630
- // Exit komutu
631
- if (userMessage.toLowerCase() === 'exit' || userMessage.toLowerCase() === 'quit') {
337
+ // exit/quit
338
+ if (userMessage === 'exit' || userMessage === 'quit') {
632
339
  await runHooks('on-exit', null, { botId: bot.id, botName: bot.name });
633
- console.log(chalk.gray('\n👋 Goodbye!\n'));
634
- rl.close();
340
+ screen.destroy();
635
341
  process.exit(0);
636
342
  }
637
343
 
638
- // Run pre-message hooks
344
+ // Normal mesaj
639
345
  userMessage = await runHooks('pre-message', userMessage, { botId: bot.id, botName: bot.name });
346
+ messageBox.log(`{white-fg}You{/} ${userMessage}`);
347
+ messageBox.setScrollPerc(100);
348
+ screen.render();
640
349
 
641
- // Loading animasyonu
642
- const loadingInterval = startLoadingAnimation();
350
+ startLoading();
643
351
 
644
352
  try {
645
- // Get tool definitions
646
353
  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
- }
354
+ let response = await _sendMessage(apiKey || config.providerApiKey, bot.id, userMessage, conversationId, systemPrompt, toolDefinitions);
355
+ stopLoading();
356
+
357
+ if (response.conversation_id) conversationId = response.conversation_id;
656
358
 
657
- // Tool execution loop
658
- let maxIterations = 5;
659
- let iteration = 0;
660
-
661
- while (iteration < maxIterations) {
662
- // Check for tool calls
359
+ // Tool loop
360
+ let iter = 0;
361
+ while (iter < 5) {
663
362
  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
363
+ if (!toolCalls?.length) break;
364
+ messageBox.log(`{yellow-fg}🔧 ${toolCalls.length} tool çalıştırılıyor...{/}`);
365
+ screen.render();
673
366
  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'));
367
+ const toolMsg = toolResults.map(tr => `Tool: ${tr.name}\nResult: ${tr.result.success ? (tr.result.output || '') : tr.result.error}`).join('\n\n');
368
+ startLoading();
369
+ response = await _sendMessage(apiKey || config.providerApiKey, bot.id, toolMsg, conversationId, systemPrompt, toolDefinitions);
370
+ stopLoading();
371
+ if (response.conversation_id) conversationId = response.conversation_id;
372
+ iter++;
699
373
  }
700
374
 
701
375
  let botReply = response.reply || response.message || 'No response';
702
-
703
- // Run post-message hooks
704
376
  botReply = await runHooks('post-message', botReply, { botId: bot.id, botName: bot.name });
705
-
706
- console.log(chalk.cyan(`${displayBotName} `) + botReply + '\n');
707
377
 
708
- // Save to history and session
378
+ messageBox.log(`{cyan-fg}${displayBotName}{/} ${botReply}`);
379
+ messageBox.log('');
380
+ messageBox.setScrollPerc(100);
381
+ screen.render();
382
+
709
383
  addToHistory(bot.id, userMessage, botReply, conversationId);
710
384
  addMessageToSession(bot.id, session.id, userMessage, botReply);
711
385
 
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
- }
386
+ const memEntries = extractMemoryFromMessage(userMessage);
387
+ for (const e of memEntries) addMemoryEntry(bot.id, e.key, e.value);
388
+
718
389
  } 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`));
390
+ stopLoading();
391
+ const errMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
392
+ messageBox.log(`{red-fg}Error: ${errMsg}{/}`);
393
+ messageBox.setScrollPerc(100);
394
+ screen.render();
723
395
  }
724
396
 
725
- currentMessage = '';
726
- rl.prompt();
397
+ inputBox.focus();
727
398
  }
728
399
 
729
- rl.on('line', (line) => {
730
- processMessage(line);
400
+ // ── Enter tuşu ──────────────────────────────────────────────────────────────
401
+ inputBox.key('enter', async () => {
402
+ const msg = inputBox.getValue();
403
+ inputBox.clearValue();
404
+ screen.render();
405
+ await handleMessage(msg);
406
+ inputBox.focus();
407
+ screen.render();
731
408
  });
732
409
 
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
- });
410
+ // ── Ctrl+C çıkış ────────────────────────────────────────────────────────────
411
+ screen.key(['C-c'], async () => {
412
+ await runHooks('on-exit', null, { botId: bot.id, botName: bot.name });
413
+ screen.destroy();
414
+ process.exit(0);
738
415
  });
739
416
 
740
- rl.prompt();
741
- }
417
+ // ── on-start hooks ──────────────────────────────────────────────────────────
418
+ await runHooks('on-start', null, { botId: bot.id, botName: bot.name });
742
419
 
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);
420
+ screen.render();
754
421
  }
755
422
 
756
- function stopLoadingAnimation(interval) {
757
- clearInterval(interval);
758
- process.stdout.write('\r');
423
+ // ── Fallback: blessed yoksa eski readline tabanlı chat ────────────────────────
424
+ async function chatFallback(bot, botList, displayBotName, userName, shortModel, apiKey, config, systemPrompt, session, conversationId, options) {
425
+ const readline = require('readline');
426
+ const chalk = require('chalk');
427
+
428
+ console.clear();
429
+ console.log(chalk.green.bold(' (\\_/)'));
430
+ console.log(chalk.green.bold(' (•ᴥ•)'));
431
+ console.log(chalk.green(' />🌿'));
432
+ console.log('');
433
+ const sep = chalk.gray('─'.repeat(process.stdout.columns || 80));
434
+ console.log(sep);
435
+ console.log(chalk.white.bold('NatureCo') + chalk.gray(' · ') + chalk.cyan(displayBotName) + chalk.gray(' · ') + chalk.gray(shortModel));
436
+ console.log(sep);
437
+ console.log('');
438
+
439
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: chalk.gray('You ') });
440
+ rl.prompt();
441
+
442
+ rl.on('line', async (line) => {
443
+ const msg = line.trim();
444
+ if (!msg) { rl.prompt(); return; }
445
+ if (msg === 'exit' || msg === 'quit') { console.log(chalk.gray('\n👋 Goodbye!\n')); process.exit(0); }
446
+
447
+ process.stdout.write(chalk.cyan('... '));
448
+ try {
449
+ const toolDefinitions = getToolDefinitions();
450
+ const response = await _sendMessage(apiKey || config.providerApiKey, bot.id, msg, conversationId, systemPrompt, toolDefinitions);
451
+ process.stdout.write('\r');
452
+ if (response.conversation_id) conversationId = response.conversation_id;
453
+ const reply = response.reply || response.message || '';
454
+ console.log(chalk.cyan(`${displayBotName} `) + reply + '\n');
455
+ addToHistory(bot.id, msg, reply, conversationId);
456
+ addMessageToSession(bot.id, session.id, msg, reply);
457
+ } catch (e) {
458
+ process.stdout.write('\r');
459
+ console.log(chalk.red(`Error: ${e.message}\n`));
460
+ }
461
+ rl.prompt();
462
+ });
463
+
464
+ rl.on('close', () => process.exit(0));
759
465
  }
760
466
 
761
467
  module.exports = chat;
@@ -211,7 +211,7 @@ body::before{
211
211
  <div class="header-bot-name" id="header-bot-name">Nature Bot</div>
212
212
  <div class="header-bot-model" id="header-bot-model">NatureCo</div>
213
213
  </div>
214
- <div class="version-badge" id="version-badge">v2.17.2</div>
214
+ <div class="version-badge" id="version-badge">v2.17.6</div>
215
215
  </div>
216
216
  <div class="messages" id="messages"></div>
217
217
  <div class="input-area">
@@ -341,7 +341,7 @@ function dashboard(action) {
341
341
  apiKey: cfg.apiKey,
342
342
  defaultBot: cfg.defaultBot,
343
343
  defaultBotId: cfg.defaultBotId,
344
- version: 'v2.17.2',
344
+ version: 'v2.17.6',
345
345
  bots: cfg.bots || [],
346
346
  telegramToken: cfg.telegramToken || null,
347
347
  whatsappConnected: cfg.whatsappConnected || false,
@@ -322,9 +322,10 @@ async function startWhatsAppProvider(sessionDir, config) {
322
322
  log('whatsapp', `Inbound message +${sender} -> +${ownNumber} (${messageText.length} chars)`, 'cyan');
323
323
 
324
324
  try {
325
- // v2.x: Send to universal provider (Groq/OpenAI/Anthropic)
326
325
  const { sendMessage } = require('../utils/api');
327
326
  const { getMemoryPrompt, extractMemoryFromMessage, addMemoryEntry } = require('../utils/memory');
327
+ const { getConfig } = require('../utils/config');
328
+ const cfg = getConfig();
328
329
 
329
330
  log('whatsapp', 'Sending to AI provider...', 'cyan');
330
331
 
@@ -342,7 +343,7 @@ async function startWhatsAppProvider(sessionDir, config) {
342
343
  systemPrompt += '\n\n' + memoryPrompt;
343
344
  }
344
345
 
345
- const response = await sendMessage(null, null, messageText, conversationId, systemPrompt);
346
+ const response = await sendMessage(cfg.providerApiKey || cfg.apiKey || '', botId, messageText, conversationId, systemPrompt);
346
347
  const reply = response?.reply || response?.message || '';
347
348
 
348
349
  if (reply) {
@@ -417,6 +418,8 @@ async function startTelegramProvider(config) {
417
418
  try {
418
419
  const { sendMessage } = require('../utils/api');
419
420
  const { getMemoryPrompt, extractMemoryFromMessage, addMemoryEntry } = require('../utils/memory');
421
+ const { getConfig } = require('../utils/config');
422
+ const cfg = getConfig();
420
423
 
421
424
  log('telegram', 'Sending to AI provider...', 'cyan');
422
425
 
@@ -434,7 +437,7 @@ async function startTelegramProvider(config) {
434
437
  systemPrompt += '\n\n' + memoryPrompt;
435
438
  }
436
439
 
437
- const response = await sendMessage(null, null, messageText, conversationId, systemPrompt);
440
+ const response = await sendMessage(cfg.providerApiKey || cfg.apiKey || '', botId, messageText, conversationId, systemPrompt);
438
441
  const reply = response?.reply || response?.message || '';
439
442
 
440
443
  if (reply) {