natureco-cli 5.7.16 → 5.7.17

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/bin/natureco.js CHANGED
@@ -67,6 +67,7 @@ const terminal = require('../src/commands/terminal');
67
67
  const transcripts = require('../src/commands/transcripts');
68
68
  const wiki = require('../src/commands/wiki');
69
69
  const browser = require('../src/commands/browser');
70
+ const tools = require('../src/commands/tools');
70
71
 
71
72
  const program = new Command();
72
73
 
@@ -623,6 +624,13 @@ program
623
624
  xp(action ? [action] : []);
624
625
  });
625
626
 
627
+ program
628
+ .command('tools [action] [name]')
629
+ .description('Tool registry (list|enable|disable)')
630
+ .action((action, name) => {
631
+ tools(action ? [action, name].filter(Boolean) : []);
632
+ });
633
+
626
634
  program
627
635
  .command('team [action] [params...]')
628
636
  .description('Multi-agent orkestrasyon (list|status|spawn|parallel)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "natureco-cli",
3
- "version": "5.7.16",
3
+ "version": "5.7.17",
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"
@@ -0,0 +1,95 @@
1
+ /**
2
+ * natureco tools — Tool registry management (Hermes-style)
3
+ *
4
+ * Kullanım:
5
+ * natureco tools List toolset groups
6
+ * natureco tools list Detailed tool list
7
+ * natureco tools enable <name> Enable a tool
8
+ * natureco tools disable <name> Disable a tool
9
+ */
10
+
11
+ const chalk = require('chalk');
12
+ const tui = require('../utils/tui');
13
+ const { loadToolDefinitions, EMOJI_MAP, TOOLSET_MAP } = require('../utils/tools');
14
+ const { getConfig, setConfigValue } = require('../utils/config');
15
+
16
+ function main(args) {
17
+ const action = args[0] || 'list';
18
+
19
+ switch (action) {
20
+ case 'list':
21
+ return cmdList();
22
+ case 'enable':
23
+ return cmdEnable(args[1]);
24
+ case 'disable':
25
+ return cmdDisable(args[1]);
26
+ default:
27
+ console.log(chalk.yellow('Kullanım:'));
28
+ console.log(chalk.gray(' natureco tools Grup listesi'));
29
+ console.log(chalk.gray(' natureco tools list Detaylı liste'));
30
+ console.log(chalk.gray(' natureco tools enable <name> Tool etkinleştir'));
31
+ console.log(chalk.gray(' natureco tools disable <name> Tool devre dışı'));
32
+ console.log('');
33
+ }
34
+ }
35
+
36
+ function cmdList() {
37
+ const allTools = loadToolDefinitions();
38
+ const disabled = getDisabledTools();
39
+
40
+ const byToolset = {};
41
+ for (const t of allTools) {
42
+ const ts = t.toolset || 'general';
43
+ if (!byToolset[ts]) byToolset[ts] = [];
44
+ byToolset[ts].push(t);
45
+ }
46
+
47
+ let total = 0;
48
+ for (const [ts, tools] of Object.entries(byToolset).sort()) {
49
+ const active = tools.filter(t => !disabled.has(t.name));
50
+ total += active.length;
51
+ const line = tools.map(t => {
52
+ const d = disabled.has(t.name);
53
+ return (d ? chalk.gray.dim : chalk.white)((t.emoji || ' ') + ' ' + t.name);
54
+ }).join(' ');
55
+ console.log(chalk.cyan.bold('\n ' + ts + ' (' + active.length + '/' + tools.length + ')'));
56
+ console.log(' ' + line);
57
+ }
58
+
59
+ console.log(chalk.gray('\n Toplam: ' + total + ' aktif tool'));
60
+ if (disabled.size > 0) {
61
+ console.log(chalk.yellow(' Devre dışı: ' + [...disabled].join(', ')));
62
+ }
63
+ console.log('');
64
+ }
65
+
66
+ function getDisabledTools() {
67
+ const cfg = getConfig();
68
+ return new Set(cfg.disabledTools || []);
69
+ }
70
+
71
+ function cmdEnable(name) {
72
+ if (!name) return console.log(chalk.red('Tool adı gerekli: natureco tools enable <name>'));
73
+ const disabled = getDisabledTools();
74
+ if (!disabled.has(name)) return console.log(chalk.yellow(name + ' zaten etkin.'));
75
+ disabled.delete(name);
76
+ setConfigValue('disabledTools', [...disabled]);
77
+ console.log(chalk.green('✅ ' + name + ' etkinleştirildi.'));
78
+ }
79
+
80
+ function cmdDisable(name) {
81
+ if (!name) return console.log(chalk.red('Tool adı gerekli: natureco tools disable <name>'));
82
+ const allTools = loadToolDefinitions();
83
+ const tool = allTools.find(t => t.name === name);
84
+ if (!tool) return console.log(chalk.red('Tool bulunamadı: ' + name));
85
+ const disabled = getDisabledTools();
86
+ if (disabled.has(name)) return console.log(chalk.yellow(name + ' zaten devre dışı.'));
87
+ disabled.add(name);
88
+ setConfigValue('disabledTools', [...disabled]);
89
+ console.log(chalk.yellow('⛔ ' + name + ' devre dışı bırakıldı.'));
90
+ }
91
+
92
+ // Diğer modüllerden çağrılabilir
93
+ main.getDisabledTools = getDisabledTools;
94
+
95
+ module.exports = main;
@@ -1,23 +1,26 @@
1
1
  /**
2
- * memory — Unified memory tool (Hermes-style)
2
+ * memory — Unified memory tool (Hermes-style, merged with memory_write + memory_search)
3
3
  *
4
- * Single tool with action=add|remove|replace|list, target=memory|user
4
+ * Single tool with action=add|remove|replace|list|search, target=memory|user
5
5
  *
6
- * Coexists with existing memory_write / memory_search tools.
7
- * New system prompt uses memory-store snapshot; this tool mutates live state.
6
+ * Uses memory-store (MEMORY.md / USER.md) for add/remove/replace/list.
7
+ * Search action uses the legacy memory_write/search JSON files for cross-session query.
8
8
  */
9
9
 
10
10
  const { getMemoryStore } = require('../utils/memory-store');
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const os = require('os');
11
14
 
12
15
  const name = 'memory';
13
- const description = 'Persistent memory across sessions. Use action=add to save facts, action=list to see everything, action=remove to delete by substring match. target=memory for environment facts, target=user for user preferences and traits.';
16
+ const description = 'Persistent memory across sessions. action=add to save facts, action=list to see everything, action=remove to delete by substring, action=search to query all past sessions and memory files. target=memory for environment facts, target=user for user preferences.';
14
17
  const parameters = {
15
18
  type: 'object',
16
19
  properties: {
17
20
  action: {
18
21
  type: 'string',
19
- enum: ['add', 'remove', 'replace', 'list'],
20
- description: 'Operation: add (append entry), remove (by substring match), replace (find by substring, replace), list (show all)',
22
+ enum: ['add', 'remove', 'replace', 'list', 'search'],
23
+ description: 'Operation: add (append entry), remove (by substring match), replace (find by substring, replace), list (show all), search (query all memory + sessions)',
21
24
  },
22
25
  target: {
23
26
  type: 'string',
@@ -26,19 +29,99 @@ const parameters = {
26
29
  },
27
30
  content: {
28
31
  type: 'string',
29
- description: 'Content to add/remove/replace. For replace, this is the new content.',
32
+ description: 'Content to add/remove/replace. For replace, this is the new content. For search, the query string.',
30
33
  },
31
34
  oldContent: {
32
35
  type: 'string',
33
36
  description: 'For replace: substring to match existing entry.',
34
37
  },
38
+ scope: {
39
+ type: 'string',
40
+ enum: ['all', 'memory', 'sessions'],
41
+ description: 'For search: scope to search (all=memory files + sessions, memory=only memory files, sessions=only session history)',
42
+ },
43
+ maxResults: {
44
+ type: 'number',
45
+ description: 'For search: max results (default 10)',
46
+ },
35
47
  },
36
- required: ['action', 'target'],
48
+ required: ['action'],
37
49
  };
38
50
 
51
+ // ── Legacy search helpers (from memory_search.js) ────────────────────────
52
+
53
+ const MEMORY_DIR = path.join(os.homedir(), '.natureco', 'memory');
54
+ const SESSION_DIR = path.join(os.homedir(), '.natureco', 'sessions');
55
+
56
+ function _searchInObject(obj, query, pathStr) {
57
+ const results = [];
58
+ if (!obj || typeof obj !== 'object') return results;
59
+ if (typeof obj === 'string' && obj.toLowerCase().includes(query)) {
60
+ return [{ path: pathStr, content: obj.slice(0, 200) }];
61
+ }
62
+ for (const [key, val] of Object.entries(obj)) {
63
+ const newPath = pathStr ? pathStr + '.' + key : key;
64
+ if (typeof val === 'string' && val.toLowerCase().includes(query)) {
65
+ results.push({ path: newPath, content: val.slice(0, 200) });
66
+ } else if (Array.isArray(val)) {
67
+ val.forEach((item, i) => {
68
+ const ip = newPath + '[' + i + ']';
69
+ if (typeof item === 'string' && item.toLowerCase().includes(query)) {
70
+ results.push({ path: ip, content: item.slice(0, 200) });
71
+ } else if (typeof item === 'object') {
72
+ results.push(..._searchInObject(item, query, ip));
73
+ }
74
+ });
75
+ } else if (typeof val === 'object' && val !== null) {
76
+ results.push(..._searchInObject(val, query, newPath));
77
+ }
78
+ }
79
+ return results;
80
+ }
81
+
82
+ function _searchFiles(dir, query, sourceLabel) {
83
+ const results = [];
84
+ if (!fs.existsSync(dir)) return results;
85
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
86
+ for (const file of files) {
87
+ try {
88
+ const data = JSON.parse(fs.readFileSync(path.join(dir, file), 'utf8'));
89
+ const hits = _searchInObject(data, query.toLowerCase(), '');
90
+ for (const h of hits) {
91
+ results.push({ source: sourceLabel, file, path: h.path, content: h.content });
92
+ }
93
+ } catch { /* skip corrupt files */ }
94
+ }
95
+ return results;
96
+ }
97
+
98
+ function _searchSessions(query, maxResults) {
99
+ const results = [];
100
+ if (!fs.existsSync(SESSION_DIR)) return results;
101
+ const files = fs.readdirSync(SESSION_DIR).filter(f => f.endsWith('.json'));
102
+ for (const file of files) {
103
+ if (results.length >= maxResults) break;
104
+ try {
105
+ const session = JSON.parse(fs.readFileSync(path.join(SESSION_DIR, file), 'utf8'));
106
+ const msgs = session.messages || [];
107
+ for (const msg of msgs) {
108
+ const text = msg.content || '';
109
+ if (typeof text === 'string' && text.toLowerCase().includes(query)) {
110
+ results.push({
111
+ source: 'session', file, role: msg.role || '?',
112
+ preview: text.slice(0, 200),
113
+ });
114
+ if (results.length >= maxResults) break;
115
+ }
116
+ }
117
+ } catch { /* skip corrupt */ }
118
+ }
119
+ return results;
120
+ }
121
+
39
122
  async function execute(args) {
40
123
  const store = getMemoryStore();
41
- const { action, target = 'memory', content, oldContent } = args;
124
+ const { action, target = 'memory', content, oldContent, scope, maxResults = 10 } = args;
42
125
 
43
126
  switch (action) {
44
127
  case 'add':
@@ -52,6 +135,20 @@ async function execute(args) {
52
135
  return store.replace(target, oldContent, content);
53
136
  case 'list':
54
137
  return store.list(target);
138
+ case 'search': {
139
+ if (!content) return JSON.stringify({ success: false, error: 'content (query) required for search' });
140
+ const s = scope || 'all';
141
+ const results = [];
142
+ if (s === 'all' || s === 'memory') {
143
+ const memHits = _searchFiles(MEMORY_DIR, content, 'memory');
144
+ results.push(...memHits);
145
+ }
146
+ if (s === 'all' || s === 'sessions') {
147
+ const sessHits = _searchSessions(content, maxResults);
148
+ results.push(...sessHits);
149
+ }
150
+ return JSON.stringify({ success: true, query: content, count: results.length, results: results.slice(0, maxResults) });
151
+ }
55
152
  default:
56
153
  return JSON.stringify({ success: false, error: `Unknown action: ${action}` });
57
154
  }
@@ -0,0 +1,104 @@
1
+ /**
2
+ * skill_manage — Create, patch, or delete skills (Hermes-style)
3
+ *
4
+ * Model can create new skills, patch existing ones, or delete them.
5
+ * Skills are SKILL.md files in ~/.natureco/skills/<name>/SKILL.md
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const os = require('os');
11
+
12
+ const USER_SKILLS_DIR = path.join(os.homedir(), '.natureco', 'skills');
13
+
14
+ const name = 'skill_manage';
15
+ const description = 'Create, update (patch), or delete skills. Use skill_view(name) to read skill content first, then skill_manage to create/patch it. Skills contain reusable workflows, instructions, and conventions.';
16
+ const parameters = {
17
+ type: 'object',
18
+ properties: {
19
+ action: {
20
+ type: 'string',
21
+ enum: ['create', 'patch', 'delete'],
22
+ description: 'create: make a new skill. patch: update an existing one. delete: remove a skill.',
23
+ },
24
+ name: {
25
+ type: 'string',
26
+ description: 'Skill name (lowercase, hyphen-separated, e.g. "my-workflow"). For patch/delete, must match an existing skill.',
27
+ },
28
+ description: {
29
+ type: 'string',
30
+ description: 'Short one-line description shown in the skills index.',
31
+ },
32
+ content: {
33
+ type: 'string',
34
+ description: 'Full SKILL.md body (including YAML frontmatter). For patch: the new content to write. For create: must include --- frontmatter with name + description.',
35
+ },
36
+ },
37
+ required: ['action', 'name'],
38
+ };
39
+
40
+ function _ensureUserSkillsDir() {
41
+ if (!fs.existsSync(USER_SKILLS_DIR)) {
42
+ fs.mkdirSync(USER_SKILLS_DIR, { recursive: true });
43
+ }
44
+ }
45
+
46
+ function _skillPath(name) {
47
+ return path.join(USER_SKILLS_DIR, name, 'SKILL.md');
48
+ }
49
+
50
+ async function execute(args) {
51
+ const { action, name: skillName, description, content } = args;
52
+
53
+ switch (action) {
54
+ case 'create': {
55
+ if (!content) {
56
+ return JSON.stringify({ success: false, error: 'content (full SKILL.md with frontmatter) required for create' });
57
+ }
58
+ if (!content.startsWith('---')) {
59
+ return JSON.stringify({ success: false, error: 'content must start with YAML frontmatter (---)' });
60
+ }
61
+ _ensureUserSkillsDir();
62
+ const skillDir = path.join(USER_SKILLS_DIR, skillName);
63
+ if (fs.existsSync(skillDir)) {
64
+ return JSON.stringify({ success: false, error: `Skill "${skillName}" already exists. Use action=patch to update.` });
65
+ }
66
+ fs.mkdirSync(skillDir, { recursive: true });
67
+ fs.writeFileSync(_skillPath(skillName), content, 'utf8');
68
+ return JSON.stringify({ success: true, message: `Skill "${skillName}" created.`, path: _skillPath(skillName) });
69
+ }
70
+ case 'patch': {
71
+ const sp = _skillPath(skillName);
72
+ if (!fs.existsSync(sp)) {
73
+ return JSON.stringify({ success: false, error: `Skill "${skillName}" not found. Use action=create first.` });
74
+ }
75
+ let newContent = content;
76
+ if (!newContent) {
77
+ return JSON.stringify({ success: false, error: 'content required for patch' });
78
+ }
79
+ if (!newContent.startsWith('---') && description) {
80
+ const existing = fs.readFileSync(sp, 'utf8');
81
+ const frontmatterEnd = existing.indexOf('\n---', 3);
82
+ if (frontmatterEnd !== -1) {
83
+ const fm = existing.slice(0, frontmatterEnd + 4);
84
+ const body = existing.slice(frontmatterEnd + 4);
85
+ newContent = fm + '\n\n' + content + body;
86
+ }
87
+ }
88
+ fs.writeFileSync(sp, newContent, 'utf8');
89
+ return JSON.stringify({ success: true, message: `Skill "${skillName}" updated.` });
90
+ }
91
+ case 'delete': {
92
+ const sp = _skillPath(skillName);
93
+ if (!fs.existsSync(sp)) {
94
+ return JSON.stringify({ success: false, error: `Skill "${skillName}" not found.` });
95
+ }
96
+ fs.rmSync(path.dirname(sp), { recursive: true, force: true });
97
+ return JSON.stringify({ success: true, message: `Skill "${skillName}" deleted.` });
98
+ }
99
+ default:
100
+ return JSON.stringify({ success: false, error: `Unknown action: ${action}` });
101
+ }
102
+ }
103
+
104
+ module.exports = { name, description, parameters, execute };
@@ -2,42 +2,145 @@
2
2
  * NatureCo CLI — Tool Definitions for OpenAI-compatible APIs
3
3
  *
4
4
  * src/tools/*.js dosyalarını OpenAI uyumlu function calling format'ına dönüştürür.
5
- * Her tool'un:
6
- * - name: tool adı
7
- * - description: ne yaptığı
8
- * - parameters: JSON schema
9
- *
10
- * REPL bu listeyi API'ye gönderir, model tool çağrısı yapar,
11
- * biz tool'u çalıştırır, sonucu modele geri veririz.
5
+ * v5.7.17: Emoji + toolset + check_fn + registry entegrasyonu.
12
6
  */
13
7
 
14
8
  const fs = require('fs');
15
9
  const path = require('path');
10
+ const { globalRegistry } = require('./registry');
16
11
 
17
12
  const TOOLS_DIR = path.join(__dirname, '..', 'tools');
18
13
 
19
- /**
20
- * src/tools/*.js dosyalarını oku, her birinin export'ladığı
21
- * tool metadata'sını topla. Eğer tool'un export'unda
22
- * { name, description, parameters, execute } varsa kullan,
23
- * yoksa dosya adından otomatik üret.
24
- */
14
+ // ── Emoji map (central, tek kaynak) ──────────────────────────────────────
15
+ const EMOJI_MAP = {
16
+ // File operations
17
+ read_file: '📖', write_file: '✏️', edit_file: '🖊️', list_dir: '📂', file_search: '🔍', grep_search: '🔎', filesystem: '🗄️',
18
+ // Terminal
19
+ bash: '💻', shell_command: '⌨️',
20
+ // Web
21
+ duckduckgo: '🦆', duckduckgo_search: '🦆', web_search: '🌐', web_readability: '📄', firecrawl: '🔥', searxng: '🔬', searxng_search: '🔬', http_request: '🌍', http: '🌍', exa_search: '🔬', parallel_search: '⚡',
22
+ // Browser
23
+ browser: '🖥️',
24
+ // Memory
25
+ memory: '🧠', memory_write: '🧠', memory_search: '🔍',
26
+ // Skills
27
+ skill_view: '📚', skills_list: '📋', skill_generate: '✨', skills_autoload: '🔄', skills_marketplace: '🏪', skill_manage: '🛠️',
28
+ // Agent
29
+ delegate_task: '👥', llm_task: '🤖', sub_agent: '👤',
30
+ // Documents
31
+ document_extract: '📄', notebook_edit: '📓', notes_add: '📝',
32
+ // Git
33
+ git: '🔀',
34
+ // Plan / Todo
35
+ plan: '📋', todo_write: '✅',
36
+ // Media
37
+ image_generation: '🎨', video_generation: '🎬', music_generation: '🎵', media_understanding: '📺', text_to_speech: '🔊', speech_to_text: '🎤', voice_chat: '🗣️',
38
+ // macOS
39
+ mac_alarm: '⏰', mac_app_open: '🚀', mac_app_quit: '⏹️', mac_notify: '🔔', macos_screenshot: '📸', phone_control: '📱', phone_control_enhanced: '📱',
40
+ // Calendar
41
+ calendar_add: '📅',
42
+ // Reminder
43
+ reminder_add: '⏰',
44
+ // Dashboard
45
+ dashboard: '📊',
46
+ kanban: '📋',
47
+ // Canvas
48
+ canvas: '🎨',
49
+ // Plugin
50
+ plugin: '🔌',
51
+ // Soul
52
+ soul: '💫',
53
+ // Cron
54
+ cron_create: '⏱️',
55
+ // Thread
56
+ thread_ownership: '🔗',
57
+ // Audio understanding
58
+ audio_understanding: '🎵',
59
+ // Code execution
60
+ code_execution: '⚡',
61
+ // Cross-session
62
+ cross_session_memory: '🔗',
63
+ };
25
64
 
26
- /**
27
- * v5.6.1: Provider'a gore tool filtrele
28
- * Groq validator cok kati — sadece basit tool'lar
29
- * Anthropic, OpenAI tam tool seti kullanir
30
- */
65
+ // ── Toolset grouping ─────────────────────────────────────────────────────
66
+ const TOOLSET_MAP = {
67
+ // File
68
+ read_file: 'file', write_file: 'file', edit_file: 'file', list_dir: 'file',
69
+ file_search: 'file', grep_search: 'file', filesystem: 'file',
70
+ // Terminal
71
+ bash: 'terminal', shell_command: 'terminal',
72
+ // Web
73
+ duckduckgo: 'web', web_search: 'web', web_readability: 'web', firecrawl: 'web',
74
+ searxng: 'web', http_request: 'web', http: 'web', exa_search: 'web',
75
+ parallel_search: 'web',
76
+ duckduckgo_search: 'web', searxng_search: 'web',
77
+ // Browser
78
+ browser: 'browser',
79
+ // Memory
80
+ memory: 'memory', memory_write: 'memory', memory_search: 'memory',
81
+ // Skills
82
+ skill_view: 'skills', skills_list: 'skills', skill_generate: 'skills',
83
+ skills_autoload: 'skills', skills_marketplace: 'skills', skill_manage: 'skills',
84
+ // Agent
85
+ delegate_task: 'agent', llm_task: 'agent',
86
+ // Documents
87
+ document_extract: 'documents', notebook_edit: 'documents', notes_add: 'documents',
88
+ // Git
89
+ git: 'git',
90
+ // Plan / Todo
91
+ plan: 'planning', todo_write: 'planning',
92
+ // Media
93
+ image_generation: 'media', video_generation: 'media', music_generation: 'media',
94
+ media_understanding: 'media', text_to_speech: 'media', speech_to_text: 'media',
95
+ voice_chat: 'media', audio_understanding: 'media',
96
+ // macOS
97
+ mac_alarm: 'macos', mac_app_open: 'macos', mac_app_quit: 'macos', mac_notify: 'macos',
98
+ macos_screenshot: 'macos', phone_control: 'macos', phone_control_enhanced: 'macos',
99
+ // Calendar
100
+ calendar_add: 'calendar',
101
+ // Reminder
102
+ reminder_add: 'reminders',
103
+ // Other
104
+ dashboard: 'dashboard', canvas: 'canvas', plugin: 'plugins', soul: 'soul',
105
+ kanban: 'planning',
106
+ cron_create: 'cron', thread_ownership: 'threads', code_execution: 'sandbox',
107
+ cross_session_memory: 'memory',
108
+ };
109
+
110
+ // ── check_fn'ler (tool availability kontrolleri) ────────────────────────
111
+ function _checkBrowser() {
112
+ try {
113
+ require.resolve('playwright');
114
+ return true;
115
+ } catch { return false; }
116
+ }
117
+
118
+ function _checkDuckDuckGo() {
119
+ return true; // API-based, always available
120
+ }
121
+
122
+ function _checkMacOSTools() {
123
+ return process.platform === 'darwin';
124
+ }
125
+
126
+ const CHECK_FN_MAP = {
127
+ browser: _checkBrowser,
128
+ mac_alarm: _checkMacOSTools,
129
+ mac_app_open: _checkMacOSTools,
130
+ mac_app_quit: _checkMacOSTools,
131
+ mac_notify: _checkMacOSTools,
132
+ macos_screenshot: _checkMacOSTools,
133
+ phone_control: _checkMacOSTools,
134
+ phone_control_enhanced: _checkMacOSTools,
135
+ };
136
+
137
+ // ── Provider filtering ───────────────────────────────────────────────────
31
138
  function getToolsForProvider(allTools, providerUrl) {
32
139
  const url = (providerUrl || '').toLowerCase();
33
-
34
- // Groq icin minimum tool seti
35
140
  if (url.includes('groq.com')) {
36
- const allowed = ['read_file', 'write_file', 'bash', 'shell_command', 'list_dir', 'soul', 'memory_write', 'memory_search'];
141
+ const allowed = ['read_file', 'write_file', 'bash', 'shell_command', 'list_dir', 'soul', 'memory', 'memory_write', 'memory_search', 'filesystem', 'grep_search'];
37
142
  return allTools.filter(t => allowed.includes(t.name));
38
143
  }
39
-
40
- // Anthropic, OpenAI, MiniMax tam set
41
144
  return allTools;
42
145
  }
43
146
 
@@ -45,7 +148,6 @@ function loadToolDefinitions() {
45
148
  const tools = [];
46
149
  const files = fs.readdirSync(TOOLS_DIR).filter(f => f.endsWith('.js'));
47
150
 
48
- // v5.6.1: Provider tespiti - Groq icin sadece temel tool'lar
49
151
  let isGroq = false;
50
152
  try {
51
153
  const { getConfig } = require('./config');
@@ -55,105 +157,109 @@ function loadToolDefinitions() {
55
157
  }
56
158
  } catch (e) {}
57
159
 
160
+ let disabledTools = new Set();
161
+ try {
162
+ const { getConfig } = require('./config');
163
+ const cfg = getConfig();
164
+ if (Array.isArray(cfg.disabledTools)) disabledTools = new Set(cfg.disabledTools);
165
+ } catch (e) {}
166
+
58
167
  const GROQ_ALLOWED = new Set([
59
168
  'read_file', 'write_file', 'list_dir', 'bash', 'shell_command',
60
- 'soul', 'memory_write', 'memory_search', 'filesystem', 'grep_search'
169
+ 'soul', 'memory', 'memory_write', 'memory_search', 'filesystem', 'grep_search'
61
170
  ]);
62
171
 
63
172
  for (const file of files) {
64
173
  try {
65
174
  const toolPath = path.join(TOOLS_DIR, file);
66
175
  const mod = require(toolPath);
176
+ const toolName = mod.name || path.basename(file, '.js');
67
177
 
68
- // Groq icin sadece temel tool'lar
69
- if (isGroq) {
70
- const toolName = mod.name || path.basename(file, '.js');
71
- if (!GROQ_ALLOWED.has(toolName)) continue;
72
- }
178
+ if (isGroq && !GROQ_ALLOWED.has(toolName)) continue;
179
+ if (disabledTools.has(toolName)) continue;
73
180
 
74
- // Tool metadata çıkar
75
181
  const meta = {
76
- name: mod.name || path.basename(file, '.js'),
182
+ name: toolName,
77
183
  description: mod.description || `${path.basename(file, '.js')} tool`,
78
184
  parameters: mod.parameters || mod.inputSchema || { type: 'object', properties: {} },
79
185
  execute: mod.execute || (mod.default && mod.default.execute) || null,
186
+ emoji: EMOJI_MAP[toolName] || '',
187
+ toolset: TOOLSET_MAP[toolName] || 'general',
188
+ checkFn: CHECK_FN_MAP[toolName] || null,
80
189
  };
81
190
 
82
- // Eğer execute fonksiyonu varsa ekle (CLI'da çalıştırmak için)
83
191
  if (meta.execute) {
84
192
  tools.push(meta);
193
+ // Registry'ye kaydet
194
+ globalRegistry.register({
195
+ name: meta.name,
196
+ toolset: meta.toolset,
197
+ schema: { name: meta.name, description: meta.description, parameters: meta.parameters },
198
+ handler: meta.execute,
199
+ checkFn: meta.checkFn,
200
+ emoji: meta.emoji,
201
+ });
85
202
  }
86
203
  } catch (e) {
87
- // Sessizce atla — bozuk tool dosyaları kritik değil
204
+ // Sessizce atla
88
205
  }
89
206
  }
90
-
91
207
  return tools;
92
208
  }
93
209
 
94
- /**
95
- * OpenAI uyumlu API'ye gönderilecek format:
96
- * [{ type: "function", function: { name, description, parameters } }]
97
- */
98
- function toOpenAIFormat(toolDefs) {
99
- // v5.6.6: Yasakli tool'lari filtrele, alias'lari cozumle
100
- // v5.6.6: Inline tool filtre - model halusinasyonla cagirabilecegi tool'lari engelle
101
- toolDefs = toolDefs.filter(t =>
102
- !['brave_search','brave-web-search','google_search','web_search','browse','open','search','shell','bash_command','execute_command','run_command','sql','query','lookup'].includes(t.name)
103
- );
210
+ const ALIAS_MAP = {
211
+ 'brave_search': 'duckduckgo', 'brave-web-search': 'duckduckgo',
212
+ 'google_search': 'duckduckgo', 'web_search': 'duckduckgo',
213
+ 'browse': 'browser', 'shell': 'bash', 'bash_command': 'bash',
214
+ 'execute_command': 'bash', 'run_command': 'bash',
215
+ };
104
216
 
217
+ const BLOCKED_NAMES = new Set([
218
+ 'brave_search', 'brave-web-search', 'google_search', 'web_search',
219
+ 'browse', 'open', 'search', 'shell', 'bash_command', 'execute_command',
220
+ 'run_command', 'sql', 'query', 'lookup',
221
+ ]);
222
+
223
+ function toOpenAIFormat(toolDefs) {
105
224
  return toolDefs
106
- .filter(t => !['brave_search','brave-web-search','google_search','web_search','browse','open','search','shell','bash_command','execute_command','run_command','sql','query','lookup'].includes(t.name))
225
+ .filter(t => !BLOCKED_NAMES.has(t.name))
226
+ .filter(t => {
227
+ if (t.checkFn) {
228
+ try { return t.checkFn() !== false; } catch { return false; }
229
+ }
230
+ return true;
231
+ })
107
232
  .map(t => {
108
- // Alias varsa degistir. Önceki kod ALIAS_MAP'i declare ediyordu ama
109
- // değiştirmek için TOOL_ALIASES kullanıyordu (undefined) alias hit
110
- // olduğunda ReferenceError ile çöküyordu. ALIAS_MAP'e döndürüldü.
111
- const ALIAS_MAP = { 'brave_search':'duckduckgo_search','brave-web-search':'duckduckgo_search','google_search':'duckduckgo_search','web_search':'duckduckgo_search','browse':'browser','shell':'bash','bash_command':'bash','execute_command':'bash','run_command':'bash' };
112
- if (ALIAS_MAP[t.name]) {
113
- t = { ...t, name: ALIAS_MAP[t.name] };
233
+ let name = t.name;
234
+ if (ALIAS_MAP[name]) name = ALIAS_MAP[name];
235
+
236
+ const cleanParams = JSON.parse(JSON.stringify(t.parameters || {}));
237
+ if (cleanParams.properties) {
238
+ Object.keys(cleanParams.properties).forEach(key => {
239
+ const prop = cleanParams.properties[key];
240
+ if (Array.isArray(prop.type)) prop.type = prop.type[0];
241
+ delete prop.additionalProperties;
242
+ });
114
243
  }
115
- // v5.4.21: Groq uyumluluk - additionalProperties: false kaldirildi
116
- // ve gereksiz kisitlamalar temizlendi
117
- const cleanParams = JSON.parse(JSON.stringify(t.parameters || {}));
118
- if (cleanParams.properties) {
119
- Object.keys(cleanParams.properties).forEach(key => {
120
- const prop = cleanParams.properties[key];
121
- // "type": ["number", "string"] union types Groq'da hata verir
122
- // Sadece ilk tipi al
123
- if (Array.isArray(prop.type)) {
124
- prop.type = prop.type[0];
125
- }
126
- // additionalProperties kaldir
127
- delete prop.additionalProperties;
128
- });
129
- }
130
- // Groq icin required kismi bazen sorun cikarir - olduugu gibi birak
131
- // ama type validation'u gevset
132
- return {
133
- type: 'function',
134
- function: {
135
- name: t.name,
136
- description: t.description,
137
- parameters: cleanParams,
138
- },
139
- };
140
- });
244
+
245
+ return {
246
+ type: 'function',
247
+ function: { name, description: t.description, parameters: cleanParams },
248
+ };
249
+ });
141
250
  }
142
251
 
143
- /**
144
- * Tool çağrısını çalıştır
145
- * @param toolName - tool adı
146
- * @param args - tool argümanları (object)
147
- * @param toolDefs - loadToolDefinitions() sonucu
148
- * @returns { result, error }
149
- */
150
252
  async function executeTool(toolName, args, toolDefs) {
151
253
  const tool = toolDefs.find(t => t.name === toolName);
152
- if (!tool) {
153
- return { error: `Tool bulunamadı: ${toolName}` };
154
- }
155
- if (!tool.execute) {
156
- return { error: `Tool execute fonksiyonu yok: ${toolName}` };
254
+ if (!tool) return { error: `Tool bulunamadı: ${toolName}` };
255
+ if (!tool.execute) return { error: `Tool execute fonksiyonu yok: ${toolName}` };
256
+ // checkFn — tool disabled? (re-check at runtime)
257
+ if (tool.checkFn) {
258
+ try {
259
+ if (tool.checkFn() === false) return { error: `${toolName} şu anda kullanılamıyor (check_fn engelledi)` };
260
+ } catch {
261
+ return { error: `${toolName} kontrol hatası` };
262
+ }
157
263
  }
158
264
  try {
159
265
  const result = await tool.execute(args || {});
@@ -164,7 +270,6 @@ async function executeTool(toolName, args, toolDefs) {
164
270
  }
165
271
 
166
272
  module.exports = {
167
- loadToolDefinitions,
168
- toOpenAIFormat,
169
- executeTool,
273
+ loadToolDefinitions, toOpenAIFormat, executeTool, getToolsForProvider,
274
+ EMOJI_MAP, TOOLSET_MAP, CHECK_FN_MAP,
170
275
  };
package/src/tools/http.js DELETED
@@ -1,78 +0,0 @@
1
- module.exports = {
2
- name: 'http_request',
3
- description: 'Make HTTP requests to any URL (GET, POST, PUT, DELETE, PATCH)',
4
- inputSchema: {
5
- type: 'object',
6
- properties: {
7
- method: {
8
- type: 'string',
9
- description: 'HTTP method: GET, POST, PUT, DELETE, PATCH',
10
- enum: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
11
- default: 'GET'
12
- },
13
- url: {
14
- type: 'string',
15
- description: 'Full URL to request'
16
- },
17
- headers: {
18
- type: 'object',
19
- description: 'Optional headers (key-value pairs)'
20
- },
21
- body: {
22
- type: 'object',
23
- description: 'Optional request body (for POST/PUT/PATCH)'
24
- }
25
- },
26
- required: ['url']
27
- },
28
-
29
- async execute(params) {
30
- try {
31
- const method = (params.method || 'GET').toUpperCase();
32
-
33
- const options = {
34
- method,
35
- headers: {
36
- 'Content-Type': 'application/json',
37
- 'User-Agent': 'NatureCo-CLI/2.7.0',
38
- ...(params.headers || {})
39
- }
40
- };
41
-
42
- // Add body for POST/PUT/PATCH
43
- if (params.body && ['POST', 'PUT', 'PATCH'].includes(method)) {
44
- options.body = JSON.stringify(params.body);
45
- }
46
-
47
- const response = await fetch(params.url, options);
48
- const text = await response.text();
49
-
50
- // Try to parse as JSON
51
- let data;
52
- try {
53
- data = JSON.parse(text);
54
- } catch {
55
- data = text;
56
- }
57
-
58
- // Truncate large responses
59
- if (typeof data === 'string' && data.length > 2000) {
60
- data = data.slice(0, 2000) + '... (truncated)';
61
- }
62
-
63
- return {
64
- success: true,
65
- status: response.status,
66
- ok: response.ok,
67
- statusText: response.statusText,
68
- headers: Object.fromEntries(response.headers.entries()),
69
- data: data
70
- };
71
- } catch (error) {
72
- return {
73
- success: false,
74
- error: error.message
75
- };
76
- }
77
- }
78
- };