feat: wire 10 new tools — file_read, file_write, glob, grep, web_fetch, task CRUD, send_message, schedule_cron

- 10 new JS tool classes in src/tools/ (clean, no framework deps)
- tools/index.js: registry-based init with env toggles
- bot/index.js: 16 tool definitions + 16 handlers (was 4)
- Added glob npm dependency
- Tools: bash, file_edit, file_read, file_write, glob, grep, web_search, web_fetch, git, task_create/update/list, send_message, schedule_cron, delegate_agent, run_skill
This commit is contained in:
admin
2026-05-05 16:43:05 +00:00
Unverified
parent 78d994fdda
commit 0a81aa2b82
14 changed files with 751 additions and 103 deletions

View File

@@ -204,80 +204,135 @@ export async function initBot(config, api, tools, skills, agents) {
const tools = [];
const toolMap = svc.toolMap;
if (toolMap.has('bash')) {
tools.push({
type: 'function',
function: {
name: 'bash',
description: 'Execute a shell command',
parameters: {
type: 'object', properties: {
command: { type: 'string', description: 'Shell command' },
timeout: { type: 'number', description: 'Timeout ms (default 300000)' },
}, required: ['command'],
},
},
});
}
if (toolMap.has('web_search')) {
tools.push({
type: 'function',
function: {
name: 'web_search',
description: 'Search the web',
parameters: {
type: 'object', properties: {
query: { type: 'string', description: 'Search query' },
num_results: { type: 'number', description: 'Results count (default 5)' },
}, required: ['query'],
},
},
});
}
if (toolMap.has('git')) {
tools.push({
type: 'function',
function: {
name: 'git',
description: 'Git operations: status, log, diff, commit, push, pull',
parameters: {
type: 'object', properties: {
action: { type: 'string', enum: ['status', 'log', 'diff', 'commit', 'push', 'pull'] },
params: { type: 'array', items: { type: 'string' } },
}, required: ['action'],
},
},
});
}
if (svc.agents.length) {
tools.push({
type: 'function',
function: {
name: 'delegate_agent',
description: 'Delegate to a specialized agent role',
parameters: {
type: 'object', properties: {
agent_id: { type: 'string', enum: svc.agents.map(a => a.id) },
task: { type: 'string', description: 'Task description' },
}, required: ['agent_id', 'task'],
},
},
});
}
if (svc.skills.length) {
tools.push({
type: 'function',
function: {
name: 'run_skill',
description: 'Run a skill by name',
parameters: {
type: 'object', properties: {
skill: { type: 'string', enum: svc.skills.map(s => s.name) },
input: { type: 'string' },
}, required: ['skill'],
},
},
});
// ── Tool definitions for the AI API (OpenAI function-calling format) ──
const TOOL_DEFS = {
bash: {
description: 'Execute a shell command',
parameters: { type: 'object', properties: {
command: { type: 'string', description: 'Shell command to execute' },
timeout: { type: 'number', description: 'Timeout in ms (default 300000)' },
}, required: ['command'] },
},
file_edit: {
description: 'Edit files — read, write, append, or find-and-replace',
parameters: { type: 'object', properties: {
action: { type: 'string', enum: ['read', 'write', 'append', 'edit'], description: 'Operation' },
file_path: { type: 'string', description: 'File path' },
content: { type: 'string', description: 'Content to write/append (for write/append)' },
old_text: { type: 'string', description: 'Text to find (for edit)' },
new_text: { type: 'string', description: 'Replacement text (for edit)' },
}, required: ['action', 'file_path'] },
},
file_read: {
description: 'Read file contents with line numbers and pagination',
parameters: { type: 'object', properties: {
file_path: { type: 'string', description: 'File path to read' },
offset: { type: 'number', description: 'Start line (1-indexed, default 1)' },
limit: { type: 'number', description: 'Max lines (default 500)' },
}, required: ['file_path'] },
},
file_write: {
description: 'Write content to a file (overwrites entire file)',
parameters: { type: 'object', properties: {
file_path: { type: 'string', description: 'File path' },
content: { type: 'string', description: 'Content to write' },
}, required: ['file_path', 'content'] },
},
glob: {
description: 'Find files matching a glob pattern',
parameters: { type: 'object', properties: {
pattern: { type: 'string', description: 'Glob pattern (e.g. "**/*.js")' },
cwd: { type: 'string', description: 'Working directory (default: current)' },
}, required: ['pattern'] },
},
grep: {
description: 'Search file contents using regex (ripgrep)',
parameters: { type: 'object', properties: {
pattern: { type: 'string', description: 'Regex pattern' },
path: { type: 'string', description: 'Directory to search (default: .)' },
file_glob: { type: 'string', description: 'File filter (e.g. "*.py")' },
max_results: { type: 'number', description: 'Max matches (default 20)' },
context: { type: 'number', description: 'Context lines before/after (default 0)' },
}, required: ['pattern'] },
},
web_search: {
description: 'Search the web for information',
parameters: { type: 'object', properties: {
query: { type: 'string', description: 'Search query' },
num_results: { type: 'number', description: 'Results count (default 5)' },
}, required: ['query'] },
},
web_fetch: {
description: 'Fetch content from a URL and return text',
parameters: { type: 'object', properties: {
url: { type: 'string', description: 'URL to fetch' },
max_length: { type: 'number', description: 'Max chars to return (default 15000)' },
}, required: ['url'] },
},
git: {
description: 'Git operations: status, log, diff, commit, push, pull',
parameters: { type: 'object', properties: {
action: { type: 'string', enum: ['status', 'log', 'diff', 'commit', 'push', 'pull'] },
params: { type: 'array', items: { type: 'string' } },
}, required: ['action'] },
},
task_create: {
description: 'Create a new task',
parameters: { type: 'object', properties: {
description: { type: 'string', description: 'Task description' },
}, required: ['description'] },
},
task_update: {
description: 'Update task status',
parameters: { type: 'object', properties: {
task_id: { type: 'string', description: 'Task ID' },
status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'cancelled'] },
}, required: ['task_id', 'status'] },
},
task_list: {
description: 'List all tasks with status',
parameters: { type: 'object', properties: {
status: { type: 'string', description: 'Filter by status (optional)' },
} },
},
send_message: {
description: 'Send a message to Telegram chat or channel',
parameters: { type: 'object', properties: {
chat_id: { type: 'string', description: 'Target chat ID (optional, uses default)' },
message: { type: 'string', description: 'Message text to send' },
}, required: ['message'] },
},
schedule_cron: {
description: 'Manage cron jobs (create/list/remove)',
parameters: { type: 'object', properties: {
action: { type: 'string', enum: ['create', 'list', 'remove'] },
name: { type: 'string', description: 'Job name' },
schedule: { type: 'string', description: 'Cron schedule (e.g. "0 9 * * *")' },
command: { type: 'string', description: 'Command to run' },
}, required: ['action'] },
},
delegate_agent: {
description: 'Delegate to a specialized agent role',
parameters: { type: 'object', properties: {
agent_id: { type: 'string', enum: svc.agents.map(a => a.id) },
task: { type: 'string', description: 'Task description' },
}, required: ['agent_id', 'task'] },
},
run_skill: {
description: 'Run a skill by name',
parameters: { type: 'object', properties: {
skill: { type: 'string', enum: svc.skills.map(s => s.name) },
input: { type: 'string' },
}, required: ['skill'] },
},
};
// Register all tools that have a matching class loaded
for (const [name, def] of Object.entries(TOOL_DEFS)) {
if (name === 'delegate_agent' && !svc.agents.length) continue;
if (name === 'run_skill' && !svc.skills.length) continue;
if (!toolMap.has(name) && name !== 'delegate_agent' && name !== 'run_skill') continue;
tools.push({ type: 'function', function: { name, ...def } });
}
try {
@@ -402,6 +457,7 @@ export async function initBot(config, api, tools, skills, agents) {
return fullResponse || '✅ Done.';
}
// ── Tool handlers: route API tool_calls to tool class methods ──
const toolHandlers = {
bash: async (args) => {
const tool = svc.toolMap.get('bash');
@@ -414,6 +470,42 @@ export async function initBot(config, api, tools, skills, agents) {
return `❌ Exit ${r.code}\n\`\`\`\n${err || out}\n\`\`\``;
} catch (e) { return `❌ Bash error: ${e.message}`; }
},
file_edit: async (args) => {
const tool = svc.toolMap.get('file_edit');
if (!tool) return '❌ File edit tool unavailable.';
try {
const r = await tool[args.action](args.file_path, args.content, args.old_text, args.new_text);
return typeof r === 'string' ? r : (r.success ? `${JSON.stringify(r)}` : `${r.error}`);
} catch (e) { return `❌ File edit error: ${e.message}`; }
},
file_read: async (args) => {
const tool = svc.toolMap.get('file_read');
if (!tool) return '❌ File read tool unavailable.';
try {
return await tool.execute(args);
} catch (e) { return `❌ File read error: ${e.message}`; }
},
file_write: async (args) => {
const tool = svc.toolMap.get('file_write');
if (!tool) return '❌ File write tool unavailable.';
try {
return await tool.execute(args);
} catch (e) { return `❌ File write error: ${e.message}`; }
},
glob: async (args) => {
const tool = svc.toolMap.get('glob');
if (!tool) return '❌ Glob tool unavailable.';
try {
return await tool.execute(args);
} catch (e) { return `❌ Glob error: ${e.message}`; }
},
grep: async (args) => {
const tool = svc.toolMap.get('grep');
if (!tool) return '❌ Grep tool unavailable.';
try {
return await tool.execute(args);
} catch (e) { return `❌ Grep error: ${e.message}`; }
},
web_search: async (args) => {
const tool = svc.toolMap.get('web_search');
if (!tool) return '❌ Web search unavailable.';
@@ -426,16 +518,48 @@ export async function initBot(config, api, tools, skills, agents) {
return `🔍 *${args.query}*\n\nNo results.`;
} catch (e) { return `❌ Search error: ${e.message}`; }
},
web_fetch: async (args) => {
const tool = svc.toolMap.get('web_fetch');
if (!tool) return '❌ Web fetch tool unavailable.';
try {
return await tool.execute(args);
} catch (e) { return `❌ Fetch error: ${e.message}`; }
},
git: async (args) => {
const tool = svc.toolMap.get('git');
if (!tool) return '❌ Git tool unavailable.';
try {
const method = tool[args.action];
if (!method) return `❌ Unknown: ${args.action}`;
if (!method) return `❌ Unknown git action: ${args.action}`;
const r = await method.call(tool, ...(args.params || []));
return r.success ? `${r.status || JSON.stringify(r)}` : `${r.stderr || r.error}`;
} catch (e) { return `❌ Git error: ${e.message}`; }
},
task_create: async (args) => {
const tool = svc.toolMap.get('task_create');
if (!tool) return '❌ Task tool unavailable.';
try { return await tool.execute(args); } catch (e) { return `${e.message}`; }
},
task_update: async (args) => {
const tool = svc.toolMap.get('task_update');
if (!tool) return '❌ Task tool unavailable.';
try { return await tool.execute(args); } catch (e) { return `${e.message}`; }
},
task_list: async (args) => {
const tool = svc.toolMap.get('task_list');
if (!tool) return '❌ Task tool unavailable.';
try { return await tool.execute(args); } catch (e) { return `${e.message}`; }
},
send_message: async (args) => {
const tool = svc.toolMap.get('send_message');
if (!tool) return '❌ Send message tool unavailable.';
try { return await tool.execute(args); } catch (e) { return `${e.message}`; }
},
schedule_cron: async (args) => {
const tool = svc.toolMap.get('schedule_cron');
if (!tool) return '❌ Cron tool unavailable.';
try { return await tool.execute(args); } catch (e) { return `${e.message}`; }
},
delegate_agent: async (args) => {
const agent = svc.agents.find(a => a.id === args.agent_id);
if (!agent) return `❌ Agent not found: ${args.agent_id}`;