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 CHANGED
@@ -221,13 +221,19 @@ program
221
221
 
222
222
  program
223
223
  .command('code [file]')
224
- .description('Agentic coding modu — dosya oku, değiştir, komut çalıştır')
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
- const codeCmd = require('../src/commands/code');
230
- codeCmd(file, options);
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.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;
@@ -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(/[.+^${}()|[\]\\]/g, "\\$&")
26
- .replace(/\\\*\\\*/g, ".*")
27
- .replace(/\\\*/g, "[^/]*")
28
- .replace(/\\\?/g, "[^/]");
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