natureco-cli 2.17.10 → 2.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/natureco.js +8 -0
- package/package.json +1 -1
- package/src/commands/chat.js +22 -11
- package/src/commands/code.js +236 -0
- package/src/utils/tool-runner.js +113 -43
package/bin/natureco.js
CHANGED
|
@@ -53,6 +53,14 @@ program
|
|
|
53
53
|
chat(botName, options);
|
|
54
54
|
});
|
|
55
55
|
|
|
56
|
+
program
|
|
57
|
+
.command('code [file]')
|
|
58
|
+
.description('Agentic coding modu — dosya oku, değiştir, komut çalıştır')
|
|
59
|
+
.action((file) => {
|
|
60
|
+
const codeCmd = require('../src/commands/code');
|
|
61
|
+
codeCmd(file);
|
|
62
|
+
});
|
|
63
|
+
|
|
56
64
|
program
|
|
57
65
|
.command('init')
|
|
58
66
|
.description('Initialize NatureCo project in current directory')
|
package/package.json
CHANGED
package/src/commands/chat.js
CHANGED
|
@@ -13,7 +13,7 @@ const { getMemoryPrompt, extractMemoryFromMessage, loadMemory, clearMemory, addM
|
|
|
13
13
|
const { getCommands, getCommandContent } = require('../utils/commands');
|
|
14
14
|
const { runHooks } = require('../utils/hooks');
|
|
15
15
|
const { createSession, loadSession, getLatestSession, addMessageToSession } = require('../utils/sessions');
|
|
16
|
-
const { getToolDefinitions, executeToolCalls } = require('../utils/tool-runner');
|
|
16
|
+
const { getToolDefinitions, executeToolCalls, getSessionStats, resetSessionStats } = require('../utils/tool-runner');
|
|
17
17
|
const { extractToolCalls } = require('../utils/tool-adapter');
|
|
18
18
|
|
|
19
19
|
// ── ASCII Logo ────────────────────────────────────────────────────────────────
|
|
@@ -127,6 +127,8 @@ async function chat(botName, options = {}) {
|
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
let conversationId = null;
|
|
130
|
+
let messagesCount = 0;
|
|
131
|
+
resetSessionStats();
|
|
130
132
|
|
|
131
133
|
// ── What's New kontrolü ─────────────────────────────────────────────────────
|
|
132
134
|
const lastVersionFile = path.join(os.homedir(), '.natureco', 'lastVersion');
|
|
@@ -138,22 +140,22 @@ async function chat(botName, options = {}) {
|
|
|
138
140
|
console.clear();
|
|
139
141
|
console.log(centerText(ASCII_LOGO.map((line, i) => i < 5 ? chalk.green(line) : chalk.gray(line)).join('\n')));
|
|
140
142
|
console.log();
|
|
141
|
-
console.log(centerText(chalk.cyan(`(\\_/)
|
|
142
|
-
console.log(
|
|
143
|
+
console.log(centerText(chalk.cyan(`(\\_/) Hoş geldin, ${userName} · ${displayBotName} hazır · v${version}`)));
|
|
144
|
+
console.log(chalk.gray('─'.repeat(process.stdout.columns || 120)));
|
|
143
145
|
console.log();
|
|
144
146
|
|
|
145
147
|
// ── What's New ──────────────────────────────────────────────────────────────
|
|
146
148
|
if (isNewVersion) {
|
|
147
|
-
console.log(chalk.yellow(`── v${version} yenilikleri ──`));
|
|
148
|
-
CHANGELOG.forEach(c => console.log(chalk.gray(
|
|
149
|
+
console.log(centerText(chalk.yellow(`── v${version} yenilikleri ──`)));
|
|
150
|
+
CHANGELOG.forEach(c => console.log(centerText(chalk.gray(`· ${c}`))));
|
|
149
151
|
console.log();
|
|
150
152
|
try { fs.writeFileSync(lastVersionFile, version); } catch {}
|
|
151
153
|
} else {
|
|
152
154
|
// Yeni versiyon yoksa günlük tip göster
|
|
153
155
|
const dayIndex = Math.floor(Date.now() / (1000 * 60 * 60 * 24)) % TIPS.length;
|
|
154
|
-
console.log(
|
|
155
|
-
console.log(
|
|
156
|
-
console.log(
|
|
156
|
+
console.log(chalk.gray('─'.repeat(process.stdout.columns || 120)));
|
|
157
|
+
console.log(centerText(chalk.yellow(TIPS[dayIndex])));
|
|
158
|
+
console.log(chalk.gray('─'.repeat(process.stdout.columns || 120)));
|
|
157
159
|
console.log();
|
|
158
160
|
}
|
|
159
161
|
|
|
@@ -167,8 +169,8 @@ async function chat(botName, options = {}) {
|
|
|
167
169
|
});
|
|
168
170
|
}
|
|
169
171
|
|
|
170
|
-
console.log(chalk.gray(
|
|
171
|
-
console.log(
|
|
172
|
+
console.log(centerText(chalk.gray(`${shortModel} · /help için yardım · Ctrl+C çıkış`)));
|
|
173
|
+
console.log(chalk.gray('─'.repeat(process.stdout.columns || 120)));
|
|
172
174
|
console.log();
|
|
173
175
|
|
|
174
176
|
// ── Yükleme animasyonu ──────────────────────────────────────────────────────
|
|
@@ -299,6 +301,7 @@ async function chat(botName, options = {}) {
|
|
|
299
301
|
// Normal mesaj
|
|
300
302
|
userMessage = await runHooks('pre-message', userMessage, { botId: bot.id, botName: bot.name });
|
|
301
303
|
console.log(chalk.white('You ') + userMessage);
|
|
304
|
+
messagesCount++;
|
|
302
305
|
|
|
303
306
|
startLoading();
|
|
304
307
|
|
|
@@ -350,7 +353,15 @@ async function chat(botName, options = {}) {
|
|
|
350
353
|
process.on('SIGINT', async () => {
|
|
351
354
|
await runHooks('on-exit', null, { botId: bot.id, botName: bot.name });
|
|
352
355
|
rl.close();
|
|
353
|
-
|
|
356
|
+
const { filesChanged, commandsRun } = getSessionStats();
|
|
357
|
+
if (filesChanged > 0 || commandsRun > 0 || messagesCount > 0) {
|
|
358
|
+
console.log(chalk.gray('\n─── Session Özeti ───'));
|
|
359
|
+
if (filesChanged > 0) console.log(chalk.green(` ✓ ${filesChanged} dosya değiştirildi`));
|
|
360
|
+
if (commandsRun > 0) console.log(chalk.green(` ✓ ${commandsRun} komut çalıştırıldı`));
|
|
361
|
+
console.log(chalk.cyan(` ✓ ${messagesCount} mesaj gönderildi`));
|
|
362
|
+
console.log();
|
|
363
|
+
}
|
|
364
|
+
console.log(chalk.gray('👋 Goodbye!\n'));
|
|
354
365
|
process.exit(0);
|
|
355
366
|
});
|
|
356
367
|
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const os = require('os');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const readline = require('readline');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
6
|
+
const chalk = require('chalk');
|
|
7
|
+
const { getApiKey, getConfig } = require('../utils/config');
|
|
8
|
+
const { getBots, _sendMessage } = require('../utils/api');
|
|
9
|
+
const { getMemoryPrompt, loadMemory } = require('../utils/memory');
|
|
10
|
+
const { getAgentsPrompt } = require('../utils/agents');
|
|
11
|
+
const { createSession, addMessageToSession } = require('../utils/sessions');
|
|
12
|
+
const { addToHistory } = require('../utils/history');
|
|
13
|
+
const { getToolDefinitions, executeToolCalls, getSessionStats, resetSessionStats } = require('../utils/tool-runner');
|
|
14
|
+
const { extractToolCalls } = require('../utils/tool-adapter');
|
|
15
|
+
|
|
16
|
+
const sep = () => chalk.gray('─'.repeat(process.stdout.columns || 120));
|
|
17
|
+
|
|
18
|
+
function centerText(text) {
|
|
19
|
+
const width = process.stdout.columns || 120;
|
|
20
|
+
return text.split('\n').map(line => {
|
|
21
|
+
const padding = Math.max(0, Math.floor((width - line.length) / 2));
|
|
22
|
+
return ' '.repeat(padding) + line;
|
|
23
|
+
}).join('\n');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const CODE_SYSTEM_PROMPT = `Sen bir AI coding asistanısın. Kullanıcının projesinde dosyaları oku, değiştir, komut çalıştır.
|
|
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()}`;
|
|
30
|
+
|
|
31
|
+
async function code(targetFile, options = {}) {
|
|
32
|
+
const apiKey = getApiKey();
|
|
33
|
+
const config = getConfig();
|
|
34
|
+
const version = require('../../package.json').version;
|
|
35
|
+
|
|
36
|
+
// ── Bot seçimi ──────────────────────────────────────────────────────────────
|
|
37
|
+
let botList;
|
|
38
|
+
try {
|
|
39
|
+
botList = await (require('../utils/api').getBots)(apiKey || config.providerApiKey || '');
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.log(chalk.red(`\n❌ Error: ${err.message}\n`));
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!botList?.bots?.length) {
|
|
46
|
+
console.log(chalk.gray('No bots found. Create one at https://developers.natureco.me\n'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let bot;
|
|
51
|
+
if (config.botName) {
|
|
52
|
+
bot = botList.bots.find(b => b.name && b.name.toLowerCase() === config.botName.toLowerCase());
|
|
53
|
+
}
|
|
54
|
+
if (!bot) {
|
|
55
|
+
process.stdin.resume();
|
|
56
|
+
const { selectedBot } = await inquirer.prompt([{
|
|
57
|
+
type: 'list',
|
|
58
|
+
name: 'selectedBot',
|
|
59
|
+
message: 'Bot seçin:',
|
|
60
|
+
choices: botList.bots.map(b => ({ name: b.name, value: b.id })),
|
|
61
|
+
}]);
|
|
62
|
+
bot = botList.bots.find(b => b.id === selectedBot);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const mem = loadMemory(bot.id);
|
|
66
|
+
const displayBotName = mem.botName || bot.name || 'NatureCo';
|
|
67
|
+
const userName = mem.name || config.userName || 'User';
|
|
68
|
+
const providerModel = config.providerModel || 'unknown';
|
|
69
|
+
const shortModel = providerModel.split('/').pop().split('-').slice(0, 3).join('-');
|
|
70
|
+
|
|
71
|
+
const agentsPrompt = getAgentsPrompt();
|
|
72
|
+
const memoryPrompt = getMemoryPrompt(bot.id);
|
|
73
|
+
|
|
74
|
+
let systemPrompt = CODE_SYSTEM_PROMPT;
|
|
75
|
+
if (agentsPrompt) systemPrompt += `\n\n## Project Instructions\n${agentsPrompt}`;
|
|
76
|
+
if (memoryPrompt) systemPrompt += '\n\n' + memoryPrompt;
|
|
77
|
+
|
|
78
|
+
// Hedef dosya varsa context'e ekle
|
|
79
|
+
if (targetFile) {
|
|
80
|
+
try {
|
|
81
|
+
const content = fs.readFileSync(path.resolve(targetFile), 'utf-8');
|
|
82
|
+
systemPrompt += `\n\n## Odak Dosyası: ${targetFile}\n\`\`\`\n${content}\n\`\`\``;
|
|
83
|
+
} catch {
|
|
84
|
+
console.log(chalk.yellow(` ⚠️ ${targetFile} okunamadı, devam ediliyor...\n`));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const session = createSession(bot.id, bot.name);
|
|
89
|
+
let conversationId = null;
|
|
90
|
+
let messagesCount = 0;
|
|
91
|
+
resetSessionStats();
|
|
92
|
+
|
|
93
|
+
// ── Header ──────────────────────────────────────────────────────────────────
|
|
94
|
+
console.clear();
|
|
95
|
+
console.log(centerText(chalk.green('⚡ NatureCo Code') + chalk.gray(' · ') + chalk.cyan(`${displayBotName}`) + chalk.gray(` · v${version}`)));
|
|
96
|
+
console.log(sep());
|
|
97
|
+
if (targetFile) {
|
|
98
|
+
console.log(centerText(chalk.gray(`📄 ${targetFile}`)));
|
|
99
|
+
console.log(sep());
|
|
100
|
+
}
|
|
101
|
+
console.log(centerText(chalk.gray(`${shortModel} · /help için yardım · Ctrl+C çıkış`)));
|
|
102
|
+
console.log(sep());
|
|
103
|
+
console.log();
|
|
104
|
+
|
|
105
|
+
// ── Yükleme animasyonu ──────────────────────────────────────────────────────
|
|
106
|
+
const loadingFrames = ['●○○', '○●○', '○○●'];
|
|
107
|
+
let loadingFrame = 0;
|
|
108
|
+
let loadingTimer = null;
|
|
109
|
+
|
|
110
|
+
function startLoading() {
|
|
111
|
+
loadingFrame = 0;
|
|
112
|
+
process.stdout.write(chalk.gray(' ' + loadingFrames[0]));
|
|
113
|
+
loadingTimer = setInterval(() => {
|
|
114
|
+
process.stdout.write('\r' + chalk.gray(' ' + loadingFrames[loadingFrame]));
|
|
115
|
+
loadingFrame = (loadingFrame + 1) % loadingFrames.length;
|
|
116
|
+
}, 300);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function stopLoading() {
|
|
120
|
+
if (loadingTimer) { clearInterval(loadingTimer); loadingTimer = null; }
|
|
121
|
+
process.stdout.write('\r\x1b[2K');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ── Session özeti ─────────────────────────────────────────────────────────
|
|
125
|
+
async function showSummary() {
|
|
126
|
+
const { filesChanged, commandsRun } = getSessionStats();
|
|
127
|
+
console.log(chalk.gray('\n─── Session Özeti ───'));
|
|
128
|
+
console.log(chalk.green(` ✓ ${filesChanged} dosya değiştirildi`));
|
|
129
|
+
console.log(chalk.green(` ✓ ${commandsRun} komut çalıştırıldı`));
|
|
130
|
+
console.log(chalk.cyan(` ✓ ${messagesCount} mesaj gönderildi`));
|
|
131
|
+
console.log();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ── Mesaj gönderme ──────────────────────────────────────────────────────────
|
|
135
|
+
async function handleMessage(userMessage) {
|
|
136
|
+
userMessage = userMessage.trim();
|
|
137
|
+
if (!userMessage) return;
|
|
138
|
+
|
|
139
|
+
if (userMessage.startsWith('/')) {
|
|
140
|
+
const [cmd] = userMessage.slice(1).split(' ');
|
|
141
|
+
switch (cmd.toLowerCase()) {
|
|
142
|
+
case 'help':
|
|
143
|
+
console.log(chalk.yellow('Code Komutları:'));
|
|
144
|
+
[
|
|
145
|
+
['/clear', 'Ekranı temizle'],
|
|
146
|
+
['/summary', 'Session özetini göster'],
|
|
147
|
+
['/help', 'Bu yardım'],
|
|
148
|
+
].forEach(([c, d]) => console.log(' ' + chalk.cyan(c.padEnd(16)) + chalk.gray(d)));
|
|
149
|
+
console.log(chalk.gray(' Ctrl+C'.padEnd(18) + 'Çıkış'));
|
|
150
|
+
console.log();
|
|
151
|
+
return;
|
|
152
|
+
case 'clear':
|
|
153
|
+
console.clear();
|
|
154
|
+
return;
|
|
155
|
+
case 'summary':
|
|
156
|
+
await showSummary();
|
|
157
|
+
return;
|
|
158
|
+
default:
|
|
159
|
+
console.log(chalk.red(`Bilinmeyen komut: /${cmd}`));
|
|
160
|
+
console.log();
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (userMessage === 'exit' || userMessage === 'quit') {
|
|
166
|
+
await showSummary();
|
|
167
|
+
process.exit(0);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
messagesCount++;
|
|
171
|
+
console.log(chalk.white('You ') + userMessage);
|
|
172
|
+
startLoading();
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const toolDefinitions = getToolDefinitions();
|
|
176
|
+
let response = await _sendMessage(apiKey || config.providerApiKey, bot.id, userMessage, conversationId, systemPrompt, toolDefinitions);
|
|
177
|
+
stopLoading();
|
|
178
|
+
|
|
179
|
+
if (response.conversation_id) conversationId = response.conversation_id;
|
|
180
|
+
|
|
181
|
+
// Tool loop — agentMode: true ile onay + spinner
|
|
182
|
+
let iter = 0;
|
|
183
|
+
while (iter < 10) {
|
|
184
|
+
const toolCalls = extractToolCalls(response);
|
|
185
|
+
if (!toolCalls?.length) break;
|
|
186
|
+
console.log(chalk.yellow(`\n🔧 ${toolCalls.length} tool çalıştırılıyor...\n`));
|
|
187
|
+
const toolResults = await executeToolCalls(toolCalls, { agentMode: true });
|
|
188
|
+
const toolMsg = toolResults.map(tr =>
|
|
189
|
+
`Tool: ${tr.name}\nResult: ${tr.result.success ? (tr.result.output || JSON.stringify(tr.result)) : tr.result.error}`
|
|
190
|
+
).join('\n\n');
|
|
191
|
+
startLoading();
|
|
192
|
+
response = await _sendMessage(apiKey || config.providerApiKey, bot.id, toolMsg, conversationId, systemPrompt, toolDefinitions);
|
|
193
|
+
stopLoading();
|
|
194
|
+
if (response.conversation_id) conversationId = response.conversation_id;
|
|
195
|
+
iter++;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const botReply = response.reply || response.message || 'No response';
|
|
199
|
+
console.log(chalk.cyan(`${displayBotName} `) + botReply);
|
|
200
|
+
console.log();
|
|
201
|
+
|
|
202
|
+
addToHistory(bot.id, userMessage, botReply, conversationId);
|
|
203
|
+
addMessageToSession(bot.id, session.id, userMessage, botReply);
|
|
204
|
+
|
|
205
|
+
} catch (err) {
|
|
206
|
+
stopLoading();
|
|
207
|
+
const errMsg = err.message.split('"message":"')[1]?.split('"')[0] || err.message;
|
|
208
|
+
console.log(chalk.red(`Error: ${errMsg}`));
|
|
209
|
+
console.log();
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ── Input loop ───────────────────────────────────────────────────────────────
|
|
214
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
|
|
215
|
+
|
|
216
|
+
process.on('SIGINT', async () => {
|
|
217
|
+
rl.close();
|
|
218
|
+
await showSummary();
|
|
219
|
+
console.log(chalk.gray('👋 Goodbye!\n'));
|
|
220
|
+
process.exit(0);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
rl.on('close', () => process.exit(0));
|
|
224
|
+
|
|
225
|
+
async function promptLoop() {
|
|
226
|
+
rl.question('', async (msg) => {
|
|
227
|
+
process.stdout.write('\x1b[1A\x1b[2K');
|
|
228
|
+
await handleMessage(msg);
|
|
229
|
+
promptLoop();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
promptLoop();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
module.exports = code;
|
package/src/utils/tool-runner.js
CHANGED
|
@@ -1,37 +1,78 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*/
|
|
6
|
+
// ── Spinner ───────────────────────────────────────────────────────────────────
|
|
7
|
+
const SPINNER_FRAMES = ['⠋','⠙','⠹','⠸','⠼','⠴','⠦','⠧','⠇','⠏'];
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
function startSpinner(label) {
|
|
10
|
+
let i = 0;
|
|
11
|
+
const timer = setInterval(() => {
|
|
12
|
+
process.stdout.write(`\r${chalk.cyan(SPINNER_FRAMES[i++ % SPINNER_FRAMES.length])} ${chalk.gray(label)}`);
|
|
13
|
+
}, 80);
|
|
14
|
+
return timer;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function stopSpinner(timer, label, success = true) {
|
|
18
|
+
clearInterval(timer);
|
|
19
|
+
if (success) {
|
|
20
|
+
process.stdout.write(`\r${chalk.green('✓')} ${chalk.gray(label)}\n`);
|
|
21
|
+
} else {
|
|
22
|
+
process.stdout.write(`\r${chalk.red('✗')} ${chalk.gray(label)}\n`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// ── Diff view ─────────────────────────────────────────────────────────────────
|
|
27
|
+
function showDiff(oldContent, newContent, filepath) {
|
|
28
|
+
const oldLines = (oldContent || '').split('\n');
|
|
29
|
+
const newLines = newContent.split('\n');
|
|
30
|
+
console.log(chalk.gray(`\n 📄 ${filepath}`));
|
|
31
|
+
newLines.forEach(line => {
|
|
32
|
+
if (line && !oldLines.includes(line)) {
|
|
33
|
+
console.log(chalk.green(' + ' + line));
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
oldLines.forEach(line => {
|
|
37
|
+
if (line && !newLines.includes(line)) {
|
|
38
|
+
console.log(chalk.red(' - ' + line));
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
console.log();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// ── Session stats (module-level counters) ─────────────────────────────────────
|
|
45
|
+
let filesChanged = 0;
|
|
46
|
+
let commandsRun = 0;
|
|
47
|
+
|
|
48
|
+
function getSessionStats() {
|
|
49
|
+
return { filesChanged, commandsRun };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function resetSessionStats() {
|
|
53
|
+
filesChanged = 0;
|
|
54
|
+
commandsRun = 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── Load tools ────────────────────────────────────────────────────────────────
|
|
10
58
|
function loadTools() {
|
|
11
59
|
const toolsDir = path.join(__dirname, '..', 'tools');
|
|
12
60
|
const tools = {};
|
|
13
|
-
|
|
14
|
-
if (!fs.existsSync(toolsDir))
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
61
|
+
|
|
62
|
+
if (!fs.existsSync(toolsDir)) return tools;
|
|
63
|
+
|
|
18
64
|
const files = fs.readdirSync(toolsDir).filter(f => f.endsWith('.js'));
|
|
19
|
-
|
|
20
65
|
for (const file of files) {
|
|
21
66
|
try {
|
|
22
67
|
const tool = require(path.join(toolsDir, file));
|
|
23
|
-
if (tool.name && tool.execute)
|
|
24
|
-
tools[tool.name] = tool;
|
|
25
|
-
}
|
|
68
|
+
if (tool.name && tool.execute) tools[tool.name] = tool;
|
|
26
69
|
} catch (err) {
|
|
27
70
|
console.error(chalk.red(`Failed to load tool ${file}:`, err.message));
|
|
28
71
|
}
|
|
29
72
|
}
|
|
30
|
-
|
|
31
73
|
return tools;
|
|
32
74
|
}
|
|
33
75
|
|
|
34
|
-
// Get tool definitions for API
|
|
35
76
|
function getToolDefinitions() {
|
|
36
77
|
const tools = loadTools();
|
|
37
78
|
return Object.values(tools).map(tool => ({
|
|
@@ -41,47 +82,74 @@ function getToolDefinitions() {
|
|
|
41
82
|
}));
|
|
42
83
|
}
|
|
43
84
|
|
|
44
|
-
// Execute a single tool
|
|
45
|
-
async function executeTool(toolName, params) {
|
|
85
|
+
// ── Execute a single tool ─────────────────────────────────────────────────────
|
|
86
|
+
async function executeTool(toolName, params, opts = {}) {
|
|
46
87
|
const tools = loadTools();
|
|
47
88
|
const tool = tools[toolName];
|
|
48
|
-
|
|
89
|
+
const agentMode = opts.agentMode || false;
|
|
90
|
+
|
|
49
91
|
if (!tool) {
|
|
50
|
-
return {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
92
|
+
return { success: false, error: `Tool '${toolName}' not found` };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const label = `${toolName}${params.path ? ' — ' + params.path : params.command ? ' — ' + params.command : ''}`;
|
|
96
|
+
|
|
97
|
+
// ── Onay mekanizması (write_file ve tehlikeli bash) ───────────────────────
|
|
98
|
+
if (agentMode) {
|
|
99
|
+
const needsConfirm =
|
|
100
|
+
toolName === 'write_file' ||
|
|
101
|
+
(toolName === 'bash' && /\b(rm|mv|cp|chmod|chown|dd|mkfs|truncate)\b/.test(params.command || ''));
|
|
102
|
+
|
|
103
|
+
if (needsConfirm) {
|
|
104
|
+
if (toolName === 'write_file') {
|
|
105
|
+
// Diff göster
|
|
106
|
+
let oldContent = '';
|
|
107
|
+
try { oldContent = fs.readFileSync(path.resolve(params.path), 'utf-8'); } catch {}
|
|
108
|
+
showDiff(oldContent, params.content || '', params.path);
|
|
109
|
+
} else {
|
|
110
|
+
console.log(chalk.yellow(`\n 🖥️ Komut: ${chalk.white(params.command)}\n`));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const { confirm } = await inquirer.prompt([{
|
|
114
|
+
type: 'confirm',
|
|
115
|
+
name: 'confirm',
|
|
116
|
+
message: chalk.yellow(` ${toolName === 'write_file' ? `✏️ ${params.path} dosyası değiştirilecek` : '⚠️ Bu komut çalıştırılacak'}. Onaylıyor musun?`),
|
|
117
|
+
default: true,
|
|
118
|
+
}]);
|
|
119
|
+
|
|
120
|
+
if (!confirm) {
|
|
121
|
+
console.log(chalk.gray(' İptal edildi.\n'));
|
|
122
|
+
return { success: false, error: 'Kullanıcı iptal etti.' };
|
|
123
|
+
}
|
|
124
|
+
}
|
|
54
125
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
126
|
+
|
|
127
|
+
// ── Spinner ile çalıştır ──────────────────────────────────────────────────
|
|
128
|
+
const spinner = startSpinner(label);
|
|
59
129
|
try {
|
|
60
130
|
const result = await tool.execute(params);
|
|
61
|
-
|
|
131
|
+
stopSpinner(spinner, label, result.success !== false);
|
|
132
|
+
|
|
133
|
+
// İstatistik güncelle
|
|
134
|
+
if (result.success !== false) {
|
|
135
|
+
if (toolName === 'write_file') filesChanged++;
|
|
136
|
+
if (toolName === 'bash') commandsRun++;
|
|
137
|
+
}
|
|
138
|
+
|
|
62
139
|
return result;
|
|
63
140
|
} catch (error) {
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
success: false,
|
|
67
|
-
error: error.message
|
|
68
|
-
};
|
|
141
|
+
stopSpinner(spinner, label, false);
|
|
142
|
+
return { success: false, error: error.message };
|
|
69
143
|
}
|
|
70
144
|
}
|
|
71
145
|
|
|
72
|
-
// Execute multiple tool calls
|
|
73
|
-
async function executeToolCalls(toolCalls) {
|
|
146
|
+
// ── Execute multiple tool calls ───────────────────────────────────────────────
|
|
147
|
+
async function executeToolCalls(toolCalls, opts = {}) {
|
|
74
148
|
const results = [];
|
|
75
|
-
|
|
76
149
|
for (const call of toolCalls) {
|
|
77
|
-
const result = await executeTool(call.name, call.input);
|
|
78
|
-
results.push({
|
|
79
|
-
id: call.id,
|
|
80
|
-
name: call.name,
|
|
81
|
-
result
|
|
82
|
-
});
|
|
150
|
+
const result = await executeTool(call.name, call.input, opts);
|
|
151
|
+
results.push({ id: call.id, name: call.name, result });
|
|
83
152
|
}
|
|
84
|
-
|
|
85
153
|
return results;
|
|
86
154
|
}
|
|
87
155
|
|
|
@@ -89,5 +157,7 @@ module.exports = {
|
|
|
89
157
|
loadTools,
|
|
90
158
|
getToolDefinitions,
|
|
91
159
|
executeTool,
|
|
92
|
-
executeToolCalls
|
|
160
|
+
executeToolCalls,
|
|
161
|
+
getSessionStats,
|
|
162
|
+
resetSessionStats,
|
|
93
163
|
};
|