export interface SkillDefinition { id: string; name: string; description: string; enabled: boolean; requiresPermission?: boolean; icon?: string; } export interface SkillExecutionResult { success: boolean; output?: string; error?: string; requiresPermission?: boolean; permissionDetails?: { type: 'command' | 'file' | 'network'; title: string; description: string; details: Record; }; } export interface SkillContext { workingDir?: string; userId: string; sessionId: string; requirePermission?: (details: SkillExecutionResult['permissionDetails']) => Promise; } export interface BaseSkill { id: string; name: string; description: string; enabled: boolean; requiresPermission: boolean; execute(input: string, context: SkillContext): Promise; } // Built-in skills export const builtInSkills: BaseSkill[] = []; class WebSearchSkill implements BaseSkill { id = 'web-search'; name = 'Web Search'; description = 'Search the web for information'; enabled = true; requiresPermission = false; async execute(input: string, _context: SkillContext): Promise { // TODO: Implement web search return { success: true, output: `Search results for: ${input}`, }; } } class CalculatorSkill implements BaseSkill { id = 'calculator'; name = 'Calculator'; description = 'Perform mathematical calculations'; enabled = true; requiresPermission = false; async execute(input: string, _context: SkillContext): Promise { try { const sanitized = input.replace(/[^0-9+\-*/.()%]/g, ''); // eslint-disable-next-line no-new-func const result = new Function(`"use strict"; return (${sanitized})`)(); return { success: true, output: String(result), }; } catch { return { success: false, error: 'Invalid expression', }; } } } class FileReadSkill implements BaseSkill { id = 'file-read'; name = 'File Read'; description = 'Read content from a file'; enabled = true; requiresPermission = true; async execute(input: string, context: SkillContext): Promise { const filePath = input.trim(); if (context.requirePermission) { const approved = await context.requirePermission({ type: 'file', title: 'Read File', description: `Read content from file: ${filePath}`, details: { file: filePath }, }); if (!approved) { return { success: false, error: 'Permission denied', }; } } return { success: true, output: `File read: ${filePath}`, }; } } class CommandRunSkill implements BaseSkill { id = 'command-run'; name = 'Command Run'; description = 'Run shell commands'; enabled = true; requiresPermission = true; async execute(input: string, context: SkillContext): Promise { if (context.requirePermission) { const approved = await context.requirePermission({ type: 'command', title: 'Run Command', description: `Execute shell command: ${input}`, details: { command: 'sh', args: ['-c', input], env: {} }, }); if (!approved) { return { success: false, error: 'Permission denied', }; } } return { success: true, output: `Command would execute: ${input}`, }; } } class NetworkFetchSkill implements BaseSkill { id = 'network-fetch'; name = 'Network Fetch'; description = 'Fetch content from a URL'; enabled = true; requiresPermission = true; async execute(input: string, context: SkillContext): Promise { const url = input.trim(); if (context.requirePermission) { const approved = await context.requirePermission({ type: 'network', title: 'Network Request', description: `Fetch content from: ${url}`, details: { url }, }); if (!approved) { return { success: false, error: 'Permission denied', }; } } return { success: true, output: `Would fetch: ${url}`, }; } } // Skill manager class SkillManager { private skills: Map = new Map(); register(skill: BaseSkill) { this.skills.set(skill.id, skill); } getSkill(id: string): BaseSkill | undefined { return this.skills.get(id); } listSkills(): SkillDefinition[] { return Array.from(this.skills.values()).map((s) => ({ id: s.id, name: s.name, description: s.description, enabled: s.enabled, requiresPermission: s.requiresPermission, })); } async executeSkill( skillId: string, input: string, context: SkillContext, requirePermission?: SkillContext['requirePermission'] ): Promise { const skill = this.skills.get(skillId); if (!skill) { return { success: false, error: `Skill not found: ${skillId}` }; } if (!skill.enabled) { return { success: false, error: `Skill disabled: ${skillId}` }; } const ctx = { ...context, requirePermission }; // Check if permission is required but not provided if (skill.requiresPermission && !requirePermission) { const result = await skill.execute(input, ctx); return { ...result, requiresPermission: true, }; } return skill.execute(input, ctx); } async executeAuto( input: string, context: SkillContext, requirePermission?: SkillContext['requirePermission'] ): Promise { const lowerInput = input.toLowerCase(); // Calculator if (/^\d+[\s+\-*/%()\d]+$/.test(input.replace(/\s/g, ''))) { return this.executeSkill('calculator', input, context, requirePermission); } // Web search if (lowerInput.startsWith('search:') || lowerInput.startsWith('查找:') || lowerInput.startsWith('搜索:')) { const query = input.replace(/^(search|查找|搜索):\s*/i, ''); return this.executeSkill('web-search', query, context, requirePermission); } // Command (starts with !) if (lowerInput.startsWith('!') || lowerInput.startsWith('run:')) { const cmd = input.replace(/^(!|run:)\s*/i, ''); return this.executeSkill('command-run', cmd, context, requirePermission); } // File read (starts with cat:) if (lowerInput.startsWith('cat:')) { const file = input.replace(/^cat:\s*/i, ''); return this.executeSkill('file-read', file, context, requirePermission); } // Network fetch (starts with fetch:) if (lowerInput.startsWith('fetch:') || lowerInput.startsWith('curl:')) { const url = input.replace(/^(fetch|curl):\s*/i, ''); return this.executeSkill('network-fetch', url, context, requirePermission); } return null; } } export const skillManager = new SkillManager(); // Register built-in skills skillManager.register(new WebSearchSkill()); skillManager.register(new CalculatorSkill()); skillManager.register(new FileReadSkill()); skillManager.register(new CommandRunSkill()); skillManager.register(new NetworkFetchSkill());