Skip to main content
Glama
context-router.js•13.6 kB
/** * Context Router for intelligent context switching * Routes decisions and actions based on changing development context */ export class ContextRouter { constructor() { // Define context types and their characteristics this.contextTypes = { exploration: { characteristics: ['searching', 'reading', 'analyzing'], tools: ['search_files', 'search_in_files', 'find_similar_files'], pace: 'measured', }, implementation: { characteristics: ['writing', 'modifying', 'creating'], tools: ['update_file', 'create_file', 'format_code'], pace: 'focused', }, validation: { characteristics: ['testing', 'checking', 'verifying'], tools: ['check_code_practices', 'check_production_readiness', 'scan_security_vulnerabilities'], pace: 'thorough', }, debugging: { characteristics: ['fixing', 'investigating', 'diagnosing'], tools: ['search_in_files', 'check_code_practices', 'read_file'], pace: 'investigative', }, review: { characteristics: ['reviewing', 'auditing', 'assessing'], tools: ['check_code_practices', 'check_accessibility', 'check_production_readiness'], pace: 'comprehensive', }, }; // Context switching rules this.switchingRules = { 'exploration->implementation': { conditions: ['sufficient_understanding', 'clear_approach'], preparation: ['create_checkpoint', 'document_findings'], }, 'implementation->validation': { conditions: ['changes_complete', 'no_syntax_errors'], preparation: ['save_progress', 'prepare_test_environment'], }, 'validation->debugging': { conditions: ['test_failures', 'unexpected_behavior'], preparation: ['capture_error_context', 'identify_failure_points'], }, 'debugging->implementation': { conditions: ['root_cause_identified', 'fix_approach_clear'], preparation: ['document_fix_approach', 'create_test_case'], }, }; // Current context tracking this.currentContext = null; this.contextHistory = []; this.contextMetrics = new Map(); } /** * Detect current context from activity */ detectContext(activity) { const indicators = { exploration: 0, implementation: 0, validation: 0, debugging: 0, review: 0, }; // Analyze recent tools used const recentTools = activity.recentTools || []; for (const tool of recentTools) { for (const [contextType, config] of Object.entries(this.contextTypes)) { if (config.tools.includes(tool)) { indicators[contextType]++; } } } // Analyze actions const actions = activity.actions || []; for (const action of actions) { for (const [contextType, config] of Object.entries(this.contextTypes)) { if (config.characteristics.some(char => action.includes(char))) { indicators[contextType] += 0.5; } } } // Find dominant context let maxScore = 0; let detectedContext = 'exploration'; for (const [context, score] of Object.entries(indicators)) { if (score > maxScore) { maxScore = score; detectedContext = context; } } return { context: detectedContext, confidence: maxScore > 0 ? Math.min(maxScore / 5, 1) : 0.3, indicators, }; } /** * Route to appropriate context */ async routeContext(currentActivity, targetGoal) { // Detect current context const detection = this.detectContext(currentActivity); const fromContext = this.currentContext || detection.context; // Determine target context const toContext = this.determineTargetContext(targetGoal, currentActivity); // Check if context switch is needed if (fromContext === toContext) { return { action: 'continue', context: fromContext, reason: 'Already in appropriate context', }; } // Validate context switch const validation = await this.validateContextSwitch(fromContext, toContext, currentActivity); if (!validation.allowed) { return { action: 'block', context: fromContext, reason: validation.reason, suggestions: validation.suggestions, }; } // Prepare for context switch const preparation = await this.prepareContextSwitch(fromContext, toContext); // Execute context switch const result = await this.executeContextSwitch(fromContext, toContext, preparation); return result; } /** * Determine target context based on goal */ determineTargetContext(goal, activity) { // Simple keyword matching for now const goalText = goal.toLowerCase(); if (goalText.includes('implement') || goalText.includes('create') || goalText.includes('add')) { return 'implementation'; } if (goalText.includes('test') || goalText.includes('validate') || goalText.includes('check')) { return 'validation'; } if (goalText.includes('fix') || goalText.includes('debug') || goalText.includes('investigate')) { return 'debugging'; } if (goalText.includes('review') || goalText.includes('audit') || goalText.includes('assess')) { return 'review'; } return 'exploration'; } /** * Validate context switch */ async validateContextSwitch(from, to, activity) { const ruleKey = `${from}->${to}`; const rules = this.switchingRules[ruleKey]; if (!rules) { // No specific rules, allow switch return { allowed: true }; } // Check conditions const unmetConditions = []; for (const condition of rules.conditions) { if (!this.checkCondition(condition, activity)) { unmetConditions.push(condition); } } if (unmetConditions.length > 0) { return { allowed: false, reason: `Cannot switch from ${from} to ${to}: missing ${unmetConditions.join(', ')}`, suggestions: this.getSuggestions(unmetConditions), }; } return { allowed: true }; } /** * Check specific condition */ checkCondition(condition, activity) { switch (condition) { case 'sufficient_understanding': return activity.filesAnalyzed >= 3 || activity.searchesPerformed >= 2; case 'clear_approach': return activity.plan && activity.plan.tasks?.length > 0; case 'changes_complete': return activity.pendingChanges === 0; case 'no_syntax_errors': return !activity.syntaxErrors || activity.syntaxErrors === 0; case 'test_failures': return activity.testFailures > 0; case 'root_cause_identified': return activity.rootCause !== null; default: return true; } } /** * Get suggestions for unmet conditions */ getSuggestions(conditions) { const suggestions = []; for (const condition of conditions) { switch (condition) { case 'sufficient_understanding': suggestions.push('Analyze more files to understand the codebase better'); suggestions.push('Use search tools to find relevant code patterns'); break; case 'clear_approach': suggestions.push('Create a plan with specific tasks before implementing'); break; case 'changes_complete': suggestions.push('Complete pending changes before validation'); break; case 'no_syntax_errors': suggestions.push('Fix syntax errors before proceeding'); break; } } return suggestions; } /** * Prepare for context switch */ async prepareContextSwitch(from, to) { const ruleKey = `${from}->${to}`; const rules = this.switchingRules[ruleKey]; const preparations = []; if (rules?.preparation) { for (const prep of rules.preparation) { const result = await this.executePreparation(prep); preparations.push({ action: prep, result, }); } } // Update metrics this.updateContextMetrics(from); return preparations; } /** * Execute preparation action */ async executePreparation(action) { switch (action) { case 'create_checkpoint': return { checkpoint: Date.now() }; case 'document_findings': return { documented: true }; case 'save_progress': return { saved: true }; case 'prepare_test_environment': return { testEnvReady: true }; case 'capture_error_context': return { errorContext: 'captured' }; case 'identify_failure_points': return { failurePoints: [] }; default: return { executed: action }; } } /** * Execute context switch */ async executeContextSwitch(from, to, preparation) { // Record in history this.contextHistory.push({ from, to, timestamp: Date.now(), preparation, }); // Update current context this.currentContext = to; // Get recommended actions for new context const recommendations = this.getContextRecommendations(to); return { action: 'switch', previousContext: from, context: to, preparations: preparation, recommendations, pace: this.contextTypes[to].pace, tools: this.contextTypes[to].tools, }; } /** * Get recommendations for context */ getContextRecommendations(context) { const recommendations = { exploration: [ 'Start with broad searches to understand the codebase', 'Look for similar implementations for reference', 'Document findings as you explore', ], implementation: [ 'Focus on one change at a time', 'Test incrementally as you implement', 'Follow existing code patterns', ], validation: [ 'Run comprehensive tests', 'Check edge cases', 'Validate against requirements', ], debugging: [ 'Isolate the problem area', 'Use systematic debugging approach', 'Document the root cause when found', ], review: [ 'Check against coding standards', 'Verify security implications', 'Assess performance impact', ], }; return recommendations[context] || []; } /** * Update context metrics */ updateContextMetrics(context) { const metrics = this.contextMetrics.get(context) || { timeSpent: 0, switches: 0, lastActive: null, }; if (metrics.lastActive) { metrics.timeSpent += Date.now() - metrics.lastActive; } metrics.switches++; metrics.lastActive = Date.now(); this.contextMetrics.set(context, metrics); } /** * Get context insights */ getContextInsights() { const insights = { currentContext: this.currentContext, timeDistribution: {}, switchPatterns: [], recommendations: [], }; // Calculate time distribution let totalTime = 0; this.contextMetrics.forEach((metrics, context) => { totalTime += metrics.timeSpent; }); this.contextMetrics.forEach((metrics, context) => { insights.timeDistribution[context] = { percentage: (metrics.timeSpent / totalTime) * 100, duration: metrics.timeSpent, switches: metrics.switches, }; }); // Analyze switch patterns const switchCounts = {}; for (let i = 1; i < this.contextHistory.length; i++) { const pattern = `${this.contextHistory[i-1].to}->${this.contextHistory[i].to}`; switchCounts[pattern] = (switchCounts[pattern] || 0) + 1; } insights.switchPatterns = Object.entries(switchCounts) .sort(([, a], [, b]) => b - a) .slice(0, 5) .map(([pattern, count]) => ({ pattern, count })); // Generate recommendations if (insights.timeDistribution.exploration?.percentage > 50) { insights.recommendations.push('Consider moving to implementation phase'); } if (insights.timeDistribution.debugging?.percentage > 40) { insights.recommendations.push('High debugging time - consider more thorough testing'); } return insights; } /** * Predict next context */ predictNextContext(currentActivity) { if (this.contextHistory.length < 2) { return { context: 'exploration', confidence: 0.3 }; } // Look for patterns in history const currentContext = this.currentContext; const patterns = {}; for (let i = 0; i < this.contextHistory.length - 1; i++) { if (this.contextHistory[i].to === currentContext) { const next = this.contextHistory[i + 1].to; patterns[next] = (patterns[next] || 0) + 1; } } // Find most likely next context let maxCount = 0; let predictedContext = currentContext; for (const [context, count] of Object.entries(patterns)) { if (count > maxCount) { maxCount = count; predictedContext = context; } } const confidence = maxCount > 0 ? Math.min(maxCount / 5, 0.9) : 0.3; return { context: predictedContext, confidence, basedOn: `${maxCount} historical transitions`, }; } /** * Reset context router */ reset() { this.currentContext = null; this.contextHistory = []; this.contextMetrics.clear(); } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/moikas-code/moidvk'

If you have feedback or need assistance with the MCP directory API, please join our Discord server