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 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.21.0",
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"
@@ -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 wfResult = await executeTool('workflow', { action: 'run', task: line }, wfToolDefs);
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(', ')}]` : '';
@@ -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 dogrudan passthrough (plan yok, direkt uretim)
129
+ // Non-tool-calling modellerde once simple/complex kontrolu yap
117
130
  if (!supportsToolCalls()) {
118
131
  const memCtx = memoryContext();
119
- const sysMsg = 'Sen yardimci bir asistansin. Kullanici ne istediyse onu dogrudan yap. Dosya olusturulacaksa icerigi eksiksiz olarak yanitla.' + (memCtx ? '\n\nKullanici bilgisi:\n' + memCtx : '') + '\n\n' + skillsIndexBlock;
120
- const chatBody = { model, stream: false, messages: [{ role: 'system', content: sysMsg }, { role: 'user', content: task }], temperature: 0.7, max_tokens: 4000 };
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 chatResult = await apiCall(providerUrl, providerApiKey, chatBody);
123
- const reply = chatResult.choices?.[0]?.message?.content || '';
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: [{ role: 'system', content: sysMsg }, { role: 'user', content: task }], temperature: 0.7, max_tokens: 1000 };
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