natureco-cli 5.6.21 → 5.6.23

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.6.21",
3
+ "version": "5.6.23",
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"
@@ -335,12 +335,18 @@ async function codeV5(targetPath) {
335
335
  rl.question("", async (input) => {
336
336
  input = input.trim();
337
337
  if (!input) { process.stdout.write(" " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true })); return ask(); }
338
- if (input === "/summary" || input === "/done") {
338
+ if (input === "/summary") {
339
+ printSummary(filesChanged, commandsRun, messages.length - 1, startTime);
340
+ // v5.6.22: /summary sadece gosterir, cikmaz
341
+ process.stdout.write("\n " + tui.styled("You ", { color: tui.PALETTE.primary, bold: true }));
342
+ return ask();
343
+ }
344
+ if (input === "/done" || input === "exit" || input === "quit") {
345
+ // v5.6.22: /done cikinca summary goster
339
346
  printSummary(filesChanged, commandsRun, messages.length - 1, startTime);
340
347
  rl.close();
341
348
  return;
342
349
  }
343
- if (input === "exit" || input === "quit") { rl.close(); return; }
344
350
 
345
351
  messages.push({ role: "user", content: input });
346
352
  process.stdout.write("\n " + tui.styled("AI ", { color: tui.PALETTE.secondary, bold: true }));
@@ -3,5 +3,5 @@ const listDir = require('./list_dir');
3
3
  module.exports = {
4
4
  ...listDir,
5
5
  name: 'filesystem',
6
- description: 'List files and directories',
6
+ description: 'List files and directories (NOT for reading single file content)',
7
7
  };
package/src/tools/git.js CHANGED
@@ -18,7 +18,8 @@ module.exports = {
18
18
  },
19
19
 
20
20
  execute({ operation, args = '', message = '' }) {
21
- const cwd = process.cwd();
21
+ // v5.6.22: Git repo otomatik bul - this baglami kayboldugu icin module-level helper kullan
22
+ const cwd = findGitRepo();
22
23
  try {
23
24
  let cmd;
24
25
  switch (operation) {
@@ -35,5 +36,101 @@ module.exports = {
35
36
  } catch (err) {
36
37
  return { success: false, error: err.stderr?.toString() || err.message };
37
38
  }
39
+ },
40
+
41
+ /**
42
+ * v5.6.22: Git repo bul - cwd'de yoksa ~/Projects ve parent dizinleri tara
43
+ */
44
+ _findGitRepo() {
45
+ const fs = require('fs');
46
+ const path = require('path');
47
+ const os = require('os');
48
+ const { execSync } = require('child_process');
49
+
50
+ // 1. Mevcut cwd'de .git var mi?
51
+ if (fs.existsSync(path.join(process.cwd(), '.git'))) {
52
+ return process.cwd();
53
+ }
54
+
55
+ // 2. Bilinen yaygin konumlari kontrol et
56
+ const home = os.homedir();
57
+ const candidates = [
58
+ path.join(home, 'Projects', 'natureco-cli'),
59
+ path.join(home, 'Projects'),
60
+ path.join(home, 'projects'),
61
+ path.join(home, 'code'),
62
+ path.join(home, 'dev'),
63
+ path.join(home, 'src'),
64
+ ];
65
+
66
+ for (const dir of candidates) {
67
+ try {
68
+ if (fs.existsSync(path.join(dir, '.git'))) {
69
+ return dir;
70
+ }
71
+ } catch {}
72
+ }
73
+
74
+ // 3. cwd'den yukarı dogru 5 seviye tara
75
+ let current = process.cwd();
76
+ for (let i = 0; i < 5; i++) {
77
+ const parent = path.dirname(current);
78
+ if (parent === current) break;
79
+ try {
80
+ if (fs.existsSync(path.join(parent, '.git'))) {
81
+ return parent;
82
+ }
83
+ } catch {}
84
+ current = parent;
85
+ }
86
+
87
+ // 4. Bulamadi, cwd'yi don
88
+ return process.cwd();
38
89
  }
39
90
  };
91
+
92
+
93
+ /**
94
+ * Git repo bul - cwd'de yoksa ~/Projects ve parent dizinleri tara
95
+ */
96
+ function findGitRepo() {
97
+ const fs = require('fs');
98
+ const path = require('path');
99
+ const os = require('os');
100
+
101
+ if (fs.existsSync(path.join(process.cwd(), '.git'))) {
102
+ return process.cwd();
103
+ }
104
+
105
+ const home = os.homedir();
106
+ const candidates = [
107
+ path.join(home, 'Projects', 'natureco-cli'),
108
+ path.join(home, 'Projects'),
109
+ path.join(home, 'projects'),
110
+ path.join(home, 'code'),
111
+ path.join(home, 'dev'),
112
+ path.join(home, 'src'),
113
+ ];
114
+
115
+ for (const dir of candidates) {
116
+ try {
117
+ if (fs.existsSync(path.join(dir, '.git'))) {
118
+ return dir;
119
+ }
120
+ } catch {}
121
+ }
122
+
123
+ let current = process.cwd();
124
+ for (let i = 0; i < 5; i++) {
125
+ const parent = path.dirname(current);
126
+ if (parent === current) break;
127
+ try {
128
+ if (fs.existsSync(path.join(parent, '.git'))) {
129
+ return parent;
130
+ }
131
+ } catch {}
132
+ current = parent;
133
+ }
134
+
135
+ return process.cwd();
136
+ }
@@ -19,7 +19,23 @@ const { spawn } = require('child_process');
19
19
  async function grepSearch({ pattern, path: searchPath = null, caseSensitive = false, includePattern = null, maxResults = 50 }) {
20
20
  if (!pattern) return { success: false, error: 'pattern gerekli' };
21
21
 
22
- const cwd = searchPath || process.cwd();
22
+ // v5.6.22: ~ expansion fix
23
+ if (searchPath && searchPath.startsWith('~')) {
24
+ searchPath = path.join(require('os').homedir(), searchPath.slice(1));
25
+ }
26
+
27
+ // v5.6.22: Dosya yolu verildiyse, parent directory'yi kullan + filename pattern ekle
28
+ let cwd = searchPath || process.cwd();
29
+ if (searchPath && fs.existsSync(searchPath)) {
30
+ const stat = fs.statSync(searchPath);
31
+ if (stat.isFile()) {
32
+ // Dosya -> parent dir + includePattern
33
+ cwd = path.dirname(searchPath);
34
+ if (!includePattern) {
35
+ includePattern = path.basename(searchPath);
36
+ }
37
+ }
38
+ }
23
39
  // ripgrep varsa onu kullan, yoksa fallback grep
24
40
  const useRipgrep = await checkCommand('rg');
25
41
 
@@ -43,9 +59,12 @@ async function grepWithRipgrep(pattern, cwd, caseSensitive, includePattern, maxR
43
59
  '--json',
44
60
  '--line-number',
45
61
  caseSensitive ? '' : '--ignore-case',
46
- includePattern ? `-g "${includePattern}"` : '',
47
62
  '--no-heading',
48
63
  ].filter(Boolean);
64
+ // v5.6.22: includePattern'i -g ve glob olarak ayri ekle (tirnak hatasini onler)
65
+ if (includePattern) {
66
+ args.push('-g', includePattern);
67
+ }
49
68
  args.push(pattern);
50
69
  args.push(cwd);
51
70
 
@@ -55,7 +74,10 @@ async function grepWithRipgrep(pattern, cwd, caseSensitive, includePattern, maxR
55
74
  proc.stdout.on('data', d => stdout += d.toString());
56
75
  proc.stderr.on('data', d => stderr += d.toString());
57
76
 
58
- proc.on('close', (code) => {
77
+ let finished = false;
78
+ const finishOnce = () => {
79
+ if (finished) return;
80
+ finished = true;
59
81
  const results = [];
60
82
  const lines = stdout.split('\n').filter(Boolean);
61
83
  for (const line of lines) {
@@ -72,7 +94,22 @@ async function grepWithRipgrep(pattern, cwd, caseSensitive, includePattern, maxR
72
94
  } catch {}
73
95
  }
74
96
  resolve({ success: true, pattern, tool: 'ripgrep', count: results.length, results });
75
- });
97
+ };
98
+
99
+ // v5.6.22: Promise ile stream okuma (race condition fix)
100
+ let stderrClosed = false;
101
+ let stdoutClosed = false;
102
+ let exitCode = null;
103
+ const checkFinish = () => {
104
+ if ((stdoutClosed || stderrClosed) && exitCode !== null) {
105
+ setTimeout(finishOnce, 100);
106
+ }
107
+ };
108
+ proc.stdout.on('data', d => stdout += d.toString());
109
+ proc.stderr.on('data', d => stderr += d.toString());
110
+ proc.stdout.on('end', () => { stdoutClosed = true; checkFinish(); });
111
+ proc.stderr.on('end', () => { stderrClosed = true; checkFinish(); });
112
+ proc.on('exit', (code) => { exitCode = code; checkFinish(); });
76
113
  proc.on('error', (e) => resolve({ success: false, error: e.message }));
77
114
  });
78
115
  }
@@ -20,7 +20,17 @@ module.exports = {
20
20
  try {
21
21
  const config = getConfig();
22
22
  const prompt = params.prompt || 'Describe this image in detail';
23
- const provider = params.provider || config.visionProvider || 'openai';
23
+ let provider = params.provider || config.visionProvider || 'openai';
24
+
25
+ // v5.6.22: MiniMax, Ollama, local provider'lar vision desteklemiyor olabilir
26
+ const providerUrl = (config.providerUrl || '').toLowerCase();
27
+ if (!params.provider && (providerUrl.includes('minimax') || providerUrl.includes('ollama') || providerUrl.includes('localhost'))) {
28
+ return {
29
+ success: false,
30
+ error: `Görsel analiz bu provider (${providerUrl}) için desteklenmiyor. Lütfen OpenAI, Anthropic, Gemini veya Groq provider'ı kullanın.`,
31
+ hint: 'natureco config set providerUrl https://api.openai.com/v1'
32
+ };
33
+ }
24
34
 
25
35
  if (!params.imagePath && !params.imageUrl) {
26
36
  return { success: false, error: 'imagePath veya imageUrl gerekli' };
@@ -55,16 +55,24 @@ async function searchMemory({ query, scope = "all", username = null, maxResults
55
55
 
56
56
  // Memory dosyalarini tara
57
57
  if (scope === "all" || scope === "memory") {
58
- const memoryFile = path.join(MEMORY_DIR, (username || "default") + ".json");
59
- if (fs.existsSync(memoryFile)) {
60
- try {
61
- const mem = JSON.parse(fs.readFileSync(memoryFile, "utf8"));
62
- const matches = searchInObject(mem, q, "memory");
63
- matches.forEach(m => {
64
- results.push({ source: "memory", ...m });
65
- sources.push("memory");
66
- });
67
- } catch {}
58
+ // v5.6.22: Username yoksa TÜM memory dosyalarini tara
59
+ const memoryDir = MEMORY_DIR;
60
+ if (fs.existsSync(memoryDir)) {
61
+ const memoryFiles = username
62
+ ? [(username || "default") + ".json"]
63
+ : listFiles(memoryDir);
64
+ for (const fname of memoryFiles) {
65
+ const memoryFile = path.join(memoryDir, fname);
66
+ if (!fs.existsSync(memoryFile)) continue;
67
+ try {
68
+ const mem = JSON.parse(fs.readFileSync(memoryFile, "utf8"));
69
+ const matches = searchInObject(mem, q, "memory");
70
+ matches.forEach(m => {
71
+ results.push({ source: "memory:" + fname, ...m });
72
+ sources.push("memory");
73
+ });
74
+ } catch {}
75
+ }
68
76
  }
69
77
  }
70
78
 
@@ -4,7 +4,7 @@ const os = require('os');
4
4
 
5
5
  module.exports = {
6
6
  name: 'read_file',
7
- description: 'Read content from a file',
7
+ description: 'PRIMARY TOOL: Read a specific file content. Use this when user wants to read/view/look at a single file. For listing directories use filesystem tool.',
8
8
  inputSchema: {
9
9
  type: 'object',
10
10
  properties: {
@@ -21,7 +21,12 @@ module.exports = {
21
21
 
22
22
  async execute(params) {
23
23
  try {
24
- const filePath = path.resolve(params.path);
24
+ // v5.6.22: ~ expansion fix
25
+ let rawPath = params.path;
26
+ if (rawPath && rawPath.startsWith('~')) {
27
+ rawPath = path.join(require('os').homedir(), rawPath.slice(1));
28
+ }
29
+ const filePath = path.resolve(rawPath);
25
30
  const dir = path.dirname(filePath);
26
31
 
27
32
  // Create directory if it doesn't exist