natureco-cli 5.0.0 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/natureco.js +9 -3
- package/package.json +1 -1
- package/src/commands/code_v5.js +279 -0
- package/src/tools/file_search.js +11 -4
package/bin/natureco.js
CHANGED
|
@@ -221,13 +221,19 @@ program
|
|
|
221
221
|
|
|
222
222
|
program
|
|
223
223
|
.command('code [file]')
|
|
224
|
-
.description('Agentic coding modu —
|
|
224
|
+
.description('Agentic coding modu v5 — Claude Code alternative (47 tools, TUI, auto tool selection)')
|
|
225
225
|
.option('--dir <path>', 'çalışma dizini', process.cwd())
|
|
226
226
|
.option('--no-stream', 'streaming devre dışı')
|
|
227
227
|
.option('--dry-run', 'değişiklikleri göster ama uygulama')
|
|
228
|
+
.option('--legacy', 'v2.23 eski code agent kullan')
|
|
228
229
|
.action((file, options) => {
|
|
229
|
-
|
|
230
|
-
|
|
230
|
+
if (options.legacy) {
|
|
231
|
+
const codeCmd = require('../src/commands/code');
|
|
232
|
+
codeCmd(file, options);
|
|
233
|
+
} else {
|
|
234
|
+
const codeV5 = require('../src/commands/code_v5');
|
|
235
|
+
codeV5(options.dir || (file ? path.dirname(path.resolve(file)) : null));
|
|
236
|
+
}
|
|
231
237
|
});
|
|
232
238
|
|
|
233
239
|
program
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.0.
|
|
3
|
+
"version": "5.0.1",
|
|
4
4
|
"description": "OpenClaw'dan daha güvenli, daha hızlı, daha ucuz AI agent CLI. Multi-agent, self-evolving skills, audit log, maliyet optimizasyonu ve NatureCo platform-native.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"natureco": "bin/natureco.js"
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* natureco code v5 — Claude Code alternatif (v5.0)
|
|
3
|
+
*
|
|
4
|
+
* TUI engine + 47 tool + auto tool selection ile modern code agent.
|
|
5
|
+
* v2.23'ün code.js'inin yerini alir.
|
|
6
|
+
*
|
|
7
|
+
* Ozellikler:
|
|
8
|
+
* - TUI welcome card (round border, status pill)
|
|
9
|
+
* - 47 tool otomatik yuklu (OpenAI function calling)
|
|
10
|
+
* - Multi-turn tool execution (max 10 iter)
|
|
11
|
+
* - Auto-context: dosya/klasor tarama (proje baglami)
|
|
12
|
+
* - Token tracking
|
|
13
|
+
* - Approval prompt (write_file, bash, dangerous komutlar)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const path = require("path");
|
|
18
|
+
const os = require("os");
|
|
19
|
+
const readline = require("readline");
|
|
20
|
+
const chalk = require("chalk");
|
|
21
|
+
const tui = require("../utils/tui");
|
|
22
|
+
const { getConfig } = require("../utils/config");
|
|
23
|
+
const { loadToolDefinitions, executeTool } = require("../utils/tools");
|
|
24
|
+
|
|
25
|
+
const IS_MAC = os.platform() === "darwin";
|
|
26
|
+
const MAX_ITERATIONS = 10;
|
|
27
|
+
|
|
28
|
+
function isMiniMax(url) {
|
|
29
|
+
return url && (url.includes("minimax.io") || url.includes("minimaxi.com"));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function apiRequest(url, key, body) {
|
|
33
|
+
const https = require("https");
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const isMM = isMiniMax(url);
|
|
36
|
+
const endpoint = isMM
|
|
37
|
+
? url.replace(/\/+$/, "") + "/v1/text/chatcompletion_v2"
|
|
38
|
+
: url.replace(/\/+$/, "") + "/chat/completions";
|
|
39
|
+
const data = JSON.stringify(body);
|
|
40
|
+
const req = https.request(endpoint, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "Authorization": "Bearer " + key, "Content-Type": "application/json" },
|
|
43
|
+
timeout: 60000,
|
|
44
|
+
}, res => {
|
|
45
|
+
let body = "";
|
|
46
|
+
res.on("data", c => body += c);
|
|
47
|
+
res.on("end", () => {
|
|
48
|
+
if (res.statusCode === 200) {
|
|
49
|
+
try { resolve(JSON.parse(body)); } catch (e) { reject(new Error("Parse hatasi")); }
|
|
50
|
+
} else reject(new Error("HTTP " + res.statusCode + ": " + body.slice(0, 200)));
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
req.on("error", reject);
|
|
54
|
+
req.on("timeout", () => req.destroy() && reject(new Error("Timeout")));
|
|
55
|
+
req.write(data);
|
|
56
|
+
req.end();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function sendMessageWithTools(providerUrl, providerKey, model, messages, toolDefs) {
|
|
61
|
+
const isMM = isMiniMax(providerUrl);
|
|
62
|
+
const tools = toolDefs.map(t => ({
|
|
63
|
+
type: "function",
|
|
64
|
+
function: { name: t.name, description: t.description, parameters: t.parameters },
|
|
65
|
+
}));
|
|
66
|
+
const body = {
|
|
67
|
+
model,
|
|
68
|
+
messages,
|
|
69
|
+
temperature: 0.3,
|
|
70
|
+
max_tokens: 4096,
|
|
71
|
+
stream: false,
|
|
72
|
+
};
|
|
73
|
+
if (tools.length > 0) {
|
|
74
|
+
body.tools = tools;
|
|
75
|
+
body.tool_choice = "auto";
|
|
76
|
+
}
|
|
77
|
+
const res = await apiRequest(providerUrl, providerKey, body);
|
|
78
|
+
return res.choices?.[0]?.message || {};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function confirm(prompt) {
|
|
82
|
+
return new Promise(resolve => {
|
|
83
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
84
|
+
rl.question(chalk.yellow("\n ⚠ " + prompt + "\n Devam edilsin mi? (Y/n) "), ans => {
|
|
85
|
+
rl.close();
|
|
86
|
+
resolve(!ans || ans.toLowerCase().startsWith("y"));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function printToolCall(name, args, result) {
|
|
92
|
+
const argsStr = JSON.stringify(args).slice(0, 100);
|
|
93
|
+
console.log("\n " + tui.styled(" 🔧 Tool: " + name, { color: tui.PALETTE.accent, bold: true }));
|
|
94
|
+
console.log(" " + tui.styled(" Args: " + argsStr, { color: tui.PALETTE.muted }));
|
|
95
|
+
if (result) {
|
|
96
|
+
if (result.error) {
|
|
97
|
+
console.log(" " + tui.styled(" ✗ Hata: " + result.error.slice(0, 200), { color: tui.PALETTE.danger }));
|
|
98
|
+
} else if (result.success !== false) {
|
|
99
|
+
const out = typeof result === "string" ? result.slice(0, 200) : JSON.stringify(result).slice(0, 200);
|
|
100
|
+
console.log(" " + tui.styled(" ✓ Sonuç: " + out, { color: tui.PALETTE.success }));
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function scanProject(cwd) {
|
|
106
|
+
try {
|
|
107
|
+
const files = fs.readdirSync(cwd, { withFileTypes: true });
|
|
108
|
+
const summary = {
|
|
109
|
+
totalFiles: 0,
|
|
110
|
+
dirs: [],
|
|
111
|
+
configFiles: [],
|
|
112
|
+
readme: null,
|
|
113
|
+
};
|
|
114
|
+
for (const f of files) {
|
|
115
|
+
if (f.name.startsWith(".") || f.name === "node_modules") continue;
|
|
116
|
+
if (f.isDirectory()) summary.dirs.push(f.name);
|
|
117
|
+
else {
|
|
118
|
+
summary.totalFiles++;
|
|
119
|
+
if (f.name === "package.json" || f.name === "Cargo.toml" || f.name === "go.mod" || f.name.endsWith(".lock")) {
|
|
120
|
+
summary.configFiles.push(f.name);
|
|
121
|
+
}
|
|
122
|
+
if (f.name.toLowerCase() === "readme.md") summary.readme = f.name;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return summary;
|
|
126
|
+
} catch {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function codeV5(targetPath) {
|
|
132
|
+
const config = getConfig();
|
|
133
|
+
if (!config.providerUrl || !config.providerApiKey) {
|
|
134
|
+
console.log(tui.C.red("\n ❌ Provider ayarlı değil. Önce: natureco setup\n"));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const cwd = targetPath ? path.resolve(targetPath) : process.cwd();
|
|
139
|
+
const projectCtx = scanProject(cwd);
|
|
140
|
+
const toolDefs = loadToolDefinitions();
|
|
141
|
+
|
|
142
|
+
// Header / Welcome
|
|
143
|
+
console.log("");
|
|
144
|
+
console.log(tui.styled(" ╭" + "─".repeat(58) + "╮", { color: tui.PALETTE.primary }));
|
|
145
|
+
console.log(tui.styled(" │", { color: tui.PALETTE.primary }) + " ⚡ " + tui.styled("NatureCo Code Agent v5", { color: tui.PALETTE.primary, bold: true }) + tui.C.muted(" Claude Code alternative") + "".padEnd(8) + tui.styled(" │", { color: tui.PALETTE.primary }));
|
|
146
|
+
console.log(tui.styled(" │", { color: tui.PALETTE.primary }) + tui.C.muted(" Proje: ") + tui.styled(cwd.padEnd(47), { color: tui.PALETTE.text }) + tui.styled(" │", { color: tui.PALETTE.primary }));
|
|
147
|
+
console.log(tui.styled(" │", { color: tui.PALETTE.primary }) + tui.C.muted(" Model: ") + tui.styled((config.providerModel || "—").padEnd(47), { color: tui.PALETTE.text }) + tui.styled(" │", { color: tui.PALETTE.primary }));
|
|
148
|
+
console.log(tui.styled(" │", { color: tui.PALETTE.primary }) + tui.C.muted(" Tools: ") + tui.styled(String(toolDefs.length).padEnd(47), { color: tui.PALETTE.success, bold: true }) + tui.styled(" │", { color: tui.PALETTE.primary }));
|
|
149
|
+
console.log(tui.styled(" ╰" + "─".repeat(58) + "╯", { color: tui.PALETTE.primary }));
|
|
150
|
+
|
|
151
|
+
// Project context
|
|
152
|
+
if (projectCtx) {
|
|
153
|
+
console.log("\n " + tui.C.muted("📂 Proje bağlamı:"));
|
|
154
|
+
if (projectCtx.readme) console.log(" " + tui.C.muted("• README: ") + tui.C.text(projectCtx.readme));
|
|
155
|
+
if (projectCtx.configFiles.length) {
|
|
156
|
+
console.log(" " + tui.C.muted("• Config: ") + tui.C.text(projectCtx.configFiles.join(", ")));
|
|
157
|
+
}
|
|
158
|
+
if (projectCtx.dirs.length) {
|
|
159
|
+
console.log(" " + tui.C.muted("• Klasörler: ") + tui.C.text(projectCtx.dirs.slice(0, 8).join(", ")));
|
|
160
|
+
}
|
|
161
|
+
console.log(" " + tui.C.muted("• Toplam dosya: ") + tui.C.text(String(projectCtx.totalFiles)));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
console.log("\n " + tui.C.muted("Komutlar:"));
|
|
165
|
+
console.log(" " + tui.C.amber("/summary") + tui.C.muted(" — Oturum özeti"));
|
|
166
|
+
console.log(" " + tui.C.amber("/done") + tui.C.muted(" — Özet + çıkış"));
|
|
167
|
+
console.log(" " + tui.C.amber("Ctrl+C") + tui.C.muted(" — Çıkış"));
|
|
168
|
+
console.log("");
|
|
169
|
+
|
|
170
|
+
// System prompt
|
|
171
|
+
const systemPrompt = [
|
|
172
|
+
"Sen NatureCo Code Agent v5'sin — Claude Code alternatifi bir terminal AI asistanısın.",
|
|
173
|
+
"47 tool'un var: dosya okuma/yazma, bash, Python, web search, macOS native, image generation, TTS, vs.",
|
|
174
|
+
"Kullanıcının isteğini analiz et, uygun tool'ları kullan, sonuçları özetle.",
|
|
175
|
+
"Türkçe cevap ver.",
|
|
176
|
+
"Kullanıcıdan onay almadan dosya yazma veya tehlikeli komut çalıştırma.",
|
|
177
|
+
].join(" ");
|
|
178
|
+
|
|
179
|
+
let messages = [{ role: "system", content: systemPrompt }];
|
|
180
|
+
let totalIn = 0, totalOut = 0;
|
|
181
|
+
let filesChanged = 0, commandsRun = 0;
|
|
182
|
+
const startTime = Date.now();
|
|
183
|
+
|
|
184
|
+
// Input loop
|
|
185
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
186
|
+
process.stdout.write("\n " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true }));
|
|
187
|
+
|
|
188
|
+
const ask = () => {
|
|
189
|
+
rl.question("", async (input) => {
|
|
190
|
+
input = input.trim();
|
|
191
|
+
if (!input) { process.stdout.write(" " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true })); return ask(); }
|
|
192
|
+
if (input === "/summary" || input === "/done") {
|
|
193
|
+
printSummary(filesChanged, commandsRun, messages.length - 1, startTime);
|
|
194
|
+
rl.close();
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (input === "exit" || input === "quit") { rl.close(); return; }
|
|
198
|
+
|
|
199
|
+
messages.push({ role: "user", content: input });
|
|
200
|
+
process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
|
|
201
|
+
|
|
202
|
+
// Multi-turn tool loop
|
|
203
|
+
let iter = 0;
|
|
204
|
+
while (iter < MAX_ITERATIONS) {
|
|
205
|
+
iter++;
|
|
206
|
+
try {
|
|
207
|
+
const reply = await sendMessageWithTools(
|
|
208
|
+
config.providerUrl, config.providerApiKey, config.providerModel,
|
|
209
|
+
messages, toolDefs
|
|
210
|
+
);
|
|
211
|
+
if (reply.content) {
|
|
212
|
+
process.stdout.write(reply.content);
|
|
213
|
+
messages.push({ role: "assistant", content: reply.content });
|
|
214
|
+
totalOut += Math.ceil(reply.content.length / 4);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (reply.tool_calls && reply.tool_calls.length > 0) {
|
|
218
|
+
// Approval for dangerous tools
|
|
219
|
+
let approved = true;
|
|
220
|
+
const dangerous = ["bash", "write_file", "edit_file", "mac_app_quit"];
|
|
221
|
+
for (const tc of reply.tool_calls) {
|
|
222
|
+
if (dangerous.includes(tc.function.name)) {
|
|
223
|
+
process.stdout.write("\n");
|
|
224
|
+
approved = await confirm(tc.function.name + " çalıştırılsın mı?");
|
|
225
|
+
if (!approved) break;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
if (!approved) {
|
|
229
|
+
console.log("\n " + tui.C.yellow("⚠ Kullanıcı onayı iptal etti, tool çalıştırılmadı."));
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
messages.push({ role: "assistant", content: reply.content || null, tool_calls: reply.tool_calls });
|
|
234
|
+
for (const tc of reply.tool_calls) {
|
|
235
|
+
const name = tc.function.name;
|
|
236
|
+
let args = {};
|
|
237
|
+
try { args = JSON.parse(tc.function.arguments || "{}"); } catch {}
|
|
238
|
+
printToolCall(name, args);
|
|
239
|
+
if (name === "bash" || name === "shell_command") commandsRun++;
|
|
240
|
+
if (name === "write_file" || name === "edit_file") filesChanged++;
|
|
241
|
+
const result = await executeTool(name, args, toolDefs);
|
|
242
|
+
printToolCall(name, args, result);
|
|
243
|
+
const out = result.error
|
|
244
|
+
? "ERROR: " + result.error
|
|
245
|
+
: (typeof result.result === "string" ? result.result : JSON.stringify(result.result));
|
|
246
|
+
messages.push({ role: "tool", tool_call_id: tc.id, content: (out || "(empty)").slice(0, 8000) });
|
|
247
|
+
totalIn += Math.ceil(out.length / 4);
|
|
248
|
+
}
|
|
249
|
+
process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
break;
|
|
253
|
+
} catch (e) {
|
|
254
|
+
console.log("\n " + tui.C.red("❌ " + e.message));
|
|
255
|
+
break;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
totalIn += Math.ceil(input.length / 4);
|
|
260
|
+
process.stdout.write("\n\n " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true }));
|
|
261
|
+
ask();
|
|
262
|
+
});
|
|
263
|
+
};
|
|
264
|
+
ask();
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function printSummary(files, cmds, msgs, startTime) {
|
|
268
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1000);
|
|
269
|
+
const min = Math.floor(elapsed / 60);
|
|
270
|
+
const sec = elapsed % 60;
|
|
271
|
+
console.log("\n " + tui.styled("─── Session Özeti ───", { color: tui.PALETTE.primary, bold: true }));
|
|
272
|
+
console.log(" " + tui.C.green(" ✓ " + files + " dosya değiştirildi"));
|
|
273
|
+
console.log(" " + tui.C.green(" ✓ " + cmds + " komut çalıştırıldı"));
|
|
274
|
+
console.log(" " + tui.C.amber(" ✓ " + msgs + " mesaj değişimi"));
|
|
275
|
+
console.log(" " + tui.C.muted(" ⏱ " + min + "dk " + sec + "sn"));
|
|
276
|
+
console.log("");
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = codeV5;
|
package/src/tools/file_search.js
CHANGED
|
@@ -21,11 +21,18 @@ async function searchFiles({ pattern, basePath = null, maxResults = 100 }) {
|
|
|
21
21
|
const results = [];
|
|
22
22
|
|
|
23
23
|
// Basit glob regex donusumu (glob deseni)
|
|
24
|
+
// **/ ve ** pattern'lerini placeholder yap (once kalsin),
|
|
25
|
+
// sonra escape et, en son placeholder'lari replace et.
|
|
26
|
+
// Glob -> regex: **/* is once, ** is sonra, sonra *, ?
|
|
24
27
|
const regexPattern = pattern
|
|
25
|
-
.replace(
|
|
26
|
-
.replace(
|
|
27
|
-
.replace(
|
|
28
|
-
.replace(
|
|
28
|
+
.replace(/\*\*\//g, '@@DSLASH@@') // **/ -> placeholder
|
|
29
|
+
.replace(/\*\*/g, '@@DSTAR@@') // ** -> placeholder
|
|
30
|
+
.replace(/\?/g, '@@QMARK@@') // ? -> placeholder
|
|
31
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // diger ozel karakterleri escape
|
|
32
|
+
.replace(/@@DSLASH@@/g, '(?:.*\\/)?') // **/ -> (?:.*\/)? (sifir veya daha fazla dizin)
|
|
33
|
+
.replace(/@@DSTAR@@/g, '.*') // ** -> .*
|
|
34
|
+
.replace(/@@QMARK@@/g, '[^/]') // ? -> [^/]
|
|
35
|
+
.replace(/\*/g, '[^/]*');
|
|
29
36
|
|
|
30
37
|
const regex = new RegExp("^" + regexPattern + "$");
|
|
31
38
|
|