natureco-cli 5.31.0 → 5.32.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,16 @@
2
2
 
3
3
  All notable changes to NatureCo CLI will be documented in this file.
4
4
 
5
+ ## [5.32.0] - 2026-07-04 — "AĞAÇ-HAFIZA + OTURUMLAR ARASI KALICILIK (Theseus mimarisi)"
6
+
7
+ ### ✨ Yeni (Hafıza)
8
+ - **Oturumlar arası hafiza DUZELDI**: eski persist dar regex kaliplariyla (sadece "adim X") fact cikariyordu; keyfi bilgi ("parolam X, hatirla") kayboluyordu. Artik ajan `memory_write` ile ANINDA kaydeder → yeni oturumda hatirlanir (E2E: parola S1→S2 ✓).
9
+ - **Agac-hafiza (`memory_tree`)**: kullanicinin OpenCode/Theseus icin tasarladigi tree-memory mimarisinden uyarlandi. Kok (1-kisisel/2-teknik/3-kararlar) → dal (## baslik) → yaprak. action: index|read|search|append. Duz `facts[]` listesinin "coplুğe donme" sorununu cozer; kategorize + logaritmik erisim.
10
+ - **Proaktif yukleme (Theseus deseni)**: her istekte hafiza agaci sistem prompt'una otomatik enjekte edilir ("BILDIGIN KALICI HAFIZA") → ajan on-demand aramaya guvenmeden baglami ZATEN bilir. (E2E: "NatureCoPixel neyle yapiliyor?"→"Godot" ✓; hafiza ile dosya sistemini ayirt eder.)
11
+ - Tek-primary + "bkz:" capraz referans; credential/secret asla duz metin.
12
+
13
+ Doğrulama: memory_write + memory_tree E2E (S1 kaydet → S2 yeni oturum hatirla); 6 yeni tree testi; **498 test yeşil**, ESLint temiz.
14
+
5
15
  ## [5.31.0] - 2026-07-04 — "CHAT/CODE ARAYÜZ ZENGİNLEŞTİRME (araç görünürlüğü + düşünme + input alanı)"
6
16
 
7
17
  ### ✨ Yeni (UX)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.31.0",
3
+ "version": "5.32.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"
@@ -21,7 +21,7 @@ const os = require('os');
21
21
  // icinde approvals politikasini uyguluyor (isSafeCommand → direkt; tehlikeli → red;
22
22
  // digerleri → allowlist/full moda gore). Yani keyfi/yikici komut calismaz.
23
23
  // Diger ~85 arac (discord, telegram, cron, browser...) bilerek DISARIDA.
24
- const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir'];
24
+ const DEFAULT_ALLOWED = ['write_file', 'read_file', 'edit_file', 'skill_view', 'bash', 'file_search', 'list_dir', 'memory_write', 'memory_tree'];
25
25
 
26
26
  const TOOL_ALIASES = {
27
27
  write_file: 'write_file', create_file: 'write_file', writefile: 'write_file', write: 'write_file', create: 'write_file', save_file: 'write_file', new_file: 'write_file',
@@ -31,6 +31,8 @@ const TOOL_ALIASES = {
31
31
  bash: 'bash', run_command: 'bash', shell: 'bash', shell_command: 'bash', exec: 'bash', run_terminal: 'bash', terminal: 'bash', run: 'bash', command: 'bash',
32
32
  file_search: 'file_search', glob: 'file_search', find_files: 'file_search', search_files: 'file_search', find: 'file_search',
33
33
  list_dir: 'list_dir', ls: 'list_dir', dir: 'list_dir', list_directory: 'list_dir', filesystem: 'list_dir',
34
+ memory_write: 'memory_write', remember: 'memory_write', save_memory: 'memory_write', memorize: 'memory_write', memory_save: 'memory_write', save_fact: 'memory_write',
35
+ memory_tree: 'memory_tree', tree_memory: 'memory_tree', memory: 'memory_tree', hafiza: 'memory_tree',
34
36
  };
35
37
 
36
38
  function expandHome(p) {
@@ -0,0 +1,156 @@
1
+ /**
2
+ * memory_tree — Ağaç-hafıza (kök → dal → yaprak). Kullanıcının OpenCode için tasarladığı
3
+ * tree-memory mimarisinden uyarlandı (opencode-hafiza-mimarisi.md).
4
+ *
5
+ * Felsefe: "bilgi saklama" değil "hızlı bilgi bulma". Düz liste zamanla çöplüğe döner;
6
+ * ağaç logaritmik arama sağlar (kök seç → dal seç → yaprak oku, <300 token).
7
+ *
8
+ * Yapı: ~/.natureco/memory/tree/<user>/
9
+ * 0-index.md — yönlendirme (hangi konu hangi kökte)
10
+ * 1-kisisel.md — Kişisel & Tercihler (## dallar altında yapraklar)
11
+ * 2-teknik.md — Teknik & Projeler
12
+ * 3-kararlar.md— Kararlar, Kurallar & Dersler
13
+ *
14
+ * Kurallar (mimariden): tek primary + "bkz:" çapraz referans; yeni bilgi ilgili dalın
15
+ * altına (dosya sonuna değil); credential/secret ASLA düz metin ("bkz: secrets vault").
16
+ */
17
+ const fs = require('fs');
18
+ const path = require('path');
19
+ const os = require('os');
20
+
21
+ const ROOTS = [
22
+ { id: '1-kisisel', title: 'Kişisel & Tercihler', branches: ['Kimlik', 'Tercihler', 'İletişim Kalıpları'] },
23
+ { id: '2-teknik', title: 'Teknik & Projeler', branches: ['Projeler', 'Kurulum & Sistem', 'Teknik Referans'] },
24
+ { id: '3-kararlar', title: 'Kararlar, Kurallar & Dersler', branches: ['Kararlar', 'Kurallar & Kısıtlar', 'Öğrenilen Dersler', 'Tarihli Olaylar'] },
25
+ ];
26
+
27
+ function treeDir(username) {
28
+ return path.join(os.homedir(), '.natureco', 'memory', 'tree', String(username || 'default').toLowerCase());
29
+ }
30
+ function rootPath(username, id) { return path.join(treeDir(username), id + '.md'); }
31
+ function readSafe(username, id) { try { return fs.readFileSync(rootPath(username, id), 'utf8'); } catch { return ''; } }
32
+ function resolveRoot(root) {
33
+ if (!root) return ROOTS[0];
34
+ const r = String(root).toLowerCase();
35
+ return ROOTS.find((x) => x.id === r || x.id.endsWith(r.replace(/^\d+-/, '')) || x.id.includes(r) || x.title.toLowerCase().includes(r)) || ROOTS[0];
36
+ }
37
+
38
+ function ensureTree(username) {
39
+ const dir = treeDir(username);
40
+ fs.mkdirSync(dir, { recursive: true });
41
+ for (const r of ROOTS) {
42
+ const p = rootPath(username, r.id);
43
+ if (!fs.existsSync(p)) fs.writeFileSync(p, `# ${r.title}\n\n` + r.branches.map((b) => `## ${b}\n`).join('\n'), 'utf8');
44
+ }
45
+ const idx = path.join(dir, '0-index.md');
46
+ if (!fs.existsSync(idx)) fs.writeFileSync(idx, '# Hafıza İndeksi (kök → dal)\n\n' + ROOTS.map((r) => `- **${r.id}** — ${r.title}: ${r.branches.join(', ')}`).join('\n') + '\n', 'utf8');
47
+ }
48
+
49
+ // Modele enjekte edilen kompakt indeks (dal başlıkları + yaprak sayısı).
50
+ function buildIndex(username) {
51
+ ensureTree(username);
52
+ const lines = [];
53
+ for (const r of ROOTS) {
54
+ const txt = readSafe(username, r.id);
55
+ const branches = (txt.match(/^##\s+(.+)$/gm) || []).map((s) => s.replace(/^##\s+/, '').trim());
56
+ const leaves = (txt.match(/^\s*-\s+\S/gm) || []).length;
57
+ lines.push(`- ${r.id} (${r.title}) — dallar: ${branches.join(' · ') || '(boş)'}${leaves ? ` [${leaves} kayıt]` : ''}`);
58
+ }
59
+ return lines.join('\n');
60
+ }
61
+
62
+ function readRoot(username, id) {
63
+ ensureTree(username);
64
+ try { return fs.readFileSync(rootPath(username, resolveRoot(id).id), 'utf8'); } catch { return ''; }
65
+ }
66
+
67
+ // Theseus deseni: oturum BAŞINDA hafızayı proaktif yükle. Tüm kökler-dallar-yapraklardan
68
+ // (boş dallar hariç) kompakt bir özet çıkar → sistem prompt'una gömülür ki ajan
69
+ // on-demand aramaya güvenmeden bilgiyi ZATEN bilsin. maxChars aşılırsa kırpılır.
70
+ function buildDigest(username, maxChars = 2600) {
71
+ ensureTree(username);
72
+ const parts = [];
73
+ for (const r of ROOTS) {
74
+ const txt = readSafe(username, r.id);
75
+ let branch = '';
76
+ const byBranch = {};
77
+ for (const line of txt.split('\n')) {
78
+ if (line.startsWith('## ')) branch = line.slice(3).trim();
79
+ else if (/^\s*-\s+\S/.test(line)) (byBranch[branch] = byBranch[branch] || []).push(line.trim().replace(/^-\s*/, ''));
80
+ }
81
+ const nonEmpty = Object.entries(byBranch).filter(([, l]) => l.length);
82
+ if (nonEmpty.length) {
83
+ parts.push(`[${r.title}]`);
84
+ for (const [b, l] of nonEmpty) parts.push(` ${b}: ${l.join('; ')}`);
85
+ }
86
+ }
87
+ let out = parts.join('\n');
88
+ if (out.length > maxChars) out = out.slice(0, maxChars) + '\n…(fazlası için memory_tree ile oku)';
89
+ return out;
90
+ }
91
+
92
+ function search(username, query) {
93
+ ensureTree(username);
94
+ const q = String(query || '').toLowerCase().trim();
95
+ if (!q) return [];
96
+ const hits = [];
97
+ for (const r of ROOTS) {
98
+ const txt = readSafe(username, r.id);
99
+ let branch = '';
100
+ for (const line of txt.split('\n')) {
101
+ if (line.startsWith('## ')) branch = line.slice(3).trim();
102
+ else if (line.trim() && !line.startsWith('#') && line.toLowerCase().includes(q)) hits.push(`${r.id}/${branch}: ${line.trim()}`);
103
+ }
104
+ }
105
+ return hits;
106
+ }
107
+
108
+ function append(username, root, branch, content) {
109
+ ensureTree(username);
110
+ const r = resolveRoot(root);
111
+ const p = rootPath(username, r.id);
112
+ const br = String(branch || 'Genel').trim();
113
+ const leaf = '- ' + String(content).replace(/\s+/g, ' ').trim();
114
+ let txt = fs.readFileSync(p, 'utf8');
115
+ const bRe = new RegExp(`^##[ \\t]+${br.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}[ \\t]*$`, 'mi');
116
+ const m = txt.match(bRe);
117
+ if (m) {
118
+ const idx = txt.indexOf(m[0]) + m[0].length;
119
+ txt = txt.slice(0, idx) + '\n' + leaf + txt.slice(idx);
120
+ } else {
121
+ txt = txt.trimEnd() + `\n\n## ${br}\n${leaf}\n`;
122
+ }
123
+ fs.writeFileSync(p, txt, 'utf8');
124
+ return { success: true, root: r.id, branch: br, saved: leaf };
125
+ }
126
+
127
+ module.exports = {
128
+ name: 'memory_tree',
129
+ description: 'Ağaç-hafıza (kök→dal→yaprak): kalıcı bilgiyi kategorize SAKLA/OKU/ARA. Yeni oturumda hatırlanır. action: index|read|search|append',
130
+ inputSchema: {
131
+ type: 'object',
132
+ properties: {
133
+ action: { type: 'string', description: 'index | read | search | append' },
134
+ username: { type: 'string', description: 'kullanıcı adı' },
135
+ root: { type: 'string', description: '1-kisisel | 2-teknik | 3-kararlar' },
136
+ branch: { type: 'string', description: 'dal başlığı (append için)' },
137
+ content: { type: 'string', description: 'yaprak metni (append için)' },
138
+ query: { type: 'string', description: 'arama (search için)' },
139
+ },
140
+ required: ['action'],
141
+ },
142
+ async execute(p) {
143
+ const u = p.username || 'default';
144
+ try {
145
+ if (p.action === 'index') return { success: true, index: buildIndex(u) };
146
+ if (p.action === 'read') return { success: true, content: readRoot(u, p.root || '1-kisisel') };
147
+ if (p.action === 'search') return { success: true, results: search(u, p.query || p.content || '') };
148
+ if (p.action === 'append') {
149
+ if (!p.content) return { success: false, error: 'content (yaprak metni) gerekli' };
150
+ return append(u, p.root || '1-kisisel', p.branch || 'Genel', p.content);
151
+ }
152
+ return { success: false, error: 'bilinmeyen action: ' + p.action + ' (index|read|search|append)' };
153
+ } catch (e) { return { success: false, error: e.message }; }
154
+ },
155
+ _internal: { ensureTree, buildIndex, buildDigest, readRoot, search, append, treeDir, rootPath, ROOTS },
156
+ };
@@ -222,6 +222,11 @@ async function workflow(params) {
222
222
  const { runAgentic } = require('./agentic-runner');
223
223
  const memCtx = memoryContext();
224
224
  const desktop = path.join(os.homedir(), 'Desktop');
225
+ const memUser = cfg.userName || 'default';
226
+ // Theseus deseni: oturum basinda hafiza agacini PROAKTIF yukle (on-demand aramaya
227
+ // guvenme). Digest = kayitli bilgiler (icerik); Index = yapi (kok→dal).
228
+ let treeIndex = '', treeDigest = '';
229
+ try { const mt = require('./memory_tree')._internal; treeDigest = mt.buildDigest(memUser); treeIndex = mt.buildIndex(memUser); } catch {}
225
230
  // Tam mod (sahibin opt-in'i): tum arac+skill'ler acilir, keyfi shell (yikici haric).
226
231
  const execFull = cfg.agentExec === 'full' || cfg.computerUse === true || String(process.env.NATURECO_AGENT_EXEC || '').toLowerCase() === 'full';
227
232
  let fullToolsBlock = '';
@@ -252,7 +257,11 @@ async function workflow(params) {
252
257
  '- list_dir: dizin icerigini listele. parametre: path',
253
258
  '- bash: kabuk komutu calistir (npm, git, node, python, test, ls, grep/findstr, mkdir...). parametre: command. Guvenli komutlar dogrudan calisir; yikici/tehlikeli komutlar guvenlik politikasiyla engellenir. Icerik aramasi icin grep/findstr kullan.',
254
259
  '- skill_view: gorevle ilgili bir skill yukle. parametre: name',
260
+ '- memory_write: HIZLI tek bilgi kaydet (isim, kisa tercih). parametreler: username ("' + memUser + '"), fact, category. YENI oturumda hatirlanir.',
261
+ '- memory_tree: AGAC-HAFIZA — zengin/kategorize kalici bilgi (proje, karar, teknik not, kisisel detay). action: index|read|search|append; username="' + memUser + '"; append icin: root (1-kisisel|2-teknik|3-kararlar) + branch (dal basligi) + content (yaprak). YENI oturumda hatirlanir.',
255
262
  '\nKurallar:',
263
+ '- Kullanici kalici bilgi paylasirsa (isim, tercih, parola, proje, karar, onemli detay) ya da "hatirla / not al / kaydet" derse HEMEN KAYDET (username="' + memUser + '"): kisa tek bilgi → memory_write; zengin/kategorize bilgi → memory_tree(append, dogru root/branch). Trivial/gecici seyleri kaydetme.',
264
+ '- Kullaniciya ozel bir sey sorulunca (gecmis, proje, tercih, karar) once memory_tree(search/read) ile ilgili kok/dali OKU (hedefli, tum hafizayi degil). Tek primary: bilgi tek yerde yasar; digerine sadece "bkz:". Credential/secret ASLA duz metin.',
256
265
  '- MEVCUT bir dosyayi degistirmeden ONCE read_file ile oku; sonra edit_file ile hedefli degisiklik yap (tum dosyayi write_file ile ezme).',
257
266
  '- Bir seyi nerede oldugunu bilmiyorsan once file_search/list_dir/bash(grep) ile kesfet.',
258
267
  '- Kod yazdiktan/degistirdikten sonra gerektiginde bash ile calistir/test et (orn. "node dosya.js", "npm test"); hata cikarsa duzelt.',
@@ -263,6 +272,8 @@ async function workflow(params) {
263
272
  '- Basit sohbet/selamlasma ise arac cagirma, dogrudan kisa yanit ver.',
264
273
  execFull ? '- Kullanici uygulama ac / tarayici kontrol / muzik cal / ekran / GUI istediyse yukaridaki computer-use araclarini KULLAN — "yapamam/engellendi" DEME, dogrudan ilgili araci cagir.' : '',
265
274
  fullToolsBlock,
275
+ treeDigest ? ('\n\nBILDIGIN KALICI HAFIZA (bu kullaniciya ait, onceki oturumlardan hatirladiklarin; kullaniciya ozel bir sey sorulursa ONCE BUNU KULLAN — dosya arama, uydurma):\n' + treeDigest) : '',
276
+ treeIndex ? ('\n\nHafiza agaci yapisi (yukarida olmayan detay icin memory_tree(action:read/search) ile ilgili kok/dali oku):\n' + treeIndex) : '',
266
277
  skillsIndexBlock ? '\n\n' + skillsIndexBlock : '',
267
278
  ].filter(Boolean).join('\n');
268
279