natureco-cli 5.21.0 → 5.23.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 +7 -0
- package/package.json +1 -1
- package/src/commands/repl.js +4 -1
- package/src/tools/workflow.js +97 -9
- package/src/utils/system-prompt.js +6 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to NatureCo CLI will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [5.23.0] - 2026-07-02 — "NON-TOOL-CALLING FIX" (MiniMax dosya yazma)
|
|
6
|
+
|
|
7
|
+
### 🐛 Düzeltme
|
|
8
|
+
- **MiniMax (non-tool-calling) dosya oluşturamıyordu**: workflow non-tool-calling path'inde simple/complex ayrımı yoktu — LLM yanıtını doğrudan sohbete yazıp geçiyordu. Artık complex görevlerde LLM'den JSON plan istenir, dosyalar Node.js tarafında yazılır.
|
|
9
|
+
|
|
10
|
+
## [5.22.0] - 2026-07-02
|
|
11
|
+
|
|
5
12
|
## [5.21.0] - 2026-07-02 — "GÜVENİLİRLİK SPRİNTİ" (gerçek API E2E denetimi)
|
|
6
13
|
|
|
7
14
|
Gerçek MiniMax API anahtarıyla uçtan uca canlı test turu; bulunan her hata düzeltilip yine canlı doğrulandı. 461 test yeşil.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.23.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"
|
package/src/commands/repl.js
CHANGED
|
@@ -1090,6 +1090,8 @@ async function startRepl(args) {
|
|
|
1090
1090
|
memorySnapshotBlock, skillsIndexBlock, projectRules,
|
|
1091
1091
|
crossSessionContext: crossSessionContext || '',
|
|
1092
1092
|
userHome: cfg.userHome || '',
|
|
1093
|
+
platform: process.platform,
|
|
1094
|
+
desktopPath: cfg.userHome ? path.join(cfg.userHome, 'Desktop') : '',
|
|
1093
1095
|
hasHistory: messages.length > 0,
|
|
1094
1096
|
memoryFacts: memory.facts || [],
|
|
1095
1097
|
};
|
|
@@ -1468,7 +1470,8 @@ async function startRepl(args) {
|
|
|
1468
1470
|
// v5.13.0: Run workflow FIRST for every request
|
|
1469
1471
|
process.stdout.write(tui.styled('\r 🔧 workflow... ', { color: tui.PALETTE.muted }));
|
|
1470
1472
|
const wfToolDefs = getToolDefs();
|
|
1471
|
-
const
|
|
1473
|
+
const recentHistory = messages.length > 1 ? messages.slice(-10) : [];
|
|
1474
|
+
const wfResult = await executeTool('workflow', { action: 'run', task: line, conversationHistory: recentHistory }, wfToolDefs);
|
|
1472
1475
|
const wf = wfResult?.result || {};
|
|
1473
1476
|
if (wf.success !== false) {
|
|
1474
1477
|
const loaded = wf.skillsLoaded && wf.skillsLoaded.length > 0 ? ` [skill: ${wf.skillsLoaded.join(', ')}]` : '';
|
package/src/tools/workflow.js
CHANGED
|
@@ -83,7 +83,7 @@ function apiCall(providerUrl, apiKey, body) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
async function workflow(params) {
|
|
86
|
-
const { action, task, steps, name, workflowId, regenerateStep } = params;
|
|
86
|
+
const { action, task, steps, name, workflowId, regenerateStep, conversationHistory } = params;
|
|
87
87
|
const cfg = loadConfig();
|
|
88
88
|
const tools = allToolNames();
|
|
89
89
|
ensureDir(WORKFLOW_DIR);
|
|
@@ -98,6 +98,19 @@ async function workflow(params) {
|
|
|
98
98
|
|
|
99
99
|
const skillsIndexBlock = buildSkillIndex();
|
|
100
100
|
|
|
101
|
+
// Build chat messages with optional conversation history for context
|
|
102
|
+
function chatMessages(sysMsg, userTask) {
|
|
103
|
+
const msgs = [{ role: 'system', content: sysMsg }];
|
|
104
|
+
if (conversationHistory && Array.isArray(conversationHistory)) {
|
|
105
|
+
for (const m of conversationHistory) {
|
|
106
|
+
if (m._internal) continue;
|
|
107
|
+
msgs.push({ role: m.role, content: m.content || '' });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
msgs.push({ role: 'user', content: userTask });
|
|
111
|
+
return msgs;
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
// Non-tool-calling model tespiti
|
|
102
115
|
function supportsToolCalls() {
|
|
103
116
|
const url = (providerUrl || '').toLowerCase();
|
|
@@ -113,14 +126,88 @@ async function workflow(params) {
|
|
|
113
126
|
if (action === 'run') {
|
|
114
127
|
if (!task) return { success: false, error: 'task gerekli' };
|
|
115
128
|
|
|
116
|
-
// Non-tool-calling modellerde
|
|
129
|
+
// Non-tool-calling modellerde once simple/complex kontrolu yap
|
|
117
130
|
if (!supportsToolCalls()) {
|
|
118
131
|
const memCtx = memoryContext();
|
|
119
|
-
|
|
120
|
-
const
|
|
132
|
+
// Phase 0: Check if simple chat or complex task
|
|
133
|
+
const simpleCheckPrompt = {
|
|
134
|
+
role: 'system',
|
|
135
|
+
content: 'Gorevin basit bir selamlasma/sohbet mi yoksa arac gerektiren bir islem mi oldugunu belirle. Sadece "simple" veya "complex" yaz, kesinlikle baska bir sey yazma.\n\nSimple: selamlasma, nasilsin, bugun ne yaptin, havadan sudan, genel bilgi sorusu, ben kimim, adim ne, kullanici bilgisi sorgulama, hatirlatma talebi\nComplex: dosya islemleri, kod yazma, arastirma, karsilastirma, duzenleme, otomasyon'
|
|
136
|
+
};
|
|
137
|
+
let isComplex = true;
|
|
121
138
|
try {
|
|
122
|
-
const
|
|
123
|
-
const
|
|
139
|
+
const checkBody = { model, stream: false, messages: [simpleCheckPrompt, { role: 'user', content: task }], temperature: 0, max_tokens: 20 };
|
|
140
|
+
const checkResult = await apiCall(providerUrl, providerApiKey, checkBody);
|
|
141
|
+
const raw = (checkResult.choices?.[0]?.message?.content || '').trim().toLowerCase().replace(/[^a-z]/g, '');
|
|
142
|
+
isComplex = raw !== 'simple';
|
|
143
|
+
} catch {}
|
|
144
|
+
|
|
145
|
+
if (!isComplex) {
|
|
146
|
+
// Simple chat — passthrough
|
|
147
|
+
const sysMsg = 'Sen yardimci bir asistansin. Kisa ve oz yanit ver. Konusma gecmisi varsa onceki mesajlari dikkate al.' + (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') + '\n\n' + skillsIndexBlock;
|
|
148
|
+
const chatBody = { model, stream: false, messages: chatMessages(sysMsg, task), temperature: 0.7, max_tokens: 1000 };
|
|
149
|
+
try {
|
|
150
|
+
const chatResult = await apiCall(providerUrl, providerApiKey, chatBody);
|
|
151
|
+
const reply = chatResult.choices?.[0]?.message?.content || '';
|
|
152
|
+
return { success: true, workflowId: 'passthrough', name: 'Direct Chat', status: 'completed', totalSteps: 0, completedSteps: 0, results: [], passthrough: true, reply };
|
|
153
|
+
} catch (e) {
|
|
154
|
+
return { success: false, error: 'Sohbet yaniti alinamadi: ' + e.message };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Complex task for non-tool-calling model: ask LLM for structured file ops
|
|
159
|
+
const execSysMsg = 'Gorevi tamamlamak icin hangi dosyalarin olusturulacagini JSON olarak belirt. SADECE JSON yaz.\n\n' +
|
|
160
|
+
'Kullanici bilgisi:\n' + (memCtx || '') +
|
|
161
|
+
'\n\nMasaustu yolu: ' + require('path').join(require('os').homedir(), 'Desktop') +
|
|
162
|
+
'\n\nKullanici home: ' + require('os').homedir() +
|
|
163
|
+
'\n\nJSON format:\n{"files": [{"path": "/tam/dosya/yolu/dosya.html", "content": "dosya icerigi buraya"}]}\n\nHer dosya icin path ve content zorunlu. path TAM yol olmali (~ veya goreceli degil). Birden fazla dosya olusturulabilir.' +
|
|
164
|
+
'\n\n' + skillsIndexBlock;
|
|
165
|
+
const execBody = { model, stream: false, messages: chatMessages(execSysMsg, task), temperature: 0.3, max_tokens: 8000 };
|
|
166
|
+
try {
|
|
167
|
+
const execResult = await apiCall(providerUrl, providerApiKey, execBody);
|
|
168
|
+
const reply = execResult.choices?.[0]?.message?.content || '';
|
|
169
|
+
|
|
170
|
+
// Try to extract JSON with file operations
|
|
171
|
+
const jsonStart = reply.indexOf('{');
|
|
172
|
+
const jsonEnd = reply.lastIndexOf('}');
|
|
173
|
+
let stepResults = [];
|
|
174
|
+
let fileWriteSuccess = false;
|
|
175
|
+
|
|
176
|
+
if (jsonStart !== -1 && jsonEnd > jsonStart) {
|
|
177
|
+
try {
|
|
178
|
+
const plan = JSON.parse(reply.slice(jsonStart, jsonEnd + 1));
|
|
179
|
+
if (plan.files && Array.isArray(plan.files)) {
|
|
180
|
+
// Execute file writes locally
|
|
181
|
+
for (const f of plan.files) {
|
|
182
|
+
try {
|
|
183
|
+
const wfPath = f.path ? require('path').resolve(f.path.replace(/^~/, require('os').homedir())) : '';
|
|
184
|
+
if (!wfPath) continue;
|
|
185
|
+
require('fs').mkdirSync(require('path').dirname(wfPath), { recursive: true });
|
|
186
|
+
require('fs').writeFileSync(wfPath, f.content || '', 'utf-8');
|
|
187
|
+
const stats = require('fs').statSync(wfPath);
|
|
188
|
+
stepResults.push({ step: 1, tool: 'write_file', status: 'done', args: { path: wfPath }, result: { success: true, path: wfPath, size: stats.size } });
|
|
189
|
+
fileWriteSuccess = true;
|
|
190
|
+
} catch (wfErr) {
|
|
191
|
+
stepResults.push({ step: 1, tool: 'write_file', status: 'error', error: wfErr.message });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch {}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
if (fileWriteSuccess) {
|
|
199
|
+
const lines = stepResults.map(r => { const p = r.result?.path || ''; const s = r.result?.size || 0; const name = require('path').basename(p); return ` ✓ ${name} oluşturuldu (${s} bytes)`; }).join('\n');
|
|
200
|
+
const reply = `Dosya(lar) başarıyla oluşturuldu:\n${lines}`;
|
|
201
|
+
return {
|
|
202
|
+
success: true, workflowId: 'file_write_' + Date.now().toString(36),
|
|
203
|
+
name: 'File Operations', status: 'completed',
|
|
204
|
+
totalSteps: stepResults.length, completedSteps: stepResults.length,
|
|
205
|
+
results: stepResults,
|
|
206
|
+
passthrough: true, reply,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// If no structured file ops detected, return LLM reply as passthrough
|
|
124
211
|
return { success: true, workflowId: 'passthrough', name: 'Direct Generation', status: 'completed', totalSteps: 0, completedSteps: 0, results: [{ step: 0, tool: 'chat', status: 'done', result: { reply } }], passthrough: true, reply };
|
|
125
212
|
} catch (e) {
|
|
126
213
|
return { success: false, error: 'Yanit alinamadi: ' + e.message };
|
|
@@ -141,10 +228,10 @@ async function workflow(params) {
|
|
|
141
228
|
} catch {}
|
|
142
229
|
|
|
143
230
|
if (isSimple) {
|
|
144
|
-
// Passthrough: just chat with LLM, no tools — include user memory
|
|
231
|
+
// Passthrough: just chat with LLM, no tools — include user memory + conversation history
|
|
145
232
|
const memCtx = memoryContext();
|
|
146
|
-
const sysMsg = 'Sen yardimci bir asistansin. Kisa ve oz yanit ver.' + (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') + '\n\n' + skillsIndexBlock;
|
|
147
|
-
const chatBody = { model, stream: false, messages:
|
|
233
|
+
const sysMsg = 'Sen yardimci bir asistansin. Kisa ve oz yanit ver. Konusma gecmisi varsa onceki mesajlari dikkate al.' + (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') + '\n\n' + skillsIndexBlock;
|
|
234
|
+
const chatBody = { model, stream: false, messages: chatMessages(sysMsg, task), temperature: 0.7, max_tokens: 1000 };
|
|
148
235
|
try {
|
|
149
236
|
const chatResult = await apiCall(providerUrl, providerApiKey, chatBody);
|
|
150
237
|
const reply = chatResult.choices?.[0]?.message?.content || '';
|
|
@@ -417,6 +504,7 @@ module.exports = {
|
|
|
417
504
|
regenerateStep: { type: 'number', description: '(retry) Yeniden calistirilacak adim numarasi' },
|
|
418
505
|
newParams: { type: 'object', description: '(retry) Yeni parametreler' },
|
|
419
506
|
description: { type: 'string', description: 'Aciklama' },
|
|
507
|
+
conversationHistory: { type: 'array', description: '(internal) REPL konusma gecmisi', items: { type: 'object' } },
|
|
420
508
|
},
|
|
421
509
|
required: ['action'],
|
|
422
510
|
},
|
|
@@ -23,6 +23,8 @@ function buildTiers(opts) {
|
|
|
23
23
|
skillsIndexBlock = '',
|
|
24
24
|
crossSessionContext = '',
|
|
25
25
|
userHome = '',
|
|
26
|
+
platform = '',
|
|
27
|
+
desktopPath = '',
|
|
26
28
|
hasHistory = false,
|
|
27
29
|
memoryFacts = [],
|
|
28
30
|
projectRules = '',
|
|
@@ -58,7 +60,8 @@ function buildTiers(opts) {
|
|
|
58
60
|
`ONEMLI: Tool cagirma SIMULE ETME. Sadece duz metin cevap ver. Islem yapmak gerekirse tool'u gercekten cagir.`,
|
|
59
61
|
`TOOL KURALI: Selamlasma, sohbet, bilgi sorusu, fikir/aciklama isteklerinde TOOL CAGIRMA — dogrudan yanit ver. Bu en hizli ve en ucuz yoldur.`,
|
|
60
62
|
`EYLEM gerektiren isteklerde (dosya okuma/yazma, komut calistirma, arama, hatirlatici, cok adimli gorev) workflow(action="run", task="<istek>") cagir — uygun tool'lari secip sirayla calistirir.`,
|
|
61
|
-
`Tek ve net bir tool yeterliyse (or. read_file, web_search) workflow yerine dogrudan o tool'u cagirabilirsin.`,
|
|
63
|
+
`Tek ve net bir tool yeterliyse (or. read_file, web_search, write_file) workflow yerine dogrudan o tool'u cagirabilirsin.`,
|
|
64
|
+
`DOSYA YAZMA: SADECE write_file tool'unu kullan. "bulk-file-operations", "create-file", "file-write" gibi tool'lar YOK. write_file(content, file_path) kullan. Dosya yolu olarak desktopPath veya userHome kullan.`,
|
|
62
65
|
`COK KRITIK: Goreve baslamadan ONCE <available_skills> listesini tara. Ilgili skill varsa skill_view(name) ile yukle, SONRA goreve basla.`,
|
|
63
66
|
`KRITIK: Skill yuklemeden islem yapma. Ilgili skill varsa once yukle.`,
|
|
64
67
|
`KRITIK: Kullanici kisisel bilgi verdiginde memory(action=add, target=user) ile kaydet.`,
|
|
@@ -88,6 +91,8 @@ function buildTiers(opts) {
|
|
|
88
91
|
|
|
89
92
|
// User environment (stable within session but changes on resume)
|
|
90
93
|
userHome ? `Kullanicinin home: ${userHome}` : '',
|
|
94
|
+
platform ? `Isletim sistemi: ${platform}` : '',
|
|
95
|
+
desktopPath ? `Masaustu yolu: ${desktopPath}` : '',
|
|
91
96
|
hasHistory ? 'Bu oturum daha onceki konusmalarin devami.' : '',
|
|
92
97
|
].filter(Boolean).join('\n');
|
|
93
98
|
|