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