Commit all workspace changes from current session
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
#!/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);
|
||||
}
|
||||
Reference in New Issue
Block a user