natureco-cli 5.6.21 → 5.6.22
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 +1 -1
- package/src/commands/code_v5.js +8 -2
- package/src/tools/filesystem.js +1 -1
- package/src/tools/git.js +52 -1
- package/src/tools/grep_search.js +41 -4
- package/src/tools/media_understanding.js +11 -1
- package/src/tools/memory_search.js +18 -10
- package/src/tools/read_file.js +1 -1
- package/src/tools/write_file.js +6 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "natureco-cli",
|
|
3
|
-
"version": "5.6.
|
|
3
|
+
"version": "5.6.22",
|
|
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/code_v5.js
CHANGED
|
@@ -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"
|
|
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 }));
|
package/src/tools/filesystem.js
CHANGED
package/src/tools/git.js
CHANGED
|
@@ -18,7 +18,8 @@ module.exports = {
|
|
|
18
18
|
},
|
|
19
19
|
|
|
20
20
|
execute({ operation, args = '', message = '' }) {
|
|
21
|
-
|
|
21
|
+
// v5.6.22: Git repo otomatik bul - cwd'de yoksa ust dizinleri kontrol et
|
|
22
|
+
const cwd = this._findGitRepo();
|
|
22
23
|
try {
|
|
23
24
|
let cmd;
|
|
24
25
|
switch (operation) {
|
|
@@ -35,5 +36,55 @@ 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
|
};
|
package/src/tools/grep_search.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
package/src/tools/read_file.js
CHANGED
|
@@ -4,7 +4,7 @@ const os = require('os');
|
|
|
4
4
|
|
|
5
5
|
module.exports = {
|
|
6
6
|
name: 'read_file',
|
|
7
|
-
description: 'Read content
|
|
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: {
|
package/src/tools/write_file.js
CHANGED
|
@@ -21,7 +21,12 @@ module.exports = {
|
|
|
21
21
|
|
|
22
22
|
async execute(params) {
|
|
23
23
|
try {
|
|
24
|
-
|
|
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
|