UNPKG

claude-flow-novice

Version:

Claude Flow Novice - Advanced orchestration platform for multi-agent AI workflows with CFN Loop architecture Includes Local RuVector Accelerator and all CFN skills for complete functionality.

535 lines (529 loc) 16.1 kB
/** * Skills Database Query Layer * * Provides type-safe SQL queries for skill metadata operations. * Part of SkillLoader API implementation. * * @module skills-query */ /** * Bootstrap skill IDs that should always be loaded */ export const BOOTSTRAP_SKILL_IDS = [ 'cfn-coordination', 'hook-pipeline', 'pre-edit-backup', 'cfn-agent-spawning', 'cfn-loop-validation' ]; /** * SQL queries for skills database operations */ export class SkillsQueryBuilder { /** * Get skills by agent type with optional context filtering */ static getSkillsByAgentType(agentType, contextKeywords, phase) { const params = [ agentType ]; let sql = ` SELECT DISTINCT s.id, s.name, s.version, s.file_path, s.content_hash, s.namespace, s.status, s.priority, s.tags, asm.is_required, asm.priority as mapping_priority FROM skills s INNER JOIN agent_skill_mappings asm ON s.id = asm.skill_id WHERE asm.agent_type = ? AND s.status = 'active' `; // Add phase filter if provided if (phase) { sql += ` AND (asm.phase IS NULL OR asm.phase = ?)`; params.push(phase); } // Add context keyword filter if provided if (contextKeywords && contextKeywords.length > 0) { const keywordConditions = contextKeywords.map(()=>`(asm.context_keywords LIKE ? OR s.tags LIKE ?)`).join(' OR '); sql += ` AND (${keywordConditions})`; contextKeywords.forEach((keyword)=>{ params.push(`%${keyword}%`, `%${keyword}%`); }); } sql += ` ORDER BY asm.is_required DESC, COALESCE(asm.priority, s.priority) DESC, s.name ASC `; return { sql, params }; } /** * Get bootstrap skills that should always be loaded */ static getBootstrapSkills() { const placeholders = BOOTSTRAP_SKILL_IDS.map(()=>'?').join(','); return { sql: ` SELECT id, name, version, file_path, content_hash, namespace, status, priority, tags FROM skills WHERE id IN (${placeholders}) AND status = 'active' ORDER BY priority DESC `, params: [ ...BOOTSTRAP_SKILL_IDS ] }; } /** * Get skill by ID */ static getSkillById(skillId) { return { sql: ` SELECT id, name, version, file_path, content_hash, namespace, status, priority, tags, created_at, updated_at FROM skills WHERE id = ? `, params: [ skillId ] }; } /** * Validate skill content hash */ static validateContentHash(skillId, expectedHash) { return { sql: ` SELECT id, content_hash, updated_at FROM skills WHERE id = ? AND content_hash = ? `, params: [ skillId, expectedHash ] }; } /** * Log skill usage for analytics */ static insertSkillUsage(usage) { return { sql: ` INSERT INTO skill_usage_log ( id, skill_id, agent_id, agent_type, loaded_at, execution_time_ms, confidence_impact ) VALUES (?, ?, ?, ?, datetime('now'), ?, ?) `, params: [ `usage-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, usage.skill_id, usage.agent_id, usage.agent_type, usage.execution_time_ms, usage.confidence_impact ?? null ] }; } /** * Get skill effectiveness analytics */ static getSkillEffectiveness(skillId) { const params = []; let sql = ` SELECT s.id, s.name, COUNT(sul.id) as usage_count, AVG(sul.execution_time_ms) as avg_load_time_ms, AVG(sul.confidence_impact) as avg_confidence_impact, MAX(sul.loaded_at) as last_used_at FROM skills s LEFT JOIN skill_usage_log sul ON s.id = sul.skill_id `; if (skillId) { sql += ` WHERE s.id = ?`; params.push(skillId); } sql += ` GROUP BY s.id, s.name ORDER BY usage_count DESC, avg_confidence_impact DESC `; return { sql, params }; } /** * Update skill content hash (for cache invalidation) */ static updateContentHash(skillId, newHash) { return { sql: ` UPDATE skills SET content_hash = ?, updated_at = datetime('now') WHERE id = ? `, params: [ newHash, skillId ] }; } /** * Get all active skills for caching */ static getAllActiveSkills() { return { sql: ` SELECT id, name, version, file_path, content_hash, namespace, status, priority, tags FROM skills WHERE status = 'active' ORDER BY priority DESC, name ASC `, params: [] }; } /** * Get skills by tags */ static getSkillsByTags(tags) { const conditions = tags.map(()=>'s.tags LIKE ?').join(' OR '); const params = tags.map((tag)=>`%${tag}%`); return { sql: ` SELECT id, name, version, file_path, content_hash, namespace, status, priority, tags FROM skills s WHERE status = 'active' AND (${conditions}) ORDER BY priority DESC, name ASC `, params }; } /** * Create skills table schema */ static createSkillsTableSchema() { return ` CREATE TABLE IF NOT EXISTS skills ( id TEXT PRIMARY KEY, name TEXT NOT NULL, version TEXT NOT NULL, file_path TEXT NOT NULL, content_hash TEXT NOT NULL, namespace TEXT NOT NULL DEFAULT 'cfn', status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'deprecated', 'experimental')), priority INTEGER NOT NULL DEFAULT 5 CHECK(priority BETWEEN 1 AND 10), tags TEXT, created_at TEXT NOT NULL DEFAULT (datetime('now')), updated_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_skills_status ON skills(status); CREATE INDEX IF NOT EXISTS idx_skills_namespace ON skills(namespace); CREATE INDEX IF NOT EXISTS idx_skills_priority ON skills(priority); CREATE INDEX IF NOT EXISTS idx_skills_tags ON skills(tags); `; } /** * Create agent_skill_mappings table schema */ static createMappingsTableSchema() { return ` CREATE TABLE IF NOT EXISTS agent_skill_mappings ( agent_type TEXT NOT NULL, skill_id TEXT NOT NULL, is_required BOOLEAN NOT NULL DEFAULT 0, priority INTEGER NOT NULL DEFAULT 5 CHECK(priority BETWEEN 1 AND 10), phase TEXT, context_keywords TEXT, PRIMARY KEY (agent_type, skill_id), FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_asm_agent_type ON agent_skill_mappings(agent_type); CREATE INDEX IF NOT EXISTS idx_asm_skill_id ON agent_skill_mappings(skill_id); CREATE INDEX IF NOT EXISTS idx_asm_phase ON agent_skill_mappings(phase); `; } /** * Create skill_usage_log table schema */ static createUsageLogTableSchema() { return ` CREATE TABLE IF NOT EXISTS skill_usage_log ( id TEXT PRIMARY KEY, skill_id TEXT NOT NULL, agent_id TEXT NOT NULL, agent_type TEXT NOT NULL, loaded_at TEXT NOT NULL, execution_time_ms INTEGER NOT NULL, confidence_impact REAL, FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_sul_skill_id ON skill_usage_log(skill_id); CREATE INDEX IF NOT EXISTS idx_sul_agent_type ON skill_usage_log(agent_type); CREATE INDEX IF NOT EXISTS idx_sul_loaded_at ON skill_usage_log(loaded_at); `; } /** * Create cache_invalidations table schema * * Tracks cache invalidation events for monitoring and analytics. */ static createCacheInvalidationsTableSchema() { return ` CREATE TABLE IF NOT EXISTS cache_invalidations ( id TEXT PRIMARY KEY, skill_id TEXT NOT NULL, invalidated_at TEXT NOT NULL DEFAULT (datetime('now')), reason TEXT NOT NULL, old_hash TEXT, new_hash TEXT, FOREIGN KEY (skill_id) REFERENCES skills(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_ci_skill_id ON cache_invalidations(skill_id); CREATE INDEX IF NOT EXISTS idx_ci_timestamp ON cache_invalidations(invalidated_at); `; } /** * Create skill_loader_metrics table schema * * Tracks skill loader performance metrics for monitoring. */ static createSkillLoaderMetricsTableSchema() { return ` CREATE TABLE IF NOT EXISTS skill_loader_metrics ( id TEXT PRIMARY KEY, agent_type TEXT NOT NULL, load_time_ms INTEGER NOT NULL, cache_hit INTEGER NOT NULL DEFAULT 0, cache_miss INTEGER NOT NULL DEFAULT 0, cache_invalidation INTEGER NOT NULL DEFAULT 0, skills_loaded INTEGER NOT NULL, timestamp TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE INDEX IF NOT EXISTS idx_slm_agent_type ON skill_loader_metrics(agent_type); CREATE INDEX IF NOT EXISTS idx_slm_timestamp ON skill_loader_metrics(timestamp); `; } /** * Get cache invalidation events (last N hours) * * @param hours - Number of hours to look back (default: 24) * @returns SQL query for cache invalidations */ static getCacheInvalidations(hours = 24) { return { sql: ` SELECT ci.skill_id, s.name as skill_name, ci.invalidated_at, ci.reason, ci.old_hash, ci.new_hash FROM cache_invalidations ci LEFT JOIN skills s ON ci.skill_id = s.id WHERE ci.invalidated_at > datetime('now', '-' || ? || ' hours') ORDER BY ci.invalidated_at DESC `, params: [ hours ] }; } /** * Get cache invalidation count (last N hours) * * @param hours - Number of hours to look back (default: 24) * @returns SQL query for invalidation count */ static getCacheInvalidationCount(hours = 24) { return { sql: ` SELECT COUNT(*) as invalidations FROM cache_invalidations WHERE invalidated_at > datetime('now', '-' || ? || ' hours') `, params: [ hours ] }; } /** * Get skills with frequent updates (top N) * * Identifies skills that are frequently modified based on invalidation events. * * @param limit - Number of top skills to return (default: 10) * @param hours - Number of hours to look back (default: 168 = 7 days) * @returns SQL query for frequently updated skills */ static getFrequentlyUpdatedSkills(limit = 10, hours = 168) { return { sql: ` SELECT ci.skill_id, s.name as skill_name, COUNT(*) as update_count, MAX(ci.invalidated_at) as last_update FROM cache_invalidations ci LEFT JOIN skills s ON ci.skill_id = s.id WHERE ci.invalidated_at > datetime('now', '-' || ? || ' hours') GROUP BY ci.skill_id, s.name ORDER BY update_count DESC LIMIT ? `, params: [ hours, limit ] }; } /** * Get cache hit/miss/invalidation ratio * * Calculates cache performance metrics over the last N hours. * * @param hours - Number of hours to look back (default: 24) * @returns SQL query for cache performance metrics */ static getCachePerformanceMetrics(hours = 24) { return { sql: ` SELECT SUM(cache_hit) as total_hits, SUM(cache_miss) as total_misses, SUM(cache_invalidation) as total_invalidations, ROUND(100.0 * SUM(cache_hit) / NULLIF(SUM(cache_hit) + SUM(cache_miss), 0), 2) as hit_rate, ROUND(AVG(load_time_ms), 2) as avg_load_time_ms, COUNT(*) as total_loads FROM skill_loader_metrics WHERE timestamp > datetime('now', '-' || ? || ' hours') `, params: [ hours ] }; } /** * Get cache performance by agent type * * Breaks down cache performance metrics per agent type. * * @param hours - Number of hours to look back (default: 24) * @returns SQL query for agent-specific cache metrics */ static getCachePerformanceByAgentType(hours = 24) { return { sql: ` SELECT agent_type, SUM(cache_hit) as hits, SUM(cache_miss) as misses, SUM(cache_invalidation) as invalidations, ROUND(100.0 * SUM(cache_hit) / NULLIF(SUM(cache_hit) + SUM(cache_miss), 0), 2) as hit_rate, ROUND(AVG(load_time_ms), 2) as avg_load_time_ms, COUNT(*) as load_count FROM skill_loader_metrics WHERE timestamp > datetime('now', '-' || ? || ' hours') GROUP BY agent_type ORDER BY load_count DESC `, params: [ hours ] }; } /** * Record cache invalidation event * * @param skillId - Skill ID that was invalidated * @param reason - Reason for invalidation * @param oldHash - Old content hash * @param newHash - New content hash * @returns SQL query for inserting invalidation record */ static recordCacheInvalidation(skillId, reason, oldHash, newHash) { return { sql: ` INSERT INTO cache_invalidations (id, skill_id, reason, old_hash, new_hash) VALUES (?, ?, ?, ?, ?) `, params: [ `ci-${skillId}-${Date.now()}`, skillId, reason, oldHash || null, newHash || null ] }; } /** * Record skill loader metrics * * @param agentType - Agent type that loaded skills * @param loadTimeMs - Load time in milliseconds * @param cacheHit - Cache hit count (0 or 1) * @param cacheMiss - Cache miss count (0 or 1) * @param cacheInvalidation - Cache invalidation count * @param skillsLoaded - Number of skills loaded * @returns SQL query for inserting metrics record */ static recordSkillLoaderMetrics(agentType, loadTimeMs, cacheHit, cacheMiss, cacheInvalidation, skillsLoaded) { return { sql: ` INSERT INTO skill_loader_metrics (id, agent_type, load_time_ms, cache_hit, cache_miss, cache_invalidation, skills_loaded) VALUES (?, ?, ?, ?, ?, ?, ?) `, params: [ `slm-${agentType}-${Date.now()}`, agentType, loadTimeMs, cacheHit, cacheMiss, cacheInvalidation, skillsLoaded ] }; } } //# sourceMappingURL=skills-query.js.map