- Created skills/ directory - Moved 272 skills to skills/ subfolder - Kept agents/ at root level - Kept installation scripts and docs at root level Repository structure: - skills/ - All 272 skills from skills.sh - agents/ - Agent definitions - *.sh, *.ps1 - Installation scripts - README.md, etc. - Documentation Co-Authored-By: Claude <noreply@anthropic.com>
386 lines
8.4 KiB
JavaScript
386 lines
8.4 KiB
JavaScript
/**
|
|
* Modular Tool System for Terminal Execution
|
|
* Inspired by AGIAgent, AutoGen, and ReAct patterns
|
|
*
|
|
* Architecture:
|
|
* - Base Tool interface for extensibility
|
|
* - Tool Registry for managing available tools
|
|
* - Enhanced Intent Analysis for smart tool selection
|
|
* - Structured error handling and output formatting
|
|
*/
|
|
|
|
/**
|
|
* Base Tool Interface
|
|
* All tools must extend this class
|
|
*/
|
|
class BaseTool {
|
|
/**
|
|
* @param {Object} config - Tool configuration
|
|
* @param {string} config.name - Unique tool name
|
|
* @param {string} config.description - What this tool does
|
|
* @param {Array} config.parameters - Parameter definitions
|
|
* @param {Object} config.options - Tool-specific options
|
|
*/
|
|
constructor(config) {
|
|
if (!config.name) {
|
|
throw new Error('Tool must have a name');
|
|
}
|
|
this.name = config.name;
|
|
this.description = config.description || '';
|
|
this.parameters = config.parameters || [];
|
|
this.options = config.options || {};
|
|
this.enabled = config.enabled !== false;
|
|
}
|
|
|
|
/**
|
|
* Execute the tool with given parameters
|
|
* Must be implemented by subclasses
|
|
*
|
|
* @param {Object} params - Execution parameters
|
|
* @returns {Promise<ToolResult>} Execution result
|
|
*/
|
|
async execute(params) {
|
|
throw new Error(`Tool ${this.name} must implement execute() method`);
|
|
}
|
|
|
|
/**
|
|
* Validate parameters before execution
|
|
*
|
|
* @param {Object} params - Parameters to validate
|
|
* @returns {Object} Validation result with valid flag and errors array
|
|
*/
|
|
validate(params) {
|
|
const errors = [];
|
|
|
|
for (const param of this.parameters) {
|
|
const value = params[param.name];
|
|
|
|
// Check required parameters
|
|
if (param.required && value === undefined) {
|
|
errors.push(`Required parameter '${param.name}' is missing`);
|
|
continue;
|
|
}
|
|
|
|
// Type validation
|
|
if (value !== undefined && param.type) {
|
|
const actualType = Array.isArray(value) ? 'array' : typeof value;
|
|
if (actualType !== param.type) {
|
|
errors.push(
|
|
`Parameter '${param.name}' should be ${param.type}, got ${actualType}`
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
valid: errors.length === 0,
|
|
errors
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get tool metadata
|
|
*/
|
|
getMetadata() {
|
|
return {
|
|
name: this.name,
|
|
description: this.description,
|
|
parameters: this.parameters,
|
|
enabled: this.enabled
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tool Result Structure
|
|
*/
|
|
class ToolResult {
|
|
/**
|
|
* @param {Object} result
|
|
* @param {boolean} result.success - Whether execution succeeded
|
|
* @param {*} result.data - Result data
|
|
* @param {string} result.output - Formatted output string
|
|
* @param {Error} result.error - Error if failed
|
|
* @param {Object} result.metadata - Additional metadata
|
|
*/
|
|
constructor(result) {
|
|
this.success = result.success !== false;
|
|
this.data = result.data;
|
|
this.output = result.output || '';
|
|
this.error = result.error;
|
|
this.metadata = result.metadata || {};
|
|
this.timestamp = new Date().toISOString();
|
|
}
|
|
|
|
/**
|
|
* Create a success result
|
|
*/
|
|
static success(data, output = '', metadata = {}) {
|
|
return new ToolResult({
|
|
success: true,
|
|
data,
|
|
output,
|
|
metadata
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Create a failure result
|
|
*/
|
|
static failure(error, output = '', metadata = {}) {
|
|
return new ToolResult({
|
|
success: false,
|
|
error,
|
|
output,
|
|
metadata
|
|
});
|
|
}
|
|
|
|
toJSON() {
|
|
return {
|
|
success: this.success,
|
|
data: this.data,
|
|
output: this.output,
|
|
error: this.error ? this.error.message : null,
|
|
metadata: this.metadata,
|
|
timestamp: this.timestamp
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tool Registry
|
|
* Manages available tools and their execution
|
|
*/
|
|
class ToolRegistry {
|
|
constructor() {
|
|
this.tools = new Map();
|
|
this.middlewares = [];
|
|
this.executionHistory = [];
|
|
this.maxHistorySize = 100;
|
|
}
|
|
|
|
/**
|
|
* Register a new tool
|
|
*
|
|
* @param {BaseTool} tool - Tool instance to register
|
|
*/
|
|
register(tool) {
|
|
if (!(tool instanceof BaseTool)) {
|
|
throw new Error('Tool must extend BaseTool');
|
|
}
|
|
|
|
if (this.tools.has(tool.name)) {
|
|
throw new Error(`Tool '${tool.name}' is already registered`);
|
|
}
|
|
|
|
this.tools.set(tool.name, tool);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Unregister a tool
|
|
*
|
|
* @param {string} name - Tool name to unregister
|
|
*/
|
|
unregister(name) {
|
|
return this.tools.delete(name);
|
|
}
|
|
|
|
/**
|
|
* Get a tool by name
|
|
*
|
|
* @param {string} name - Tool name
|
|
* @returns {BaseTool|null}
|
|
*/
|
|
get(name) {
|
|
return this.tools.get(name) || null;
|
|
}
|
|
|
|
/**
|
|
* Check if a tool exists and is enabled
|
|
*
|
|
* @param {string} name - Tool name
|
|
*/
|
|
has(name) {
|
|
const tool = this.tools.get(name);
|
|
return tool && tool.enabled;
|
|
}
|
|
|
|
/**
|
|
* List all available tools
|
|
*
|
|
* @param {Object} options - Listing options
|
|
* @param {boolean} options.includeDisabled - Include disabled tools
|
|
*/
|
|
list(options = {}) {
|
|
const tools = Array.from(this.tools.values());
|
|
|
|
if (!options.includeDisabled) {
|
|
return tools.filter(t => t.enabled);
|
|
}
|
|
|
|
return tools;
|
|
}
|
|
|
|
/**
|
|
* List tools metadata
|
|
*/
|
|
listMetadata() {
|
|
return this.list().map(tool => tool.getMetadata());
|
|
}
|
|
|
|
/**
|
|
* Execute a tool by name
|
|
*
|
|
* @param {string} name - Tool name
|
|
* @param {Object} params - Execution parameters
|
|
* @returns {Promise<ToolResult>}
|
|
*/
|
|
async execute(name, params = {}) {
|
|
const startTime = Date.now();
|
|
|
|
try {
|
|
// Get tool
|
|
const tool = this.get(name);
|
|
if (!tool) {
|
|
throw new Error(`Tool '${name}' not found or disabled`);
|
|
}
|
|
|
|
// Validate parameters
|
|
const validation = tool.validate(params);
|
|
if (!validation.valid) {
|
|
throw new Error(`Parameter validation failed: ${validation.errors.join(', ')}`);
|
|
}
|
|
|
|
// Run before middlewares
|
|
for (const mw of this.middlewares) {
|
|
if (mw.before) {
|
|
await mw.before(name, params);
|
|
}
|
|
}
|
|
|
|
// Execute tool
|
|
let result = await tool.execute(params);
|
|
|
|
// Run after middlewares
|
|
for (const mw of this.middlewares) {
|
|
if (mw.after) {
|
|
result = await mw.after(name, params, result) || result;
|
|
}
|
|
}
|
|
|
|
// Record history
|
|
this.recordExecution({
|
|
tool: name,
|
|
params,
|
|
result: result.toJSON(),
|
|
duration: Date.now() - startTime,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
return result;
|
|
} catch (error) {
|
|
const result = ToolResult.failure(error, error.message);
|
|
|
|
// Record failure
|
|
this.recordExecution({
|
|
tool: name,
|
|
params,
|
|
result: result.toJSON(),
|
|
duration: Date.now() - startTime,
|
|
timestamp: new Date().toISOString()
|
|
});
|
|
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add middleware for execution hooks
|
|
*
|
|
* @param {Object} middleware - Middleware with before/after hooks
|
|
*/
|
|
use(middleware) {
|
|
this.middlewares.push(middleware);
|
|
return this;
|
|
}
|
|
|
|
/**
|
|
* Record execution in history
|
|
*/
|
|
recordExecution(record) {
|
|
this.executionHistory.push(record);
|
|
|
|
// Limit history size
|
|
if (this.executionHistory.length > this.maxHistorySize) {
|
|
this.executionHistory.shift();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get execution history
|
|
*/
|
|
getHistory(options = {}) {
|
|
let history = this.executionHistory;
|
|
|
|
if (options.tool) {
|
|
history = history.filter(r => r.tool === options.tool);
|
|
}
|
|
|
|
if (options.limit) {
|
|
history = history.slice(-options.limit);
|
|
}
|
|
|
|
return history;
|
|
}
|
|
|
|
/**
|
|
* Clear execution history
|
|
*/
|
|
clearHistory() {
|
|
this.executionHistory = [];
|
|
}
|
|
|
|
/**
|
|
* Get statistics
|
|
*/
|
|
getStats() {
|
|
const stats = {
|
|
totalExecutions: this.executionHistory.length,
|
|
toolUsage: {},
|
|
successRate: 0,
|
|
avgDuration: 0
|
|
};
|
|
|
|
let successCount = 0;
|
|
let totalDuration = 0;
|
|
|
|
for (const record of this.executionHistory) {
|
|
stats.toolUsage[record.tool] = (stats.toolUsage[record.tool] || 0) + 1;
|
|
|
|
if (record.result.success) {
|
|
successCount++;
|
|
}
|
|
|
|
totalDuration += record.duration;
|
|
}
|
|
|
|
if (stats.totalExecutions > 0) {
|
|
stats.successRate = (successCount / stats.totalExecutions * 100).toFixed(2);
|
|
stats.avgDuration = (totalDuration / stats.totalExecutions).toFixed(2);
|
|
}
|
|
|
|
return stats;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Export all classes
|
|
*/
|
|
module.exports = {
|
|
BaseTool,
|
|
ToolResult,
|
|
ToolRegistry
|
|
};
|