natureco-cli 2.18.2 → 2.19.1
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/bin/natureco.js +49 -0
- package/package.json +1 -1
- package/src/commands/code.js +265 -101
- package/src/utils/api.js +26 -13
package/bin/natureco.js
CHANGED
|
@@ -21,6 +21,55 @@ program
|
|
|
21
21
|
.description('NatureCo AI Bot Terminal Interface')
|
|
22
22
|
.version(packageJson.version);
|
|
23
23
|
|
|
24
|
+
program.addHelpText('after', `
|
|
25
|
+
${chalk.yellow('🤖 AI & Chat')}
|
|
26
|
+
${chalk.cyan('chat')} AI sohbet başlat
|
|
27
|
+
${chalk.cyan('code')} Code agent — dosya oku, yaz, komut çalıştır
|
|
28
|
+
${chalk.cyan('ask')} Tek soru sor
|
|
29
|
+
${chalk.cyan('run')} Markdown script çalıştır
|
|
30
|
+
${chalk.cyan('bots')} Bot listesi
|
|
31
|
+
|
|
32
|
+
${chalk.yellow('⚙️ Kurulum & Ayarlar')}
|
|
33
|
+
${chalk.cyan('setup')} İlk kurulum sihirbazı
|
|
34
|
+
${chalk.cyan('login')} API key ile giriş
|
|
35
|
+
${chalk.cyan('logout')} Çıkış
|
|
36
|
+
${chalk.cyan('config')} Ayarları yönet
|
|
37
|
+
${chalk.cyan('doctor')} Sistem sağlık kontrolü
|
|
38
|
+
${chalk.cyan('update')} Güncelleme kontrol
|
|
39
|
+
|
|
40
|
+
${chalk.yellow('🔌 Entegrasyonlar')}
|
|
41
|
+
${chalk.cyan('telegram')} Telegram bağlantısı
|
|
42
|
+
${chalk.cyan('whatsapp')} WhatsApp bağlantısı
|
|
43
|
+
${chalk.cyan('discord')} Discord bağlantısı
|
|
44
|
+
${chalk.cyan('slack')} Slack bağlantısı
|
|
45
|
+
${chalk.cyan('gateway')} WebSocket gateway
|
|
46
|
+
|
|
47
|
+
${chalk.yellow('🛠️ Geliştirici Araçları')}
|
|
48
|
+
${chalk.cyan('git')} Git entegrasyonu
|
|
49
|
+
${chalk.cyan('skills')} Skill yönetimi
|
|
50
|
+
${chalk.cyan('mcp')} MCP sunucuları
|
|
51
|
+
${chalk.cyan('hooks')} Hook yönetimi
|
|
52
|
+
${chalk.cyan('cron')} Zamanlanmış görevler
|
|
53
|
+
${chalk.cyan('sessions')} Oturum yönetimi
|
|
54
|
+
${chalk.cyan('ultrareview')} Detaylı kod inceleme
|
|
55
|
+
|
|
56
|
+
${chalk.yellow('📊 Yönetim')}
|
|
57
|
+
${chalk.cyan('dashboard')} Web kontrol paneli
|
|
58
|
+
${chalk.cyan('memory')} Hafıza yönetimi
|
|
59
|
+
${chalk.cyan('logs')} Gateway logları
|
|
60
|
+
${chalk.cyan('status')} Sistem durumu
|
|
61
|
+
${chalk.cyan('agents')} Agent yönetimi
|
|
62
|
+
${chalk.cyan('plugins')} Plugin yönetimi
|
|
63
|
+
${chalk.cyan('security')} Güvenlik denetimi
|
|
64
|
+
${chalk.cyan('reset')} Sıfırlama
|
|
65
|
+
|
|
66
|
+
${chalk.yellow('Örnekler:')}
|
|
67
|
+
${chalk.gray('$ natureco setup')} İlk kurulum
|
|
68
|
+
${chalk.gray('$ natureco chat')} Sohbet başlat
|
|
69
|
+
${chalk.gray('$ natureco code')} Code agent
|
|
70
|
+
${chalk.gray('$ natureco doctor')} Sistem kontrolü
|
|
71
|
+
`);
|
|
72
|
+
|
|
24
73
|
program
|
|
25
74
|
.command('login')
|
|
26
75
|
.description('Login with your NatureCo API key')
|
package/package.json
CHANGED
package/src/commands/code.js
CHANGED
|
@@ -5,14 +5,14 @@ const readline = require('readline');
|
|
|
5
5
|
const inquirer = require('inquirer');
|
|
6
6
|
const chalk = require('chalk');
|
|
7
7
|
const { getApiKey, getConfig } = require('../utils/config');
|
|
8
|
-
const { getBots,
|
|
8
|
+
const { getBots, getProviderConfig, startMcpServers } = require('../utils/api');
|
|
9
9
|
const { getMemoryPrompt, loadMemory } = require('../utils/memory');
|
|
10
10
|
const { getAgentsPrompt } = require('../utils/agents');
|
|
11
11
|
const { createSession, addMessageToSession } = require('../utils/sessions');
|
|
12
12
|
const { addToHistory } = require('../utils/history');
|
|
13
|
-
const { getToolDefinitions,
|
|
14
|
-
const { extractToolCalls } = require('../utils/tool-adapter');
|
|
13
|
+
const { getToolDefinitions, executeTool } = require('../utils/tool-runner');
|
|
15
14
|
|
|
15
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
16
16
|
const sep = () => chalk.gray('─'.repeat(process.stdout.columns || 120));
|
|
17
17
|
|
|
18
18
|
function centerText(text) {
|
|
@@ -23,11 +23,141 @@ function centerText(text) {
|
|
|
23
23
|
}).join('\n');
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
const
|
|
27
|
-
Her adımı açıkça belirt. Değişiklik yapmadan önce plan sun.
|
|
28
|
-
Türkçe konuş.
|
|
29
|
-
Mevcut çalışma dizini: ${process.cwd()}`;
|
|
26
|
+
const SPINNER_FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
30
27
|
|
|
28
|
+
function startSpinner(label) {
|
|
29
|
+
let i = 0;
|
|
30
|
+
return setInterval(() => {
|
|
31
|
+
process.stdout.write(`\r ${chalk.cyan(SPINNER_FRAMES[i++ % SPINNER_FRAMES.length])} ${chalk.gray(label)}`);
|
|
32
|
+
}, 80);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stopSpinner(timer, label, success = true) {
|
|
36
|
+
clearInterval(timer);
|
|
37
|
+
process.stdout.write(`\r ${success ? chalk.green('✓') : chalk.red('✗')} ${chalk.cyan(label)}\n`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Streaming fetch ───────────────────────────────────────────────────────────
|
|
41
|
+
async function streamMessage(providerConfig, messages, tools) {
|
|
42
|
+
const endpoint = `${providerConfig.url}/chat/completions`;
|
|
43
|
+
const body = {
|
|
44
|
+
model: providerConfig.model,
|
|
45
|
+
messages,
|
|
46
|
+
tools,
|
|
47
|
+
tool_choice: 'auto',
|
|
48
|
+
temperature: 0.7,
|
|
49
|
+
max_tokens: 4000,
|
|
50
|
+
stream: true,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const response = await fetch(endpoint, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: {
|
|
56
|
+
'Authorization': `Bearer ${providerConfig.apiKey}`,
|
|
57
|
+
'Content-Type': 'application/json',
|
|
58
|
+
},
|
|
59
|
+
body: JSON.stringify(body),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
const err = await response.text();
|
|
64
|
+
throw new Error(`API error: ${response.status} — ${err}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const reader = response.body.getReader();
|
|
68
|
+
const decoder = new TextDecoder();
|
|
69
|
+
let fullText = '';
|
|
70
|
+
let toolCallsBuffer = {}; // id -> { name, arguments }
|
|
71
|
+
|
|
72
|
+
process.stdout.write('\n');
|
|
73
|
+
|
|
74
|
+
while (true) {
|
|
75
|
+
const { done, value } = await reader.read();
|
|
76
|
+
if (done) break;
|
|
77
|
+
|
|
78
|
+
const chunk = decoder.decode(value);
|
|
79
|
+
const lines = chunk.split('\n').filter(l => l.startsWith('data: '));
|
|
80
|
+
|
|
81
|
+
for (const line of lines) {
|
|
82
|
+
const data = line.slice(6).trim();
|
|
83
|
+
if (data === '[DONE]') continue;
|
|
84
|
+
try {
|
|
85
|
+
const json = JSON.parse(data);
|
|
86
|
+
const delta = json.choices?.[0]?.delta;
|
|
87
|
+
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
|
|
96
|
+
if (delta.tool_calls) {
|
|
97
|
+
for (const tc of delta.tool_calls) {
|
|
98
|
+
const idx = tc.index ?? 0;
|
|
99
|
+
if (!toolCallsBuffer[idx]) {
|
|
100
|
+
toolCallsBuffer[idx] = { id: tc.id || '', name: '', arguments: '' };
|
|
101
|
+
}
|
|
102
|
+
if (tc.id) toolCallsBuffer[idx].id = tc.id;
|
|
103
|
+
if (tc.function?.name) toolCallsBuffer[idx].name += tc.function.name;
|
|
104
|
+
if (tc.function?.arguments) toolCallsBuffer[idx].arguments += tc.function.arguments;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} catch {}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
process.stdout.write('\n');
|
|
112
|
+
|
|
113
|
+
const toolCalls = Object.values(toolCallsBuffer)
|
|
114
|
+
.filter(tc => tc.name)
|
|
115
|
+
.map(tc => ({
|
|
116
|
+
id: tc.id,
|
|
117
|
+
name: tc.name,
|
|
118
|
+
input: (() => { try { return JSON.parse(tc.arguments); } catch { return {}; } })(),
|
|
119
|
+
}));
|
|
120
|
+
|
|
121
|
+
return { text: fullText, toolCalls };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── Tool execution with spinner + confirm ─────────────────────────────────────
|
|
125
|
+
const DANGEROUS = ['rm ', 'rmdir', 'delete', 'DROP', 'truncate'];
|
|
126
|
+
|
|
127
|
+
async function runToolCall(toolCall, stats) {
|
|
128
|
+
const inputPreview = JSON.stringify(toolCall.input).slice(0, 60);
|
|
129
|
+
const label = `${toolCall.name} ${chalk.gray(inputPreview)}`;
|
|
130
|
+
|
|
131
|
+
const needsConfirm =
|
|
132
|
+
toolCall.name === 'write_file' ||
|
|
133
|
+
(toolCall.name === 'bash' && DANGEROUS.some(d => toolCall.input.command?.includes(d)));
|
|
134
|
+
|
|
135
|
+
if (needsConfirm) {
|
|
136
|
+
console.log(chalk.yellow(`\n ⚠️ ${toolCall.name}: ${inputPreview}`));
|
|
137
|
+
const { ok } = await inquirer.prompt([{
|
|
138
|
+
type: 'confirm', name: 'ok',
|
|
139
|
+
message: ' Devam edilsin mi?', default: true,
|
|
140
|
+
}]);
|
|
141
|
+
if (!ok) {
|
|
142
|
+
console.log(chalk.gray(' İptal edildi.\n'));
|
|
143
|
+
return { success: false, output: 'Kullanıcı iptal etti.' };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const spinner = startSpinner(`${toolCall.name} ${inputPreview}`);
|
|
148
|
+
const result = await executeTool(toolCall.name, toolCall.input);
|
|
149
|
+
stopSpinner(spinner, toolCall.name, result.success !== false);
|
|
150
|
+
|
|
151
|
+
if (result.success !== false) {
|
|
152
|
+
if (toolCall.name === 'write_file') stats.filesChanged++;
|
|
153
|
+
if (toolCall.name === 'bash') stats.commandsRun++;
|
|
154
|
+
stats.toolCallCount++;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return result;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
31
161
|
async function code(targetFile, options = {}) {
|
|
32
162
|
const apiKey = getApiKey();
|
|
33
163
|
const config = getConfig();
|
|
@@ -36,27 +166,25 @@ async function code(targetFile, options = {}) {
|
|
|
36
166
|
// ── Bot seçimi ──────────────────────────────────────────────────────────────
|
|
37
167
|
let botList;
|
|
38
168
|
try {
|
|
39
|
-
botList = await
|
|
169
|
+
botList = await getBots(apiKey || config.providerApiKey || '');
|
|
40
170
|
} catch (err) {
|
|
41
171
|
console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
|
|
42
172
|
process.exit(1);
|
|
43
173
|
}
|
|
44
174
|
|
|
45
175
|
if (!botList?.bots?.length) {
|
|
46
|
-
console.log(chalk.gray('No bots found
|
|
176
|
+
console.log(chalk.gray('No bots found.\n'));
|
|
47
177
|
process.exit(1);
|
|
48
178
|
}
|
|
49
179
|
|
|
50
180
|
let bot;
|
|
51
181
|
if (config.botName) {
|
|
52
|
-
bot = botList.bots.find(b => b.name
|
|
182
|
+
bot = botList.bots.find(b => b.name?.toLowerCase() === config.botName.toLowerCase());
|
|
53
183
|
}
|
|
54
184
|
if (!bot) {
|
|
55
185
|
process.stdin.resume();
|
|
56
186
|
const { selectedBot } = await inquirer.prompt([{
|
|
57
|
-
type: 'list',
|
|
58
|
-
name: 'selectedBot',
|
|
59
|
-
message: 'Bot seçin:',
|
|
187
|
+
type: 'list', name: 'selectedBot', message: 'Bot seçin:',
|
|
60
188
|
choices: botList.bots.map(b => ({ name: b.name, value: b.id })),
|
|
61
189
|
}]);
|
|
62
190
|
bot = botList.bots.find(b => b.id === selectedBot);
|
|
@@ -65,13 +193,35 @@ async function code(targetFile, options = {}) {
|
|
|
65
193
|
const mem = loadMemory(bot.id);
|
|
66
194
|
const displayBotName = mem.botName || bot.name || 'NatureCo';
|
|
67
195
|
const userName = mem.name || config.userName || 'User';
|
|
68
|
-
const
|
|
69
|
-
|
|
196
|
+
const providerConfig = getProviderConfig();
|
|
197
|
+
|
|
198
|
+
if (!providerConfig) {
|
|
199
|
+
console.log(chalk.red('\n❌ Provider yapılandırılmamış. natureco config set providerUrl ...\n'));
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
70
202
|
|
|
203
|
+
const shortModel = providerConfig.model.split('/').pop().split('-').slice(0, 3).join('-');
|
|
204
|
+
|
|
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 {}
|
|
213
|
+
|
|
214
|
+
// ── Sistem prompt ────────────────────────────────────────────────────────────
|
|
71
215
|
const agentsPrompt = getAgentsPrompt();
|
|
72
216
|
const memoryPrompt = getMemoryPrompt(bot.id);
|
|
73
217
|
|
|
74
|
-
let systemPrompt =
|
|
218
|
+
let systemPrompt = `Sen NatureCo Code Agent'sın — güçlü bir AI coding asistanı.
|
|
219
|
+
Kullanıcının projesinde gerçekten çalış: dosyaları oku, değiştir, komutları çalıştır.
|
|
220
|
+
Her adımı Türkçe açıkla. Değişiklik yapmadan önce ne yapacağını söyle.
|
|
221
|
+
Hataları yakala ve düzelt. İş bitince özet sun.
|
|
222
|
+
Mevcut dizin: ${process.cwd()}
|
|
223
|
+
Kullanıcı: ${userName}${projectContext}`;
|
|
224
|
+
|
|
75
225
|
if (agentsPrompt) systemPrompt += `\n\n## Project Instructions\n${agentsPrompt}`;
|
|
76
226
|
if (memoryPrompt) systemPrompt += '\n\n' + memoryPrompt;
|
|
77
227
|
|
|
@@ -79,16 +229,30 @@ async function code(targetFile, options = {}) {
|
|
|
79
229
|
if (targetFile) {
|
|
80
230
|
try {
|
|
81
231
|
const content = fs.readFileSync(path.resolve(targetFile), 'utf-8');
|
|
82
|
-
systemPrompt += `\n\n## Odak Dosyası: ${targetFile}\n\`\`\`\n${content}\n\`\`\``;
|
|
232
|
+
systemPrompt += `\n\n## Odak Dosyası: ${targetFile}\n\`\`\`\n${content.slice(0, 2000)}\n\`\`\``;
|
|
83
233
|
} catch {
|
|
84
|
-
console.log(chalk.yellow(` ⚠️ ${targetFile} okunamad
|
|
234
|
+
console.log(chalk.yellow(` ⚠️ ${targetFile} okunamadı\n`));
|
|
85
235
|
}
|
|
86
236
|
}
|
|
87
237
|
|
|
238
|
+
// ── Session ──────────────────────────────────────────────────────────────────
|
|
88
239
|
const session = createSession(bot.id, bot.name);
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
240
|
+
const conversationMessages = [{ role: 'system', content: systemPrompt }];
|
|
241
|
+
const stats = { filesChanged: 0, commandsRun: 0, toolCallCount: 0, messageCount: 0 };
|
|
242
|
+
|
|
243
|
+
// ── MCP sunucularını başlat ──────────────────────────────────────────────────
|
|
244
|
+
await startMcpServers().catch(() => {});
|
|
245
|
+
|
|
246
|
+
// ── Tool definitions ─────────────────────────────────────────────────────────
|
|
247
|
+
const localTools = getToolDefinitions();
|
|
248
|
+
const tools = localTools.map(t => ({
|
|
249
|
+
type: 'function',
|
|
250
|
+
function: {
|
|
251
|
+
name: t.name,
|
|
252
|
+
description: t.description,
|
|
253
|
+
parameters: t.inputSchema || { type: 'object', properties: {} },
|
|
254
|
+
},
|
|
255
|
+
}));
|
|
92
256
|
|
|
93
257
|
// ── Header ──────────────────────────────────────────────────────────────────
|
|
94
258
|
console.clear();
|
|
@@ -99,136 +263,136 @@ async function code(targetFile, options = {}) {
|
|
|
99
263
|
'██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗██╔══╝ ██║ ██║ ██║',
|
|
100
264
|
'██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████╗╚██████╗ ╚██████╔╝',
|
|
101
265
|
'╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝',
|
|
102
|
-
|
|
103
|
-
const codeSubLogo = [
|
|
266
|
+
'',
|
|
104
267
|
'█▀▀ █▀█ █▀▄ █▀▀',
|
|
105
268
|
'█▄▄ █▄█ █▄▀ ██▄',
|
|
106
269
|
].join('\n');
|
|
107
270
|
console.log(centerText(chalk.green(codeLogo)));
|
|
108
|
-
console.log(
|
|
271
|
+
console.log();
|
|
272
|
+
console.log(centerText(chalk.cyan(`(\\_/) ${displayBotName} hazır`) + chalk.gray(` · v${version}`)));
|
|
109
273
|
console.log(sep());
|
|
110
|
-
if (targetFile) {
|
|
111
|
-
|
|
112
|
-
console.log(sep());
|
|
113
|
-
}
|
|
114
|
-
console.log(centerText(chalk.gray(`${shortModel} · /help için yardım · Ctrl+C çıkış`)));
|
|
274
|
+
if (targetFile) console.log(centerText(chalk.gray(`📄 ${targetFile}`)));
|
|
275
|
+
console.log(centerText(chalk.gray(`${shortModel} · /help · Ctrl+C çıkış`)));
|
|
115
276
|
console.log(sep());
|
|
116
277
|
console.log();
|
|
117
278
|
|
|
118
|
-
// ── Yükleme animasyonu ──────────────────────────────────────────────────────
|
|
119
|
-
const loadingFrames = ['●○○', '○●○', '○○●'];
|
|
120
|
-
let loadingFrame = 0;
|
|
121
|
-
let loadingTimer = null;
|
|
122
|
-
|
|
123
|
-
function startLoading() {
|
|
124
|
-
loadingFrame = 0;
|
|
125
|
-
process.stdout.write(chalk.gray(' ' + loadingFrames[0]));
|
|
126
|
-
loadingTimer = setInterval(() => {
|
|
127
|
-
process.stdout.write('\r' + chalk.gray(' ' + loadingFrames[loadingFrame]));
|
|
128
|
-
loadingFrame = (loadingFrame + 1) % loadingFrames.length;
|
|
129
|
-
}, 300);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function stopLoading() {
|
|
133
|
-
if (loadingTimer) { clearInterval(loadingTimer); loadingTimer = null; }
|
|
134
|
-
process.stdout.write('\r\x1b[2K');
|
|
135
|
-
}
|
|
136
|
-
|
|
137
279
|
// ── Session özeti ─────────────────────────────────────────────────────────
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
console.log(chalk.
|
|
141
|
-
console.log(chalk.green(
|
|
142
|
-
console.log(chalk.green(
|
|
143
|
-
console.log(chalk.cyan(
|
|
144
|
-
console.log();
|
|
280
|
+
function showSummary() {
|
|
281
|
+
console.log(chalk.gray('\n─── Agent Session Özeti ───'));
|
|
282
|
+
console.log(` ${chalk.green('✓')} ${stats.filesChanged} dosya değiştirildi`);
|
|
283
|
+
console.log(` ${chalk.green('✓')} ${stats.commandsRun} komut çalıştırıldı`);
|
|
284
|
+
console.log(` ${chalk.green('✓')} ${stats.toolCallCount} tool çağrısı yapıldı`);
|
|
285
|
+
console.log(` ${chalk.cyan('◉')} ${stats.messageCount} mesaj\n`);
|
|
145
286
|
}
|
|
146
287
|
|
|
147
|
-
// ── Mesaj
|
|
288
|
+
// ── Mesaj gönder + tool loop ──────────────────────────────────────────────
|
|
148
289
|
async function handleMessage(userMessage) {
|
|
149
290
|
userMessage = userMessage.trim();
|
|
150
291
|
if (!userMessage) return;
|
|
151
292
|
|
|
293
|
+
// Slash komutları
|
|
152
294
|
if (userMessage.startsWith('/')) {
|
|
153
295
|
const [cmd] = userMessage.slice(1).split(' ');
|
|
154
296
|
switch (cmd.toLowerCase()) {
|
|
155
297
|
case 'help':
|
|
156
|
-
console.log(chalk.yellow('Code Komutları:'));
|
|
298
|
+
console.log(chalk.yellow('Code Agent Komutları:'));
|
|
157
299
|
[
|
|
158
|
-
['/clear',
|
|
300
|
+
['/clear', 'Ekranı temizle'],
|
|
159
301
|
['/summary', 'Session özetini göster'],
|
|
160
|
-
['/
|
|
161
|
-
|
|
162
|
-
console.log(
|
|
302
|
+
['/done', 'Bitir ve özet göster'],
|
|
303
|
+
['/help', 'Bu yardım'],
|
|
304
|
+
].forEach(([c, d]) => console.log(' ' + chalk.cyan(c.padEnd(12)) + chalk.gray(d)));
|
|
305
|
+
console.log(chalk.gray(' Ctrl+C'.padEnd(14) + 'Çıkış'));
|
|
163
306
|
console.log();
|
|
164
307
|
return;
|
|
165
308
|
case 'clear':
|
|
166
309
|
console.clear();
|
|
167
310
|
return;
|
|
168
311
|
case 'summary':
|
|
169
|
-
|
|
312
|
+
case 'done':
|
|
313
|
+
showSummary();
|
|
314
|
+
if (cmd === 'done') process.exit(0);
|
|
170
315
|
return;
|
|
171
316
|
default:
|
|
172
|
-
console.log(chalk.red(`Bilinmeyen komut: /${cmd}`));
|
|
173
|
-
console.log();
|
|
317
|
+
console.log(chalk.red(`Bilinmeyen komut: /${cmd}\n`));
|
|
174
318
|
return;
|
|
175
319
|
}
|
|
176
320
|
}
|
|
177
321
|
|
|
178
322
|
if (userMessage === 'exit' || userMessage === 'quit') {
|
|
179
|
-
|
|
323
|
+
showSummary();
|
|
180
324
|
process.exit(0);
|
|
181
325
|
}
|
|
182
326
|
|
|
183
|
-
|
|
327
|
+
stats.messageCount++;
|
|
184
328
|
console.log(chalk.white('You ') + userMessage);
|
|
185
|
-
startLoading();
|
|
186
329
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
let
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
).join('\n\n');
|
|
204
|
-
startLoading();
|
|
205
|
-
response = await _sendMessage(apiKey || config.providerApiKey, bot.id, toolMsg, conversationId, systemPrompt, toolDefinitions);
|
|
206
|
-
stopLoading();
|
|
207
|
-
if (response.conversation_id) conversationId = response.conversation_id;
|
|
208
|
-
iter++;
|
|
330
|
+
// Mesajı history'e ekle
|
|
331
|
+
conversationMessages.push({ role: 'user', content: userMessage });
|
|
332
|
+
|
|
333
|
+
// ── Streaming + tool loop (max 20 iter) ──────────────────────────────────
|
|
334
|
+
let iter = 0;
|
|
335
|
+
while (iter < 20) {
|
|
336
|
+
iter++;
|
|
337
|
+
|
|
338
|
+
let streamResult;
|
|
339
|
+
try {
|
|
340
|
+
process.stdout.write(chalk.cyan(`${displayBotName} `));
|
|
341
|
+
streamResult = await streamMessage(providerConfig, conversationMessages, tools);
|
|
342
|
+
} catch (err) {
|
|
343
|
+
console.log(chalk.red(`\nError: ${err.message}\n`));
|
|
344
|
+
conversationMessages.pop(); // başarısız user mesajını geri al
|
|
345
|
+
return;
|
|
209
346
|
}
|
|
210
347
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
348
|
+
// Assistant mesajını history'e ekle
|
|
349
|
+
const assistantMsg = { role: 'assistant', content: streamResult.text || '' };
|
|
350
|
+
if (streamResult.toolCalls?.length) {
|
|
351
|
+
assistantMsg.tool_calls = streamResult.toolCalls.map(tc => ({
|
|
352
|
+
id: tc.id || `call_${Date.now()}_${Math.random().toString(36).slice(2)}`,
|
|
353
|
+
type: 'function',
|
|
354
|
+
function: { name: tc.name, arguments: JSON.stringify(tc.input) },
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
357
|
+
conversationMessages.push(assistantMsg);
|
|
358
|
+
|
|
359
|
+
// Tool call yoksa bitti
|
|
360
|
+
if (!streamResult.toolCalls?.length) break;
|
|
361
|
+
|
|
362
|
+
// Tool call'ları çalıştır
|
|
363
|
+
console.log(chalk.yellow(`\n🔧 ${streamResult.toolCalls.length} tool çalıştırılıyor...\n`));
|
|
364
|
+
|
|
365
|
+
for (const toolCall of streamResult.toolCalls) {
|
|
366
|
+
const result = await runToolCall(toolCall, stats);
|
|
367
|
+
const resultStr = result.success !== false
|
|
368
|
+
? (result.output || JSON.stringify(result))
|
|
369
|
+
: `Hata: ${result.error}`;
|
|
370
|
+
|
|
371
|
+
conversationMessages.push({
|
|
372
|
+
role: 'tool',
|
|
373
|
+
tool_call_id: assistantMsg.tool_calls?.find(tc => tc.function.name === toolCall.name)?.id || toolCall.id,
|
|
374
|
+
name: toolCall.name,
|
|
375
|
+
content: resultStr.slice(0, 3000),
|
|
376
|
+
});
|
|
377
|
+
}
|
|
217
378
|
|
|
218
|
-
} catch (err) {
|
|
219
|
-
stopLoading();
|
|
220
|
-
const errMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
|
|
221
|
-
console.log(chalk.red(`Error: ${errMsg}`));
|
|
222
379
|
console.log();
|
|
223
380
|
}
|
|
381
|
+
|
|
382
|
+
// Session'a kaydet
|
|
383
|
+
const lastAssistant = [...conversationMessages].reverse().find(m => m.role === 'assistant');
|
|
384
|
+
if (lastAssistant?.content) {
|
|
385
|
+
addToHistory(bot.id, userMessage, lastAssistant.content, null);
|
|
386
|
+
addMessageToSession(bot.id, session.id, userMessage, lastAssistant.content);
|
|
387
|
+
}
|
|
224
388
|
}
|
|
225
389
|
|
|
226
390
|
// ── Input loop ───────────────────────────────────────────────────────────────
|
|
227
391
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
228
392
|
|
|
229
|
-
process.on('SIGINT',
|
|
393
|
+
process.on('SIGINT', () => {
|
|
230
394
|
rl.close();
|
|
231
|
-
|
|
395
|
+
showSummary();
|
|
232
396
|
console.log(chalk.gray('👋 Goodbye!\n'));
|
|
233
397
|
process.exit(0);
|
|
234
398
|
});
|
package/src/utils/api.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const os = require('os');
|
|
6
6
|
const path = require('path');
|
|
7
|
+
const chalk = require('chalk');
|
|
7
8
|
const { getConfig } = require('./config');
|
|
8
9
|
const { getToolDefinitions, executeToolCalls } = require('./tool-runner');
|
|
9
10
|
const { MCPClient } = require('./mcp-client');
|
|
@@ -428,8 +429,7 @@ function formatToolsForAnthropic() {
|
|
|
428
429
|
*/
|
|
429
430
|
async function sendMessageOpenAICompatible(providerConfig, messages, tools) {
|
|
430
431
|
const endpoint = `${providerConfig.url}/chat/completions`;
|
|
431
|
-
|
|
432
|
-
// Log request size before sending
|
|
432
|
+
|
|
433
433
|
const requestBody = {
|
|
434
434
|
model: providerConfig.model,
|
|
435
435
|
messages: messages,
|
|
@@ -438,8 +438,12 @@ async function sendMessageOpenAICompatible(providerConfig, messages, tools) {
|
|
|
438
438
|
temperature: 0.7,
|
|
439
439
|
max_tokens: 2000,
|
|
440
440
|
};
|
|
441
|
-
|
|
442
|
-
|
|
441
|
+
|
|
442
|
+
// NatureCo backend proxy — tool call'ları Groq'a iletmek için explicitly set
|
|
443
|
+
if (providerConfig.url.includes('api.natureco.me')) {
|
|
444
|
+
requestBody.tool_choice = 'auto';
|
|
445
|
+
requestBody.tools = tools;
|
|
446
|
+
}
|
|
443
447
|
|
|
444
448
|
const response = await fetch(endpoint, {
|
|
445
449
|
method: 'POST',
|
|
@@ -447,7 +451,7 @@ async function sendMessageOpenAICompatible(providerConfig, messages, tools) {
|
|
|
447
451
|
'Authorization': `Bearer ${providerConfig.apiKey}`,
|
|
448
452
|
'Content-Type': 'application/json',
|
|
449
453
|
},
|
|
450
|
-
body:
|
|
454
|
+
body: JSON.stringify(requestBody),
|
|
451
455
|
});
|
|
452
456
|
|
|
453
457
|
if (!response.ok) {
|
|
@@ -588,27 +592,36 @@ async function sendMessageToProvider(apiKey, message, conversationId = null, sys
|
|
|
588
592
|
}));
|
|
589
593
|
|
|
590
594
|
const toolResults = [];
|
|
595
|
+
const SPINNER_FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
591
596
|
|
|
592
597
|
for (const toolCall of toolCalls) {
|
|
598
|
+
// Spinner başlat
|
|
599
|
+
let frameIdx = 0;
|
|
600
|
+
const inputPreview = JSON.stringify(toolCall.input).slice(0, 50);
|
|
601
|
+
const spinner = setInterval(() => {
|
|
602
|
+
process.stdout.write(`\r ${chalk.cyan(SPINNER_FRAMES[frameIdx++ % SPINNER_FRAMES.length])} ${chalk.gray(toolCall.name + ' — ' + inputPreview)}`);
|
|
603
|
+
}, 80);
|
|
604
|
+
|
|
593
605
|
// Check if this is an MCP tool
|
|
594
606
|
const mcpTools = getMcpTools();
|
|
595
607
|
const isMcpTool = mcpTools.find(t => t.name === toolCall.name);
|
|
608
|
+
let result;
|
|
596
609
|
|
|
597
610
|
if (isMcpTool) {
|
|
598
|
-
// Execute MCP tool
|
|
599
611
|
debugLog(`[MCP] Executing tool: ${toolCall.name}`);
|
|
600
|
-
|
|
601
|
-
toolResults.push({
|
|
602
|
-
id: toolCall.id,
|
|
603
|
-
name: toolCall.name,
|
|
604
|
-
result: result
|
|
605
|
-
});
|
|
612
|
+
result = await executeMcpTool(toolCall.name, toolCall.input);
|
|
613
|
+
toolResults.push({ id: toolCall.id, name: toolCall.name, result });
|
|
606
614
|
} else {
|
|
607
|
-
// Execute local tool
|
|
608
615
|
debugLog(`[Local] Executing tool: ${toolCall.name}`);
|
|
609
616
|
const localResults = await executeToolCalls([toolCall]);
|
|
610
617
|
toolResults.push(...localResults);
|
|
618
|
+
result = localResults[0]?.result;
|
|
611
619
|
}
|
|
620
|
+
|
|
621
|
+
// Spinner durdur, sonucu göster
|
|
622
|
+
clearInterval(spinner);
|
|
623
|
+
const success = result?.success !== false;
|
|
624
|
+
process.stdout.write(`\r ${success ? chalk.green('✓') : chalk.red('✗')} ${chalk.cyan(toolCall.name)} ${chalk.gray('— ' + inputPreview)}\n`);
|
|
612
625
|
}
|
|
613
626
|
|
|
614
627
|
// Add tool results to messages (base64 encoded for safety)
|