#!/usr/bin/env node /** * Workflow Router - Intent Classification and Routing * * Before responding, classify intent and route to fixed workflow template */ const fs = require('fs'); const path = require('path'); class WorkflowRouter { constructor() { this.workflows = new Map(); this.loadWorkflows(); } /** * Load all workflow definitions from workflows/ directory */ loadWorkflows() { const workflowsDir = path.join(__dirname, '..', 'workflows'); const workflowFiles = [ 'coding.md', 'debug.md', 'deploy.md', 'audit.md', 'planning.md' ]; for (const file of workflowFiles) { const workflowName = path.basename(file, '.md'); const filePath = path.join(workflowsDir, file); try { const content = fs.readFileSync(filePath, 'utf8'); this.workflows.set(workflowName, this.parseWorkflow(content)); } catch (err) { console.warn(`Workflow ${file} not found, using defaults`); this.workflows.set(workflowName, this.getDefaultWorkflow(workflowName)); } } } /** * Parse workflow markdown into structured object */ parseWorkflow(content) { const workflow = { name: '', triggers: [], outputFormat: '', toolRules: [], constraints: [], requiredSections: [] }; // Extract name from first header const nameMatch = content.match(/^#\s+Workflow:\s*(.+)$/m); if (nameMatch) workflow.name = nameMatch[1].trim(); // Extract triggers const triggersMatch = content.match(/\*\*Triggers:\*\*\s*(.+)/); if (triggersMatch) { workflow.triggers = triggersMatch[1].split(',').map(t => t.trim().toLowerCase()); } // Extract output format (code block after "Fixed Output Format") const formatMatch = content.match(/## Fixed Output Format\s*```markdown\s*([\s\S]*?)```/); if (formatMatch) workflow.outputFormat = formatMatch[1].trim(); // Extract tool rules const rulesSection = content.match(/## Tool Access Rules\s*([\s\S]*?)(?=##|$)/); if (rulesSection) { workflow.toolRules = rulesSection[1] .split('\n') .filter(line => /^\d+\./.test(line)) .map(line => line.replace(/^\d+\.\s*/, '').trim()); } // Extract constraints const constraintsSection = content.match(/## Constraints\s*([\s\S]*?)(?=##|$)/); if (constraintsSection) { workflow.constraints = constraintsSection[1] .split('\n') .filter(line => /^(NO|YES):/.test(line)) .map(line => line.trim()); } // Extract required sections from format template const sectionMatches = workflow.outputFormat.match(/^##\s+(.+)$/gm); if (sectionMatches) { workflow.requiredSections = sectionMatches.map(s => s.replace(/^##\s*/, '').trim()); } return workflow; } /** * Classify user intent and return matching workflow */ classifyIntent(userInput) { const input = userInput.toLowerCase(); // Intent patterns with confidence scoring const intentPatterns = { 'coding': { patterns: [ /\b(code|implement|build|create|write|develop|feature|fix bug|add\s+\w+\s+to)\b/, /\b(refactor|optimize|improve|clean up)\b/, /\b(pull request|pr|commit|merge|branch)\b/, /\b(api|endpoint|route|handler|controller|model)\b/ ], weight: 1.0 }, 'debug': { patterns: [ /\b(debug|fix|broken|error|bug|issue|problem|crash|fails?|not working)\b/, /\b(troubleshoot|diagnose|investigate|trace|root cause)\b/, /\b(exception|stack trace|log|error message)\b/ ], weight: 1.0 }, 'deploy': { patterns: [ /\b(deploy|release|push to|go live|production|staging)\b/, /\b(docker|container|build|image|registry)\b/, /\b(rollback|revert|restore|emergency)\b/, /\b(install|upgrade|update|migrate)\b/ ], weight: 1.0 }, 'audit': { patterns: [ /\b(audit|review|assess|inventory|check|scan|inspect)\b/, /\b(security|vulnerability|compliance|policy)\b/, /\b(status|health|state|report|summary)\b/, /\b(workspace|files|structure|organization)\b/ ], weight: 1.0 }, 'planning': { patterns: [ /\b(plan|design|architecture|strategy|roadmap|timeline)\b/, /\b(requirement|spec|proposal|approach)\b/, /\b(breakdown|estimate|scope|prioritize)\b/ ], weight: 0.8 } }; let bestMatch = null; let bestScore = 0; for (const [workflowName, config] of Object.entries(intentPatterns)) { let score = 0; let matches = 0; for (const pattern of config.patterns) { if (pattern.test(input)) { matches++; score += config.weight; } } // Boost score for multiple matches if (matches > 1) score *= 1.2; if (score > bestScore) { bestScore = score; bestMatch = workflowName; } } // Default to coding if no strong match if (bestScore < 0.5 || !bestMatch) { bestMatch = 'coding'; bestScore = 0.3; } return { workflow: bestMatch, confidence: Math.min(bestScore / 2, 1.0), // Normalize allScores: this.getAllScores(intentPatterns, input) }; } /** * Get confidence scores for all workflows */ getAllScores(patterns, input) { const scores = {}; for (const [name, config] of Object.entries(patterns)) { let score = 0; for (const pattern of config.patterns) { if (pattern.test(input)) score += config.weight; } scores[name] = score; } return scores; } /** * Get workflow by name */ getWorkflow(name) { return this.workflows.get(name) || this.getDefaultWorkflow(name); } /** * Get default workflow template */ getDefaultWorkflow(name) { return { name: name || 'default', triggers: ['general'], outputFormat: '## Summary\n[Response]\n\n## Details\n[Details]', toolRules: ['Delegate when possible', 'Verify before reporting'], constraints: ['NO: Assumptions without verification', 'YES: Clear status updates'], requiredSections: ['Summary', 'Details'] }; } /** * Build context-aware system prompt for the classified workflow */ buildSystemPrompt(workflowName, context = {}) { const workflow = this.getWorkflow(workflowName); const parts = [ `You are in ${workflow.name} workflow mode.`, '', '## Required Output Sections', ...workflow.requiredSections.map(s => `- ${s}`), '', '## Tool Access Rules', ...workflow.toolRules.map((rule, i) => `${i + 1}. ${rule}`), '', '## Constraints', ...workflow.constraints, '', '## Format Template', '```markdown', workflow.outputFormat, '```' ]; if (context.project) { parts.push('', `## Current Project: ${context.project}`); } return parts.join('\n'); } /** * Main entry point: classify and route */ route(userInput, context = {}) { const classification = this.classifyIntent(userInput); const workflow = this.getWorkflow(classification.workflow); const systemPrompt = this.buildSystemPrompt(classification.workflow, context); return { workflow: classification.workflow, confidence: classification.confidence, workflowConfig: workflow, systemPrompt: systemPrompt, context: context }; } } // Export for use module.exports = WorkflowRouter; // CLI usage if (require.main === module) { const router = new WorkflowRouter(); const userInput = process.argv[2] || 'implement a new feature'; const result = router.route(userInput, { project: process.env.CURRENT_PROJECT }); console.log('=== Workflow Classification ==='); console.log(`Workflow: ${result.workflow}`); console.log(`Confidence: ${(result.confidence * 100).toFixed(1)}%`); console.log('\n=== System Prompt ==='); console.log(result.systemPrompt); }