natureco-cli 5.0.0 → 5.1.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/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/src/tools/skill_generate.js +146 -0
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.
|
|
3
|
+
"version": "5.1.0",
|
|
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
|
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* skill_generate - Self-generating skill (v5.1.0)
|
|
3
|
+
*
|
|
4
|
+
* Parton'un vizyonu: "Ihtiyaca gore skill yoksa kendi uretsin"
|
|
5
|
+
*
|
|
6
|
+
* Akis:
|
|
7
|
+
* 1. Kullanici bir istek yapar (ornek: "tum pdf'leri birlestir")
|
|
8
|
+
* 2. Mevcut tool/skill'ler yoksa bu tool cagirilir
|
|
9
|
+
* 3. LLM'a skill taslagi uretmesi icin istek gonderilir
|
|
10
|
+
* 4. SKILL.md dosyasi yazilir, kaydedilir, hemen kullanilir
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require("fs");
|
|
14
|
+
const path = require("path");
|
|
15
|
+
const os = require("os");
|
|
16
|
+
const https = require("https");
|
|
17
|
+
|
|
18
|
+
const SKILLS_DIR = path.join(os.homedir(), ".natureco", "skills");
|
|
19
|
+
|
|
20
|
+
function getConfig() {
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(fs.readFileSync(path.join(os.homedir(), ".natureco", "config.json"), "utf8"));
|
|
23
|
+
} catch { return {}; }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isMiniMax(url) {
|
|
27
|
+
return url && (url.includes("minimax.io") || url.includes("minimaxi.com"));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function apiRequest(url, key, body) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const isMM = isMiniMax(url);
|
|
33
|
+
const endpoint = isMM
|
|
34
|
+
? url.replace(/\/$/, "") + "/v1/text/chatcompletion_v2"
|
|
35
|
+
: url.replace(/\/$/, "") + "/chat/completions";
|
|
36
|
+
const req = https.request(endpoint, {
|
|
37
|
+
method: "POST",
|
|
38
|
+
headers: { "Authorization": "Bearer " + key, "Content-Type": "application/json" },
|
|
39
|
+
timeout: 30000,
|
|
40
|
+
}, res => {
|
|
41
|
+
let data = "";
|
|
42
|
+
res.on("data", c => data += c);
|
|
43
|
+
res.on("end", () => {
|
|
44
|
+
if (res.statusCode === 200) {
|
|
45
|
+
try { resolve(JSON.parse(data)); } catch (e) { reject(new Error("Parse hatasi")); }
|
|
46
|
+
} else reject(new Error("HTTP " + res.statusCode + ": " + data.slice(0, 200)));
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
req.on("error", reject);
|
|
50
|
+
req.on("timeout", () => req.destroy() && reject(new Error("Timeout")));
|
|
51
|
+
req.write(JSON.stringify(body));
|
|
52
|
+
req.end();
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function slugify(text) {
|
|
57
|
+
return (text || "")
|
|
58
|
+
.toLowerCase()
|
|
59
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
60
|
+
.replace(/^-+|-+$/g, "")
|
|
61
|
+
.slice(0, 40) || "custom-skill";
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function generateSkillInstructions(taskDescription, skillName) {
|
|
65
|
+
const cfg = getConfig();
|
|
66
|
+
if (!cfg.providerUrl || !cfg.providerApiKey) {
|
|
67
|
+
throw new Error("Provider ayarli degil. Once: natureco setup");
|
|
68
|
+
}
|
|
69
|
+
const promptText = "Sen NatureCo CLI icin SKILL talimatlari yazarsin. Asagidaki gorev icin System Prompt'a eklenecek Turkce talimat yaz:\n\nGOREV: " + taskDescription + "\n\nKURALLAR:\n- Turkce, kisa, oz, madde madde yaz\n- 5-10 satir, dogrudan uygulanabilir\n- Hangi araclarin kullanilacagi (dosya/shell/web) acikca belirt\n- Ornek bir kucuk kullanim ornegi ekle\n- Yanlis ciktilari nasil onlenmesi gerektigini yaz\n\nDirekt SKILL.md icerigini yaz, baska aciklama yapma.";
|
|
70
|
+
|
|
71
|
+
const body = {
|
|
72
|
+
model: cfg.providerModel || "MiniMax-M2.5",
|
|
73
|
+
messages: [
|
|
74
|
+
{ role: "system", content: "Sen bir CLI skill talimat yazarsin. Kisa ve net Turkce yaz. Markdown kullanma, duz metin yaz." },
|
|
75
|
+
{ role: "user", content: promptText },
|
|
76
|
+
],
|
|
77
|
+
temperature: 0.5,
|
|
78
|
+
max_tokens: 1500,
|
|
79
|
+
};
|
|
80
|
+
const res = await apiRequest(cfg.providerUrl, cfg.providerApiKey, body);
|
|
81
|
+
const content = res.choices && res.choices[0] && res.choices[0].message ? (res.choices[0].message.content || "") : "";
|
|
82
|
+
return content.trim();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function generateSkill(params) {
|
|
86
|
+
const taskDescription = params.taskDescription;
|
|
87
|
+
const requestedName = params.skillName;
|
|
88
|
+
if (!taskDescription) return { success: false, error: "taskDescription gerekli" };
|
|
89
|
+
|
|
90
|
+
const skillName = requestedName || slugify(taskDescription);
|
|
91
|
+
const skillDir = path.join(SKILLS_DIR, skillName);
|
|
92
|
+
|
|
93
|
+
if (fs.existsSync(skillDir)) {
|
|
94
|
+
return { success: true, message: "Skill zaten mevcut", path: skillDir, skillName: skillName, alreadyExisted: true };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
let skillContent;
|
|
98
|
+
try {
|
|
99
|
+
skillContent = await generateSkillInstructions(taskDescription, skillName);
|
|
100
|
+
} catch (e) {
|
|
101
|
+
return { success: false, error: "Skill talimati uretilemedi: " + e.message };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!skillContent || skillContent.length < 50) {
|
|
105
|
+
return { success: false, error: "LLM bos icerik uretti" };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
fs.mkdirSync(skillDir, { recursive: true });
|
|
109
|
+
fs.writeFileSync(path.join(skillDir, "SKILL.md"), skillContent, "utf8");
|
|
110
|
+
const meta = {
|
|
111
|
+
name: skillName,
|
|
112
|
+
description: "Otomatik uretildi: " + taskDescription.slice(0, 100),
|
|
113
|
+
author: "skill_generate (auto)",
|
|
114
|
+
version: "1.0.0",
|
|
115
|
+
tags: ["auto-generated", "user-created"],
|
|
116
|
+
autoGenerated: true,
|
|
117
|
+
taskDescription: taskDescription,
|
|
118
|
+
createdAt: new Date().toISOString(),
|
|
119
|
+
};
|
|
120
|
+
fs.writeFileSync(path.join(skillDir, "metadata.json"), JSON.stringify(meta, null, 2), "utf8");
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
success: true,
|
|
124
|
+
message: "Yeni skill olusturuldu ve hemen kullanilabilir!",
|
|
125
|
+
skillName: skillName,
|
|
126
|
+
path: skillDir,
|
|
127
|
+
content: skillContent.slice(0, 300),
|
|
128
|
+
note: "REPL'i yeniden acarsaniz yeni skill yuklenecek. /skills list ile test edebilirsiniz.",
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = {
|
|
133
|
+
name: "skill_generate",
|
|
134
|
+
description: "Olmayan bir ozellik icin LLM ile yeni bir skill talimati uretir, kaydeder ve hemen kullanima sunar. 'PDF birlestir', 'JSON duzenle' gibi gorevler icin.",
|
|
135
|
+
inputSchema: {
|
|
136
|
+
type: "object",
|
|
137
|
+
properties: {
|
|
138
|
+
taskDescription: { type: "string", description: "Skill'in ne yapacagi (ornek: 'PDF dosyalarini tek dosyada birlestir')" },
|
|
139
|
+
skillName: { type: "string", description: "Ozel skill adi (opsiyonel, otomatik uretilir)" },
|
|
140
|
+
},
|
|
141
|
+
required: ["taskDescription"],
|
|
142
|
+
},
|
|
143
|
+
async execute(params) {
|
|
144
|
+
return await generateSkill(params);
|
|
145
|
+
},
|
|
146
|
+
};
|