natureco-cli 5.62.0 → 5.64.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/CHANGELOG.md +34 -1
- package/bin/natureco.js +10 -3
- package/package.json +8 -5
- package/scripts/benchmark-startup.js +26 -0
- package/src/commands/backup.js +3 -3
- package/src/commands/chat.js +17 -17
- package/src/commands/code.js +49 -14
- package/src/commands/code_v5.js +15 -1
- package/src/commands/gateway-server.js +128 -24
- package/src/commands/git.js +2 -2
- package/src/commands/pairing.js +2 -22
- package/src/commands/repl.js +118 -112
- package/src/tools/agentic-runner.js +41 -33
- package/src/tools/memory_write.js +16 -12
- package/src/tools/structural_patch.js +25 -0
- package/src/tools/workflow.js +10 -2
- package/src/utils/agent-core.js +51 -0
- package/src/utils/agent-workspace.js +24 -0
- package/src/utils/api.js +12 -8
- package/src/utils/channel-sdk.js +212 -0
- package/src/utils/code-intelligence.js +69 -0
- package/src/utils/coding-session.js +54 -0
- package/src/utils/delivery-store.js +34 -0
- package/src/utils/i18n.js +7 -1
- package/src/utils/json-schema.js +43 -0
- package/src/utils/lsp-client.js +129 -0
- package/src/utils/memory-record.js +49 -0
- package/src/utils/pairing-store.js +55 -0
- package/src/utils/pattern-detector.js +13 -3
- package/src/utils/plugin-registry.js +3 -3
- package/src/utils/process-errors.js +14 -7
- package/src/utils/runtime-health.js +28 -0
- package/src/utils/secret-store.js +90 -0
- package/src/utils/secure-sync.js +63 -0
- package/src/utils/skill-lifecycle.js +59 -0
- package/src/utils/structural-patch.js +68 -0
- package/src/utils/sub-agent.js +13 -2
- package/src/utils/test-failure-analyzer.js +64 -0
- package/src/utils/tool-execution-gateway.js +56 -0
- package/src/utils/tool-manifest.js +31 -0
- package/src/utils/tool-path-policy.js +49 -0
- package/src/utils/tool-result.js +17 -0
- package/src/utils/tool-runner.js +81 -53
- package/src/utils/tools.js +30 -42
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
All notable changes to NatureCo CLI will be documented in this file.
|
|
3
|
+
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
|
+
|
|
5
|
+
## [5.64.0] - 2026-07-13 — Secure unified agent and operations foundation
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Unified `AgentCore`, `ToolExecutionGateway`, single tool manifest, mandatory JSON Schema validation and standardized tool results.
|
|
9
|
+
- Conflict-safe structural patching, atomic rollback, coding `/undo`, `/retry`, `/compact`, local code intelligence and LSP JSON-RPC client.
|
|
10
|
+
- Provenance/confidence/TTL memory records, conflict resolution, approved skill promotion, versioning and rollback.
|
|
11
|
+
- Shared channel SDK with pairing-by-default, persistent idempotent delivery queue, retries, dead letters, reconnect supervision, health and metrics.
|
|
12
|
+
- macOS Keychain, Windows DPAPI, Linux Secret Service and AES-256-GCM encrypted secret fallback.
|
|
13
|
+
- Encrypted multi-device sync primitives with authenticated envelopes and vector-clock conflict detection.
|
|
14
|
+
- Isolated sub-agent worktrees, test-failure analysis/repair loop, startup benchmark and TR/EN catalog snapshots.
|
|
15
|
+
|
|
16
|
+
### Security
|
|
17
|
+
- Fixed bulk-write guardrail bypasses, protected sensitive paths across execution origins and enabled guardrail hard-stop.
|
|
18
|
+
- Removed high-risk shell string interpolation from iMessage delivery, backups, plugin installation/cloning and AI-generated Git commits.
|
|
19
|
+
- Added channel pairing gates, non-interactive fail-closed permission handling and process-listener cleanup.
|
|
20
|
+
|
|
21
|
+
### Tests
|
|
22
|
+
- 73 test files, 711 passing tests (3 skipped), zero high-severity audit findings, CLI smoke and package checks.
|
|
23
|
+
- Windows `--version` median reduced from ~366 ms to ~75 ms, meeting the <100 ms target.
|
|
24
|
+
- Real Windows/macOS/Linux GitHub Actions matrix with lint, tests, smoke, audit and package verification.
|
|
25
|
+
|
|
26
|
+
## [5.63.0] - 2026-07-12 — Complete English REPL localization
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
- Completed the interactive REPL's Turkish/English localization across help, session headers, memory, plan review, identity prompts, command descriptions and workflow summaries.
|
|
30
|
+
- Localized default user, assistant and empty-session labels for new English installations without changing existing saved personas.
|
|
31
|
+
- Added English identity-question handling and language-aware internal plan/workflow instructions so responses remain in the selected interface language.
|
|
32
|
+
- Removed the unused legacy `mattermost` client, upgraded `node-telegram-bot-api` to 1.1.2 and Discord to 14.26.5, and pinned its compatible patched `undici` 6.27.0 runtime.
|
|
33
|
+
|
|
34
|
+
### Tests
|
|
35
|
+
- Added a dedicated English REPL regression suite that prevents untranslated Turkish help text from returning.
|
|
36
|
+
- Full validation: 49 test files, 640 passing tests (3 skipped), ESLint, CLI smoke test and npm package dry run.
|
|
4
37
|
|
|
5
38
|
## [5.51.4] - 2026-07-11 — "account: OTP kodu magiclink tipini de dener"
|
|
6
39
|
|
package/bin/natureco.js
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Fast path: version checks are used by shells, installers and health probes.
|
|
4
|
+
// Avoid loading Commander and ~100 command modules for a constant-time answer.
|
|
5
|
+
if (process.argv.length === 3 && (process.argv[2] === '--version' || process.argv[2] === '-V')) {
|
|
6
|
+
process.stdout.write(require('../package.json').version + '\n');
|
|
7
|
+
process.exit(0);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { Command } = require('commander');
|
|
4
11
|
const chalk = require('chalk');
|
|
5
12
|
const packageJson = require('../package.json');
|
|
6
13
|
// Küresel çökme/EPIPE yakalayıcıları — ham Node stack yerine düzgün mesaj + audit log
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "5.64.0",
|
|
4
|
+
"description": "Terminal-native AI agent CLI with bilingual TR/EN UI, multi-agent orchestration, persistent memory, secure tools and messaging integrations.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"natureco": "bin/natureco.js"
|
|
7
7
|
},
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"test:unit": "vitest run",
|
|
23
23
|
"test:watch": "vitest",
|
|
24
24
|
"test:coverage": "vitest run --coverage",
|
|
25
|
+
"perf:startup": "node scripts/benchmark-startup.js",
|
|
25
26
|
"lint": "eslint src/ bin/ test/",
|
|
26
27
|
"lint:fix": "eslint src/ bin/ test/ --fix",
|
|
27
28
|
"lint:errors-only": "eslint src/ bin/ test/ --quiet",
|
|
@@ -74,11 +75,10 @@
|
|
|
74
75
|
"@whiskeysockets/baileys": "^7.0.0-rc10",
|
|
75
76
|
"chalk": "^4.1.2",
|
|
76
77
|
"commander": "^11.1.0",
|
|
77
|
-
"discord.js": "^14.26.
|
|
78
|
+
"discord.js": "^14.26.5",
|
|
78
79
|
"irc": "^0.5.2",
|
|
79
|
-
"mattermost": "^3.4.0",
|
|
80
80
|
"node-cron": "^4.2.1",
|
|
81
|
-
"node-telegram-bot-api": "^
|
|
81
|
+
"node-telegram-bot-api": "^1.1.2",
|
|
82
82
|
"openai": "^6.45.0",
|
|
83
83
|
"pino": "^8.21.0",
|
|
84
84
|
"qrcode": "^1.5.4",
|
|
@@ -93,5 +93,8 @@
|
|
|
93
93
|
"eslint": "^10.6.0",
|
|
94
94
|
"globals": "^15.15.0",
|
|
95
95
|
"vitest": "^4.1.9"
|
|
96
|
+
},
|
|
97
|
+
"overrides": {
|
|
98
|
+
"undici": "6.27.0"
|
|
96
99
|
}
|
|
97
100
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawnSync } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
|
|
6
|
+
const bin = path.join(__dirname, '..', 'bin', 'natureco.js');
|
|
7
|
+
function measure(args, runs = 7) {
|
|
8
|
+
const samples = [];
|
|
9
|
+
for (let i = 0; i < runs; i++) {
|
|
10
|
+
const start = process.hrtime.bigint();
|
|
11
|
+
const result = spawnSync(process.execPath, [bin, ...args], { encoding: 'utf8', env: { ...process.env, NATURECO_NO_UPDATE_CHECK: '1', FORCE_COLOR: '0' } });
|
|
12
|
+
const durationMs = Number(process.hrtime.bigint() - start) / 1e6;
|
|
13
|
+
if (result.status !== 0) throw new Error(`${args.join(' ')} failed: ${result.stderr}`);
|
|
14
|
+
samples.push(durationMs);
|
|
15
|
+
}
|
|
16
|
+
samples.sort((a, b) => a - b);
|
|
17
|
+
return { min: samples[0], median: samples[Math.floor(samples.length / 2)], p95: samples[Math.min(samples.length - 1, Math.ceil(samples.length * 0.95) - 1)], samples };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const version = measure(['--version']);
|
|
21
|
+
const report = { timestamp: new Date().toISOString(), node: process.version, platform: process.platform, version };
|
|
22
|
+
console.log(JSON.stringify(report, null, 2));
|
|
23
|
+
if (version.median >= 100) {
|
|
24
|
+
console.error(`--version median ${version.median.toFixed(1)}ms exceeds 100ms target`);
|
|
25
|
+
process.exitCode = 1;
|
|
26
|
+
}
|
package/src/commands/backup.js
CHANGED
|
@@ -4,7 +4,7 @@ const { getLang: _gl } = require('../utils/i18n');
|
|
|
4
4
|
const L = (tr, en) => (_gl() === 'en' ? en : tr);
|
|
5
5
|
const path = require('path');
|
|
6
6
|
const os = require('os');
|
|
7
|
-
const {
|
|
7
|
+
const { execFileSync } = require('child_process');
|
|
8
8
|
|
|
9
9
|
const NATURECO_DIR = path.join(os.homedir(), '.natureco');
|
|
10
10
|
|
|
@@ -32,7 +32,7 @@ function createBackup() {
|
|
|
32
32
|
|
|
33
33
|
if (fs.existsSync(NATURECO_DIR)) {
|
|
34
34
|
try {
|
|
35
|
-
|
|
35
|
+
execFileSync('tar', ['-czf', backupFile, '-C', path.dirname(NATURECO_DIR), path.basename(NATURECO_DIR)], {
|
|
36
36
|
stdio: 'pipe',
|
|
37
37
|
encoding: 'utf8',
|
|
38
38
|
timeout: 30000
|
|
@@ -97,7 +97,7 @@ function restoreBackup(file) {
|
|
|
97
97
|
|
|
98
98
|
console.log(chalk.yellow(`\n ⚠️ Restoring ${file}...\n`));
|
|
99
99
|
try {
|
|
100
|
-
|
|
100
|
+
execFileSync('tar', ['-xzf', backupFile, '-C', os.homedir()], { stdio: 'pipe', timeout: 15000 });
|
|
101
101
|
console.log(chalk.green(' ✅ Restore complete\n'));
|
|
102
102
|
} catch {
|
|
103
103
|
console.log(chalk.red(' ❌ Restore failed\n'));
|
package/src/commands/chat.js
CHANGED
|
@@ -87,8 +87,8 @@ async function chat(botName, options = {}) {
|
|
|
87
87
|
console.log('');
|
|
88
88
|
console.log(tui.styled(' 🌿 NatureCo Chat v4.6+', { color: tui.PALETTE.primary, bold: true }));
|
|
89
89
|
console.log(tui.styled(' ' + '─'.repeat(56), { color: tui.PALETTE.border }));
|
|
90
|
-
console.log(tui.C.muted(' Chat komutu artık ') + tui.C.brand('repl') + tui.C.muted(' komutunu çağırıyor (Phase 9 TUI engine)'));
|
|
91
|
-
console.log(tui.C.muted(' Tüm özellikler korundu: memory, sessions, hooks, custom commands'));
|
|
90
|
+
console.log(tui.C.muted(L(' Chat komutu artık ', ' The chat command now ')) + tui.C.brand('repl') + tui.C.muted(L(' komutunu çağırıyor (Phase 9 TUI engine)', ' command (Phase 9 TUI engine)')));
|
|
91
|
+
console.log(tui.C.muted(L(' Tüm özellikler korundu: memory, sessions, hooks, custom commands', ' All features preserved: memory, sessions, hooks, custom commands')));
|
|
92
92
|
console.log('');
|
|
93
93
|
return repl(replArgs);
|
|
94
94
|
}
|
|
@@ -97,11 +97,11 @@ async function chat(botName, options = {}) {
|
|
|
97
97
|
if (options.list) {
|
|
98
98
|
const sessions = listSessions('chat');
|
|
99
99
|
if (!sessions.length) {
|
|
100
|
-
console.log(chalk.gray('\nKayıtlı oturum yok.\n'));
|
|
100
|
+
console.log(chalk.gray(L('\nKayıtlı oturum yok.\n', '\nNo saved sessions.\n')));
|
|
101
101
|
return;
|
|
102
102
|
}
|
|
103
103
|
sessions.forEach(s => {
|
|
104
|
-
console.log(` [${s.id}] ${s.savedAt.slice(0, 10)} — ${s.preview || '(boş)'} (${s.messageCount} mesaj)`);
|
|
104
|
+
console.log(` [${s.id}] ${s.savedAt.slice(0, 10)} — ${s.preview || L('(boş)', '(empty)')} (${s.messageCount} ${L('mesaj', 'messages')})`);
|
|
105
105
|
});
|
|
106
106
|
console.log();
|
|
107
107
|
return;
|
|
@@ -130,7 +130,7 @@ async function chat(botName, options = {}) {
|
|
|
130
130
|
const { selectedBot } = await inquirer.prompt([{
|
|
131
131
|
type: 'list',
|
|
132
132
|
name: 'selectedBot',
|
|
133
|
-
message: 'Bot seçin:',
|
|
133
|
+
message: L('Bot seçin:', 'Select bot:'),
|
|
134
134
|
choices: botList.bots.map(b => ({ name: b.name, value: b.id })),
|
|
135
135
|
}]);
|
|
136
136
|
bot = botList.bots.find(b => b.id === selectedBot);
|
|
@@ -174,7 +174,7 @@ async function chat(botName, options = {}) {
|
|
|
174
174
|
} else if (options.continue) {
|
|
175
175
|
const last = loadLastSession('chat');
|
|
176
176
|
if (last) {
|
|
177
|
-
console.log(chalk.cyan(` ↻ Son oturum yüklendi (${last.messages.length} mesaj)\n`));
|
|
177
|
+
console.log(chalk.cyan(` ↻ ${L('Son oturum yüklendi', 'Last session loaded')} (${last.messages.length} ${L('mesaj', 'messages')})\n`));
|
|
178
178
|
}
|
|
179
179
|
session = createSession(bot.id, bot.name);
|
|
180
180
|
} else {
|
|
@@ -195,7 +195,7 @@ async function chat(botName, options = {}) {
|
|
|
195
195
|
console.clear();
|
|
196
196
|
console.log(centerText(ASCII_LOGO.map((line, i) => i < 5 ? chalk.green(line) : chalk.gray(line)).join('\n')));
|
|
197
197
|
console.log();
|
|
198
|
-
console.log(centerText(chalk.cyan(`(\\_/) Hoş geldin, ${userName} · ${displayBotName} hazır · v${version}`)));
|
|
198
|
+
console.log(centerText(chalk.cyan(`(\\_/) ${L('Hoş geldin', 'Welcome')}, ${userName} · ${displayBotName} ${L('hazır', 'ready')} · v${version}`)));
|
|
199
199
|
console.log(chalk.gray('─'.repeat(process.stdout.columns || 120)));
|
|
200
200
|
console.log();
|
|
201
201
|
|
|
@@ -224,7 +224,7 @@ async function chat(botName, options = {}) {
|
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
|
|
227
|
-
console.log(centerText(chalk.gray(`${shortModel} · /help için yardım · Ctrl+C
|
|
227
|
+
console.log(centerText(chalk.gray(`${shortModel} · /help ${L('için yardım', 'for help')} · Ctrl+C ${L('çıkış', 'exit')}`)));
|
|
228
228
|
console.log(chalk.gray('─'.repeat(process.stdout.columns || 120)));
|
|
229
229
|
console.log();
|
|
230
230
|
|
|
@@ -273,7 +273,7 @@ async function chat(botName, options = {}) {
|
|
|
273
273
|
bot = newBot;
|
|
274
274
|
conversationId = null;
|
|
275
275
|
session = createSession(bot.id, bot.name);
|
|
276
|
-
console.log(chalk.green(
|
|
276
|
+
console.log(chalk.green(`${L('Bot değişti', 'Bot changed')}: ${newBot.name}`));
|
|
277
277
|
} else {
|
|
278
278
|
console.log(chalk.red(`${L('Bot bulunamadı', 'Bot not found')}: ${newName}`));
|
|
279
279
|
}
|
|
@@ -282,18 +282,18 @@ async function chat(botName, options = {}) {
|
|
|
282
282
|
return;
|
|
283
283
|
case 'skills':
|
|
284
284
|
const skills = getSkills();
|
|
285
|
-
if (!skills.length) console.log(chalk.gray('Yüklü skill yok.'));
|
|
285
|
+
if (!skills.length) console.log(chalk.gray(L('Yüklü skill yok.', 'No skills installed.')));
|
|
286
286
|
else skills.forEach(s => console.log(chalk.cyan(`· ${s.name}`) + chalk.gray(` ${s.description}`)));
|
|
287
287
|
console.log();
|
|
288
288
|
return;
|
|
289
289
|
case 'memory':
|
|
290
290
|
if (args[0] === 'clear') {
|
|
291
291
|
clearMemory(bot.id);
|
|
292
|
-
console.log(chalk.green('✓ Hafıza temizlendi'));
|
|
292
|
+
console.log(chalk.green(L('✓ Hafıza temizlendi', '✓ Memory cleared')));
|
|
293
293
|
} else {
|
|
294
294
|
const m = loadMemory(bot.id);
|
|
295
295
|
if (m.botName) console.log(chalk.cyan('Bot: ') + m.botName);
|
|
296
|
-
if (m.name) console.log(chalk.cyan('İsim: ') + m.name);
|
|
296
|
+
if (m.name) console.log(chalk.cyan(L('İsim: ', 'Name: ')) + m.name);
|
|
297
297
|
(m.facts || []).slice(0, 8).forEach(f => {
|
|
298
298
|
const v = typeof f === 'string' ? f : f.value;
|
|
299
299
|
console.log(chalk.gray(`· ${v}`));
|
|
@@ -317,7 +317,7 @@ async function chat(botName, options = {}) {
|
|
|
317
317
|
return;
|
|
318
318
|
case 'commands':
|
|
319
319
|
const cmds = getCommands();
|
|
320
|
-
if (!cmds.length) console.log(chalk.gray('Özel komut yok.'));
|
|
320
|
+
if (!cmds.length) console.log(chalk.gray(L('Özel komut yok.', 'No custom commands.')));
|
|
321
321
|
else cmds.forEach(c => console.log(chalk.cyan(`/${c.name}`)));
|
|
322
322
|
console.log();
|
|
323
323
|
return;
|
|
@@ -406,10 +406,10 @@ async function chat(botName, options = {}) {
|
|
|
406
406
|
saveSession('chat', historyMessages, { botId: bot.id, botName: bot.name });
|
|
407
407
|
}
|
|
408
408
|
if (filesChanged > 0 || commandsRun > 0 || messagesCount > 0) {
|
|
409
|
-
console.log(chalk.gray('\n─── Session Özeti ───'));
|
|
410
|
-
if (filesChanged > 0) console.log(chalk.green(` ✓ ${filesChanged} dosya değiştirildi`));
|
|
411
|
-
if (commandsRun > 0) console.log(chalk.green(` ✓ ${commandsRun} komut
|
|
412
|
-
console.log(chalk.cyan(` ✓ ${messagesCount} mesaj gönderildi`));
|
|
409
|
+
console.log(chalk.gray(L('\n─── Session Özeti ───', '\n─── Session Summary ───')));
|
|
410
|
+
if (filesChanged > 0) console.log(chalk.green(` ✓ ${filesChanged} ${L('dosya değiştirildi', 'files changed')}`));
|
|
411
|
+
if (commandsRun > 0) console.log(chalk.green(` ✓ ${commandsRun} ${L('komut çalıştırıldı', 'commands run')}`));
|
|
412
|
+
console.log(chalk.cyan(` ✓ ${messagesCount} ${L('mesaj gönderildi', 'messages sent')}`));
|
|
413
413
|
console.log();
|
|
414
414
|
}
|
|
415
415
|
console.log(chalk.gray('👋 Goodbye!\n'));
|
package/src/commands/code.js
CHANGED
|
@@ -2,7 +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
|
+
const { execSync, execFileSync } = require('child_process');
|
|
6
6
|
const inquirer = require('../utils/inquirer-wrapper');
|
|
7
7
|
const TB = require('../utils/token-budget');
|
|
8
8
|
const chalk = require('chalk');
|
|
@@ -14,7 +14,11 @@ const { getMemoryPrompt, loadMemory } = require('../utils/memory');
|
|
|
14
14
|
const { getAgentsPrompt } = require('../utils/agents');
|
|
15
15
|
const { createSession, addMessageToSession } = require('../utils/sessions');
|
|
16
16
|
const { addToHistory } = require('../utils/history');
|
|
17
|
-
const { getToolDefinitions, executeTool } = require('../utils/tool-runner');
|
|
17
|
+
const { getToolDefinitions, executeTool } = require('../utils/tool-runner');
|
|
18
|
+
const { AgentCore } = require('../utils/agent-core');
|
|
19
|
+
const { CodingSession } = require('../utils/coding-session');
|
|
20
|
+
|
|
21
|
+
const agentCore = new AgentCore({ maxIterations: 10 });
|
|
18
22
|
|
|
19
23
|
let rl = null;
|
|
20
24
|
|
|
@@ -224,12 +228,17 @@ async function streamMessage(providerConfig, messages, tools) {
|
|
|
224
228
|
// ── Tool execution ────────────────────────────────────────────────────────────
|
|
225
229
|
const DANGEROUS = [/\brm\b/, /\brmdir\b/, /\bdelete\b/i, /\bdrop\b/i, /\btruncate\b/i];
|
|
226
230
|
|
|
227
|
-
async function runToolCall(toolCall, stats, dryRun = false) {
|
|
231
|
+
async function runToolCall(toolCall, stats, dryRun = false, codingSession = null) {
|
|
232
|
+
const guard = agentCore.assess({ name: toolCall.name, input: toolCall.input });
|
|
233
|
+
if (guard.blocked) return { success: false, error: guard.reason || `blocked_by_guardrails: ${toolCall.name}` };
|
|
228
234
|
const inputPreview = JSON.stringify(toolCall.input).slice(0, 60);
|
|
229
235
|
|
|
230
|
-
const needsConfirm =
|
|
236
|
+
const needsConfirm =
|
|
231
237
|
toolCall.name === 'write_file' ||
|
|
232
|
-
(toolCall.name === 'bash' && DANGEROUS.some(re => re.test(toolCall.input.command || '')));
|
|
238
|
+
(toolCall.name === 'bash' && DANGEROUS.some(re => re.test(toolCall.input.command || '')));
|
|
239
|
+
|
|
240
|
+
const risk = codingSession?.riskSummary(toolCall);
|
|
241
|
+
if (risk && risk.level !== 'low') console.log(chalk.gray(` Risk: ${risk.level} (${risk.risks.join(', ')})`));
|
|
233
242
|
|
|
234
243
|
// Dry-run: write_file'ı engelle, diff göster
|
|
235
244
|
if (dryRun && toolCall.name === 'write_file') {
|
|
@@ -261,8 +270,12 @@ async function runToolCall(toolCall, stats, dryRun = false) {
|
|
|
261
270
|
}
|
|
262
271
|
}
|
|
263
272
|
|
|
264
|
-
const spinner = startSpinner(`${toolCall.name} ${inputPreview}`);
|
|
265
|
-
|
|
273
|
+
const spinner = startSpinner(`${toolCall.name} ${inputPreview}`);
|
|
274
|
+
if (!dryRun && codingSession && (toolCall.name === 'write_file' || toolCall.name === 'edit_file')) {
|
|
275
|
+
codingSession.capture(toolCall.input.path);
|
|
276
|
+
}
|
|
277
|
+
const result = await executeTool(toolCall.name, toolCall.input);
|
|
278
|
+
agentCore.record({ name: toolCall.name, input: toolCall.input }, result);
|
|
266
279
|
stopSpinner(spinner, toolCall.name, result.success !== false);
|
|
267
280
|
|
|
268
281
|
if (result.success !== false) {
|
|
@@ -310,7 +323,8 @@ async function runTests(projectIndex, conversationMessages, tools, providerConfi
|
|
|
310
323
|
}
|
|
311
324
|
|
|
312
325
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
313
|
-
async function code(targetFile, options = {}) {
|
|
326
|
+
async function code(targetFile, options = {}) {
|
|
327
|
+
const codingSession = new CodingSession();
|
|
314
328
|
const workDir = options.dir || process.cwd();
|
|
315
329
|
const apiKey = getApiKey();
|
|
316
330
|
const config = getConfig();
|
|
@@ -507,15 +521,35 @@ ${indexPrompt}`);
|
|
|
507
521
|
['/run <cmd>',L('Komutu çalıştır, hata varsa düzelt', 'Run command, fix errors if any')],
|
|
508
522
|
['/test', L('Testleri çalıştır, hata varsa düzelt', 'Run tests, fix errors if any')],
|
|
509
523
|
['/git', L('Git durumu ve son commitler', 'Git status and recent commits')],
|
|
510
|
-
['/commit', L('Staged değişiklikleri AI ile commit et', 'Commit staged changes with AI')],
|
|
524
|
+
['/commit', L('Staged değişiklikleri AI ile commit et', 'Commit staged changes with AI')],
|
|
525
|
+
['/undo', L('Son dosya değişikliğini geri al', 'Undo the last file change')],
|
|
526
|
+
['/retry', L('Son isteği yeniden çalıştır', 'Retry the last request')],
|
|
527
|
+
['/compact', L('Konuşma bağlamını sıkıştır', 'Compact conversation context')],
|
|
511
528
|
['/help', L('Bu yardım', 'This help')],
|
|
512
529
|
].forEach(([c, d]) => console.log(' ' + chalk.cyan(c.padEnd(12)) + chalk.gray(d)));
|
|
513
530
|
console.log(chalk.gray(' Ctrl+C'.padEnd(14) + L('Çıkış', 'Exit')));
|
|
514
531
|
console.log();
|
|
515
532
|
return;
|
|
516
|
-
case 'clear':
|
|
533
|
+
case 'clear':
|
|
517
534
|
console.clear();
|
|
518
|
-
return;
|
|
535
|
+
return;
|
|
536
|
+
case 'undo': {
|
|
537
|
+
const undone = codingSession.undo();
|
|
538
|
+
console.log(undone.ok ? chalk.green(` ✓ ${undone.path}`) : chalk.yellow(` ${undone.error}`));
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
case 'compact': {
|
|
542
|
+
const compacted = codingSession.compact(conversationMessages);
|
|
543
|
+
conversationMessages.splice(0, conversationMessages.length, ...compacted.messages);
|
|
544
|
+
console.log(chalk.green(` ✓ Context compacted: ${compacted.before} → ${compacted.after}`));
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
case 'retry': {
|
|
548
|
+
const previous = codingSession.retryMessage();
|
|
549
|
+
if (!previous) console.log(chalk.yellow(L(' Tekrarlanacak istek yok.', ' No request to retry.')));
|
|
550
|
+
else await handleMessage(previous);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
519
553
|
case 'summary':
|
|
520
554
|
case 'done': {
|
|
521
555
|
const sum = await generateSummary(conversationMessages, providerConfig);
|
|
@@ -591,7 +625,7 @@ ${indexPrompt}`);
|
|
|
591
625
|
console.log(chalk.cyan(`\n ${L('Önerilen', 'Suggested')}: ${chalk.white(commitMsg)}\n`));
|
|
592
626
|
const ok = await confirmAction(L('Commit edilsin mi?', 'Commit?'));
|
|
593
627
|
if (ok) {
|
|
594
|
-
|
|
628
|
+
execFileSync('git', ['commit', '-m', commitMsg], { cwd: workDir, stdio: 'pipe' });
|
|
595
629
|
console.log(chalk.green(L(' ✓ Commit yapıldı!\n', ' ✓ Committed!\n')));
|
|
596
630
|
} else {
|
|
597
631
|
console.log(chalk.gray(L(' İptal edildi.\n', ' Cancelled.\n')));
|
|
@@ -607,7 +641,8 @@ ${indexPrompt}`);
|
|
|
607
641
|
}
|
|
608
642
|
}
|
|
609
643
|
|
|
610
|
-
stats.messageCount++;
|
|
644
|
+
stats.messageCount++;
|
|
645
|
+
codingSession.rememberUserMessage(userMessage);
|
|
611
646
|
console.log(chalk.white('You ') + userMessage);
|
|
612
647
|
conversationMessages.push({ role: 'user', content: userMessage });
|
|
613
648
|
|
|
@@ -655,7 +690,7 @@ ${indexPrompt}`);
|
|
|
655
690
|
|
|
656
691
|
for (let ti = 0; ti < streamResult.toolCalls.length; ti++) {
|
|
657
692
|
const toolCall = streamResult.toolCalls[ti];
|
|
658
|
-
const result = await runToolCall(toolCall, stats, options.dryRun);
|
|
693
|
+
const result = await runToolCall(toolCall, stats, options.dryRun, codingSession);
|
|
659
694
|
const resultStr = result.success !== false
|
|
660
695
|
? (result.output || JSON.stringify(result))
|
|
661
696
|
: `${L('Hata', 'Error')}: ${result.error}`;
|
package/src/commands/code_v5.js
CHANGED
|
@@ -33,6 +33,9 @@ const { getFallbackChain } = require("../utils/fallback-chain");
|
|
|
33
33
|
const { getTaskManager } = require("../utils/tasks");
|
|
34
34
|
const { buildTiers, assemble, discoverProjectRules } = require("../utils/system-prompt");
|
|
35
35
|
const { buildSkillIndex } = require("../utils/skill-index");
|
|
36
|
+
const { AgentCore } = require("../utils/agent-core");
|
|
37
|
+
|
|
38
|
+
const agentCore = new AgentCore({ maxIterations: 10 });
|
|
36
39
|
|
|
37
40
|
const IS_MAC = os.platform() === "darwin";
|
|
38
41
|
const MAX_ITERATIONS = 10;
|
|
@@ -624,9 +627,16 @@ async function codeV5(targetPath) {
|
|
|
624
627
|
const PARALLEL_SAFE_TOOLS = new Set(['read_file', 'file_search', 'grep_search', 'web_search', 'web_readability', 'duckduckgo_search', 'exa_search', 'searxng_search', 'firecrawl', 'memory_search', 'memory']);
|
|
625
628
|
|
|
626
629
|
async function processToolCalls(reply, config, toolDefs, messages, onToolResult) {
|
|
630
|
+
agentCore.startIteration();
|
|
631
|
+
const coreBlocked = new Map();
|
|
627
632
|
for (const tc of reply.tool_calls) {
|
|
628
633
|
let args = {};
|
|
629
634
|
try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
|
|
635
|
+
const guard = agentCore.assess({ name: tc.function.name, input: args });
|
|
636
|
+
if (guard.blocked) {
|
|
637
|
+
coreBlocked.set(tc.id, guard.reason || "blocked_by_guardrails");
|
|
638
|
+
continue;
|
|
639
|
+
}
|
|
630
640
|
const risk = assessRisk(tc.function.name, args);
|
|
631
641
|
|
|
632
642
|
// Built-in risk assessment
|
|
@@ -687,7 +697,11 @@ async function processToolCalls(reply, config, toolDefs, messages, onToolResult)
|
|
|
687
697
|
|
|
688
698
|
messages.push({ role: "assistant", content: reply.content || null, tool_calls: reply.tool_calls });
|
|
689
699
|
|
|
690
|
-
const
|
|
700
|
+
for (const [id, reason] of coreBlocked) {
|
|
701
|
+
messages.push({ role: "tool", tool_call_id: id, content: "ERROR: " + reason });
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
const parsed = reply.tool_calls.filter(tc => !coreBlocked.has(tc.id)).map(tc => {
|
|
691
705
|
let args = {};
|
|
692
706
|
try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
|
|
693
707
|
return { name: tc.function.name, args, id: tc.id };
|