natureco-cli 2.19.2 → 2.20.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/package.json +1 -1
- package/src/commands/code.js +266 -40
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,7 +38,131 @@ 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
|
|
|
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
|
+
|
|
144
|
+
// ── Git helpers ───────────────────────────────────────────────────────────────
|
|
145
|
+
async function generateCommitMessage(diff, providerConfig) {
|
|
146
|
+
const body = {
|
|
147
|
+
model: providerConfig.model,
|
|
148
|
+
messages: [
|
|
149
|
+
{ role: 'system', content: 'Sen bir git commit mesajı üreticisin. Conventional Commits formatında (feat/fix/refactor/chore vb.) kısa ve açıklayıcı bir commit mesajı yaz. Sadece mesajı yaz, başka hiçbir şey yazma.' },
|
|
150
|
+
{ role: 'user', content: `Bu diff için commit mesajı üret:\n\n${diff}` },
|
|
151
|
+
],
|
|
152
|
+
temperature: 0.3,
|
|
153
|
+
max_tokens: 100,
|
|
154
|
+
stream: false,
|
|
155
|
+
};
|
|
156
|
+
const res = await fetch(`${providerConfig.url}/chat/completions`, {
|
|
157
|
+
method: 'POST',
|
|
158
|
+
headers: { 'Authorization': `Bearer ${providerConfig.apiKey}`, 'Content-Type': 'application/json' },
|
|
159
|
+
body: JSON.stringify(body),
|
|
160
|
+
});
|
|
161
|
+
const data = await res.json();
|
|
162
|
+
return (data.choices?.[0]?.message?.content || 'chore: update files').trim().replace(/^["']|["']$/g, '');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
|
|
41
166
|
async function streamMessage(providerConfig, messages, tools) {
|
|
42
167
|
const endpoint = `${providerConfig.url}/chat/completions`;
|
|
43
168
|
const body = {
|
|
@@ -67,17 +192,15 @@ async function streamMessage(providerConfig, messages, tools) {
|
|
|
67
192
|
const reader = response.body.getReader();
|
|
68
193
|
const decoder = new TextDecoder();
|
|
69
194
|
let fullText = '';
|
|
70
|
-
let toolCallsBuffer = {};
|
|
195
|
+
let toolCallsBuffer = {};
|
|
71
196
|
|
|
72
197
|
process.stdout.write('\n');
|
|
73
198
|
|
|
74
199
|
while (true) {
|
|
75
200
|
const { done, value } = await reader.read();
|
|
76
201
|
if (done) break;
|
|
77
|
-
|
|
78
202
|
const chunk = decoder.decode(value);
|
|
79
203
|
const lines = chunk.split('\n').filter(l => l.startsWith('data: '));
|
|
80
|
-
|
|
81
204
|
for (const line of lines) {
|
|
82
205
|
const data = line.slice(6).trim();
|
|
83
206
|
if (data === '[DONE]') continue;
|
|
@@ -85,20 +208,11 @@ async function streamMessage(providerConfig, messages, tools) {
|
|
|
85
208
|
const json = JSON.parse(data);
|
|
86
209
|
const delta = json.choices?.[0]?.delta;
|
|
87
210
|
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
|
|
211
|
+
if (delta.content) { process.stdout.write(delta.content); fullText += delta.content; }
|
|
96
212
|
if (delta.tool_calls) {
|
|
97
213
|
for (const tc of delta.tool_calls) {
|
|
98
214
|
const idx = tc.index ?? 0;
|
|
99
|
-
if (!toolCallsBuffer[idx]) {
|
|
100
|
-
toolCallsBuffer[idx] = { id: tc.id || '', name: '', arguments: '' };
|
|
101
|
-
}
|
|
215
|
+
if (!toolCallsBuffer[idx]) toolCallsBuffer[idx] = { id: tc.id || '', name: '', arguments: '' };
|
|
102
216
|
if (tc.id) toolCallsBuffer[idx].id = tc.id;
|
|
103
217
|
if (tc.function?.name) toolCallsBuffer[idx].name += tc.function.name;
|
|
104
218
|
if (tc.function?.arguments) toolCallsBuffer[idx].arguments += tc.function.arguments;
|
|
@@ -121,12 +235,11 @@ async function streamMessage(providerConfig, messages, tools) {
|
|
|
121
235
|
return { text: fullText, toolCalls };
|
|
122
236
|
}
|
|
123
237
|
|
|
124
|
-
// ── Tool execution
|
|
238
|
+
// ── Tool execution ────────────────────────────────────────────────────────────
|
|
125
239
|
const DANGEROUS = ['rm ', 'rmdir', 'delete', 'DROP', 'truncate'];
|
|
126
240
|
|
|
127
241
|
async function runToolCall(toolCall, stats) {
|
|
128
242
|
const inputPreview = JSON.stringify(toolCall.input).slice(0, 60);
|
|
129
|
-
const label = `${toolCall.name} ${chalk.gray(inputPreview)}`;
|
|
130
243
|
|
|
131
244
|
const needsConfirm =
|
|
132
245
|
toolCall.name === 'write_file' ||
|
|
@@ -149,7 +262,15 @@ async function runToolCall(toolCall, stats) {
|
|
|
149
262
|
stopSpinner(spinner, toolCall.name, result.success !== false);
|
|
150
263
|
|
|
151
264
|
if (result.success !== false) {
|
|
152
|
-
if (toolCall.name === 'write_file')
|
|
265
|
+
if (toolCall.name === 'write_file') {
|
|
266
|
+
stats.filesChanged++;
|
|
267
|
+
stats.changedFiles.push(toolCall.input.path || '?');
|
|
268
|
+
console.log(chalk.green(` ✓ ${toolCall.input.path} güncellendi`));
|
|
269
|
+
if (stats.changedFiles.length === 1) {
|
|
270
|
+
// İlk değişiklikte ipucu göster
|
|
271
|
+
console.log(chalk.gray(` 💡 git add . && /commit ile kaydet`));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
153
274
|
if (toolCall.name === 'bash') stats.commandsRun++;
|
|
154
275
|
stats.toolCallCount++;
|
|
155
276
|
}
|
|
@@ -157,6 +278,40 @@ async function runToolCall(toolCall, stats) {
|
|
|
157
278
|
return result;
|
|
158
279
|
}
|
|
159
280
|
|
|
281
|
+
// ── Test runner ───────────────────────────────────────────────────────────────
|
|
282
|
+
async function runTests(projectIndex, conversationMessages, tools, providerConfig, displayBotName) {
|
|
283
|
+
const testScript = projectIndex.packageJson?.scripts?.test;
|
|
284
|
+
if (!testScript || testScript.includes('no test')) return;
|
|
285
|
+
|
|
286
|
+
const { confirm } = await inquirer.prompt([{
|
|
287
|
+
type: 'confirm', name: 'confirm',
|
|
288
|
+
message: chalk.yellow(' 🧪 Testleri çalıştıralım mı?'), default: true,
|
|
289
|
+
}]);
|
|
290
|
+
if (!confirm) return;
|
|
291
|
+
|
|
292
|
+
console.log(chalk.gray('\n npm test çalışıyor...\n'));
|
|
293
|
+
try {
|
|
294
|
+
const output = execSync('npm test', {
|
|
295
|
+
cwd: process.cwd(), timeout: 30000, stdio: 'pipe',
|
|
296
|
+
}).toString();
|
|
297
|
+
console.log(chalk.green(' ✅ Testler geçti!\n'));
|
|
298
|
+
} catch (err) {
|
|
299
|
+
const errOutput = (err.stdout?.toString() || err.message || '').slice(0, 500);
|
|
300
|
+
console.log(chalk.red(' ❌ Test hatası:\n'));
|
|
301
|
+
console.log(chalk.gray(' ' + errOutput.split('\n').join('\n ')));
|
|
302
|
+
console.log();
|
|
303
|
+
|
|
304
|
+
const { fix } = await inquirer.prompt([{
|
|
305
|
+
type: 'confirm', name: 'fix',
|
|
306
|
+
message: chalk.yellow(' Agent test hatasını düzeltsin mi?'), default: true,
|
|
307
|
+
}]);
|
|
308
|
+
if (fix) {
|
|
309
|
+
return `Test hatası oluştu:\n${errOutput}\nBu hatayı düzelt.`;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
160
315
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
161
316
|
async function code(targetFile, options = {}) {
|
|
162
317
|
const apiKey = getApiKey();
|
|
@@ -202,25 +357,23 @@ async function code(targetFile, options = {}) {
|
|
|
202
357
|
|
|
203
358
|
const shortModel = providerConfig.model.split('/').pop().split('-').slice(0, 3).join('-');
|
|
204
359
|
|
|
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 {}
|
|
360
|
+
// ── Proje indexing ───────────────────────────────────────────────────────────
|
|
361
|
+
const indexSpinner = startSpinner('Proje indexleniyor...');
|
|
362
|
+
const projectIndex = await indexProject(process.cwd());
|
|
363
|
+
stopSpinner(indexSpinner, `${projectIndex.type.toUpperCase()} projesi — ${projectIndex.files.length} dosya`, true);
|
|
213
364
|
|
|
214
365
|
// ── Sistem prompt ────────────────────────────────────────────────────────────
|
|
215
366
|
const agentsPrompt = getAgentsPrompt();
|
|
216
367
|
const memoryPrompt = getMemoryPrompt(bot.id);
|
|
368
|
+
const indexPrompt = buildIndexPrompt(projectIndex);
|
|
217
369
|
|
|
218
370
|
let systemPrompt = `Sen NatureCo Code Agent'sın — güçlü bir AI coding asistanı.
|
|
219
371
|
Kullanıcının projesinde gerçekten çalış: dosyaları oku, değiştir, komutları çalıştır.
|
|
220
372
|
Her adımı Türkçe açıkla. Değişiklik yapmadan önce ne yapacağını söyle.
|
|
221
373
|
Hataları yakala ve düzelt. İş bitince özet sun.
|
|
222
|
-
|
|
223
|
-
|
|
374
|
+
Kullanıcı: ${userName}
|
|
375
|
+
|
|
376
|
+
${indexPrompt}`;
|
|
224
377
|
|
|
225
378
|
if (agentsPrompt) systemPrompt += `\n\n## Project Instructions\n${agentsPrompt}`;
|
|
226
379
|
if (memoryPrompt) systemPrompt += '\n\n' + memoryPrompt;
|
|
@@ -238,12 +391,10 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
238
391
|
// ── Session ──────────────────────────────────────────────────────────────────
|
|
239
392
|
const session = createSession(bot.id, bot.name);
|
|
240
393
|
const conversationMessages = [{ role: 'system', content: systemPrompt }];
|
|
241
|
-
const stats = { filesChanged: 0, commandsRun: 0, toolCallCount: 0, messageCount: 0 };
|
|
394
|
+
const stats = { filesChanged: 0, commandsRun: 0, toolCallCount: 0, messageCount: 0, changedFiles: [] };
|
|
242
395
|
|
|
243
|
-
// ── MCP sunucularını başlat ──────────────────────────────────────────────────
|
|
244
396
|
await startMcpServers().catch(() => {});
|
|
245
397
|
|
|
246
|
-
// ── Tool definitions ─────────────────────────────────────────────────────────
|
|
247
398
|
const localTools = getToolDefinitions();
|
|
248
399
|
const tools = localTools.map(t => ({
|
|
249
400
|
type: 'function',
|
|
@@ -271,6 +422,11 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
271
422
|
console.log();
|
|
272
423
|
console.log(centerText(chalk.cyan(`(\\_/) ${displayBotName} hazır`) + chalk.gray(` · v${version}`)));
|
|
273
424
|
console.log(sep());
|
|
425
|
+
console.log(centerText(chalk.gray(`📁 ${projectIndex.type.toUpperCase()} projesi · ${projectIndex.files.length} dosya`)));
|
|
426
|
+
if (projectIndex.gitBranch) {
|
|
427
|
+
const changes = projectIndex.gitStatus?.length || 0;
|
|
428
|
+
console.log(centerText(chalk.gray(`🌿 ${projectIndex.gitBranch} · ${changes} değişiklik`)));
|
|
429
|
+
}
|
|
274
430
|
if (targetFile) console.log(centerText(chalk.gray(`📄 ${targetFile}`)));
|
|
275
431
|
console.log(centerText(chalk.gray(`${shortModel} · /help · Ctrl+C çıkış`)));
|
|
276
432
|
console.log(sep());
|
|
@@ -278,11 +434,21 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
278
434
|
|
|
279
435
|
// ── Session özeti ─────────────────────────────────────────────────────────
|
|
280
436
|
function showSummary() {
|
|
281
|
-
|
|
437
|
+
const w = process.stdout.columns || 120;
|
|
438
|
+
console.log(chalk.gray('\n' + '─'.repeat(w)));
|
|
439
|
+
console.log(chalk.gray(' Agent Session Özeti'));
|
|
440
|
+
console.log(chalk.gray('─'.repeat(w)));
|
|
282
441
|
console.log(` ${chalk.green('✓')} ${stats.filesChanged} dosya değiştirildi`);
|
|
283
442
|
console.log(` ${chalk.green('✓')} ${stats.commandsRun} komut çalıştırıldı`);
|
|
284
443
|
console.log(` ${chalk.green('✓')} ${stats.toolCallCount} tool çağrısı yapıldı`);
|
|
285
|
-
console.log(` ${chalk.cyan('◉')} ${stats.messageCount} mesaj
|
|
444
|
+
console.log(` ${chalk.cyan('◉')} ${stats.messageCount} mesaj`);
|
|
445
|
+
if (stats.changedFiles.length > 0) {
|
|
446
|
+
console.log();
|
|
447
|
+
console.log(chalk.cyan(` 📝 Değiştirilen dosyalar (${stats.changedFiles.length}):`));
|
|
448
|
+
stats.changedFiles.forEach(f => console.log(chalk.white(` ${f}`)));
|
|
449
|
+
}
|
|
450
|
+
console.log(chalk.gray('─'.repeat(w)));
|
|
451
|
+
console.log();
|
|
286
452
|
}
|
|
287
453
|
|
|
288
454
|
// ── Mesaj gönder + tool loop ──────────────────────────────────────────────
|
|
@@ -290,7 +456,6 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
290
456
|
userMessage = userMessage.trim();
|
|
291
457
|
if (!userMessage) return;
|
|
292
458
|
|
|
293
|
-
// Slash komutları
|
|
294
459
|
if (userMessage.startsWith('/')) {
|
|
295
460
|
const [cmd] = userMessage.slice(1).split(' ');
|
|
296
461
|
switch (cmd.toLowerCase()) {
|
|
@@ -300,6 +465,9 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
300
465
|
['/clear', 'Ekranı temizle'],
|
|
301
466
|
['/summary', 'Session özetini göster'],
|
|
302
467
|
['/done', 'Bitir ve özet göster'],
|
|
468
|
+
['/index', 'Projeyi yeniden indexle'],
|
|
469
|
+
['/git', 'Git durumu ve son commitler'],
|
|
470
|
+
['/commit', 'Staged değişiklikleri AI ile commit et'],
|
|
303
471
|
['/help', 'Bu yardım'],
|
|
304
472
|
].forEach(([c, d]) => console.log(' ' + chalk.cyan(c.padEnd(12)) + chalk.gray(d)));
|
|
305
473
|
console.log(chalk.gray(' Ctrl+C'.padEnd(14) + 'Çıkış'));
|
|
@@ -313,6 +481,58 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
313
481
|
showSummary();
|
|
314
482
|
if (cmd === 'done') process.exit(0);
|
|
315
483
|
return;
|
|
484
|
+
case 'index': {
|
|
485
|
+
const sp = startSpinner('Proje yeniden indexleniyor...');
|
|
486
|
+
const newIndex = await indexProject(process.cwd());
|
|
487
|
+
stopSpinner(sp, `${newIndex.files.length} dosya indexlendi`, true);
|
|
488
|
+
// Sistem prompt'u güncelle
|
|
489
|
+
conversationMessages[0].content = conversationMessages[0].content.replace(
|
|
490
|
+
/Proje Bilgisi:[\s\S]*?(?=\n\n|$)/,
|
|
491
|
+
buildIndexPrompt(newIndex)
|
|
492
|
+
);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
case 'git': {
|
|
496
|
+
try {
|
|
497
|
+
const status = execSync('git status --short', { cwd: process.cwd(), stdio: 'pipe' }).toString().trim();
|
|
498
|
+
const log = execSync('git log --oneline -5', { cwd: process.cwd(), stdio: 'pipe' }).toString().trim();
|
|
499
|
+
console.log(chalk.yellow('\n Git Durumu:'));
|
|
500
|
+
console.log(status ? chalk.gray(' ' + status.split('\n').join('\n ')) : chalk.gray(' temiz'));
|
|
501
|
+
console.log(chalk.yellow('\n Son Commitler:'));
|
|
502
|
+
console.log(chalk.gray(' ' + log.split('\n').join('\n ')));
|
|
503
|
+
console.log();
|
|
504
|
+
} catch (e) {
|
|
505
|
+
console.log(chalk.red(' Git bulunamadı veya bu dizin bir git repo değil.\n'));
|
|
506
|
+
}
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
case 'commit': {
|
|
510
|
+
try {
|
|
511
|
+
const diff = execSync('git diff --staged', { cwd: process.cwd(), stdio: 'pipe' }).toString();
|
|
512
|
+
if (!diff.trim()) {
|
|
513
|
+
console.log(chalk.red('\n Staged değişiklik yok.'));
|
|
514
|
+
console.log(chalk.gray(' Önce: git add .\n'));
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
const sp = startSpinner('Commit mesajı üretiliyor...');
|
|
518
|
+
const commitMsg = await generateCommitMessage(diff.slice(0, 2000), providerConfig);
|
|
519
|
+
stopSpinner(sp, 'Commit mesajı hazır', true);
|
|
520
|
+
console.log(chalk.cyan(`\n Önerilen: ${chalk.white(commitMsg)}\n`));
|
|
521
|
+
const { ok } = await inquirer.prompt([{
|
|
522
|
+
type: 'confirm', name: 'ok',
|
|
523
|
+
message: ' Commit edilsin mi?', default: true,
|
|
524
|
+
}]);
|
|
525
|
+
if (ok) {
|
|
526
|
+
execSync(`git commit -m "${commitMsg.replace(/"/g, '\\"')}"`, { cwd: process.cwd(), stdio: 'pipe' });
|
|
527
|
+
console.log(chalk.green(' ✓ Commit yapıldı!\n'));
|
|
528
|
+
} else {
|
|
529
|
+
console.log(chalk.gray(' İptal edildi.\n'));
|
|
530
|
+
}
|
|
531
|
+
} catch (e) {
|
|
532
|
+
console.log(chalk.red(` Git hatası: ${e.message}\n`));
|
|
533
|
+
}
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
316
536
|
default:
|
|
317
537
|
console.log(chalk.red(`Bilinmeyen komut: /${cmd}\n`));
|
|
318
538
|
return;
|
|
@@ -326,10 +546,10 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
326
546
|
|
|
327
547
|
stats.messageCount++;
|
|
328
548
|
console.log(chalk.white('You ') + userMessage);
|
|
329
|
-
|
|
330
|
-
// Mesajı history'e ekle
|
|
331
549
|
conversationMessages.push({ role: 'user', content: userMessage });
|
|
332
550
|
|
|
551
|
+
const prevFilesChanged = stats.filesChanged;
|
|
552
|
+
|
|
333
553
|
// ── Streaming + tool loop (max 20 iter) ──────────────────────────────────
|
|
334
554
|
let iter = 0;
|
|
335
555
|
while (iter < 20) {
|
|
@@ -341,11 +561,10 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
341
561
|
streamResult = await streamMessage(providerConfig, conversationMessages, tools);
|
|
342
562
|
} catch (err) {
|
|
343
563
|
console.log(chalk.red(`\nError: ${err.message}\n`));
|
|
344
|
-
conversationMessages.pop();
|
|
564
|
+
conversationMessages.pop();
|
|
345
565
|
return;
|
|
346
566
|
}
|
|
347
567
|
|
|
348
|
-
// Assistant mesajını history'e ekle
|
|
349
568
|
const assistantMsg = { role: 'assistant', content: streamResult.text || '' };
|
|
350
569
|
if (streamResult.toolCalls?.length) {
|
|
351
570
|
assistantMsg.tool_calls = streamResult.toolCalls.map(tc => ({
|
|
@@ -356,10 +575,8 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
356
575
|
}
|
|
357
576
|
conversationMessages.push(assistantMsg);
|
|
358
577
|
|
|
359
|
-
// Tool call yoksa bitti
|
|
360
578
|
if (!streamResult.toolCalls?.length) break;
|
|
361
579
|
|
|
362
|
-
// Tool call'ları çalıştır
|
|
363
580
|
console.log(chalk.yellow(`\n🔧 ${streamResult.toolCalls.length} tool çalıştırılıyor...\n`));
|
|
364
581
|
|
|
365
582
|
for (const toolCall of streamResult.toolCalls) {
|
|
@@ -385,6 +602,15 @@ Kullanıcı: ${userName}${projectContext}`;
|
|
|
385
602
|
addToHistory(bot.id, userMessage, lastAssistant.content, null);
|
|
386
603
|
addMessageToSession(bot.id, session.id, userMessage, lastAssistant.content);
|
|
387
604
|
}
|
|
605
|
+
|
|
606
|
+
// ── Test runner — dosya değiştiyse sor ───────────────────────────────────
|
|
607
|
+
if (stats.filesChanged > prevFilesChanged && projectIndex.packageJson?.scripts?.test) {
|
|
608
|
+
const fixPrompt = await runTests(projectIndex, conversationMessages, tools, providerConfig, displayBotName);
|
|
609
|
+
if (fixPrompt) {
|
|
610
|
+
// Test hatasını agent'a gönder
|
|
611
|
+
await handleMessage(fixPrompt);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
388
614
|
}
|
|
389
615
|
|
|
390
616
|
// ── Input loop ───────────────────────────────────────────────────────────────
|