claude-flow
Version:
Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration
813 lines • 35.7 kB
JavaScript
/**
* V3 CLI Route Command
* Intelligent task-to-agent routing using Q-Learning
*
* Features:
* - Q-Learning based agent selection
* - Semantic task understanding
* - Confidence scoring
* - Learning from feedback
*
* Created with love by ruv.io
*/
import { output } from '../output.js';
import { createQLearningRouter, isRuvectorAvailable, } from '../ruvector/index.js';
/**
* Available agent types for routing
*/
const AGENT_TYPES = [
{ id: 'coder', name: 'Coder', description: 'Implements features and writes code', capabilities: ['coding', 'implementation', 'refactoring'], priority: 1 },
{ id: 'tester', name: 'Tester', description: 'Creates tests and validates functionality', capabilities: ['testing', 'validation', 'quality'], priority: 2 },
{ id: 'reviewer', name: 'Reviewer', description: 'Reviews code quality and security', capabilities: ['review', 'security', 'best-practices'], priority: 3 },
{ id: 'architect', name: 'Architect', description: 'Designs system architecture', capabilities: ['design', 'architecture', 'planning'], priority: 4 },
{ id: 'researcher', name: 'Researcher', description: 'Researches requirements and patterns', capabilities: ['research', 'analysis', 'documentation'], priority: 5 },
{ id: 'optimizer', name: 'Optimizer', description: 'Optimizes performance and efficiency', capabilities: ['optimization', 'performance', 'profiling'], priority: 6 },
{ id: 'debugger', name: 'Debugger', description: 'Debugs issues and fixes bugs', capabilities: ['debugging', 'troubleshooting', 'fixing'], priority: 7 },
{ id: 'documenter', name: 'Documenter', description: 'Creates and updates documentation', capabilities: ['documentation', 'writing', 'explaining'], priority: 8 },
];
// ============================================================================
// Router Singleton
// ============================================================================
let routerInstance = null;
let routerInitialized = false;
/**
* Get or create the router instance
*/
async function getRouter() {
if (!routerInstance) {
routerInstance = createQLearningRouter();
}
if (!routerInitialized) {
await routerInstance.initialize();
routerInitialized = true;
}
return routerInstance;
}
/**
* Get agent type by route name
*/
function getAgentType(route) {
return AGENT_TYPES.find(a => a.id === route);
}
// ============================================================================
// Route Subcommand
// ============================================================================
const routeTaskCommand = {
name: 'task',
description: 'Route a task to the optimal agent using Q-Learning',
options: [
{
name: 'q-learning',
short: 'q',
description: 'Use Q-Learning for agent selection (default: true)',
type: 'boolean',
default: true,
},
{
name: 'agent',
short: 'a',
description: 'Force specific agent (bypasses Q-Learning)',
type: 'string',
},
{
name: 'explore',
short: 'e',
description: 'Enable exploration (random selection chance)',
type: 'boolean',
default: true,
},
{
name: 'json',
short: 'j',
description: 'Output in JSON format',
type: 'boolean',
default: false,
},
],
examples: [
{ command: 'claude-flow route task "implement authentication"', description: 'Route task to best agent' },
{ command: 'claude-flow route task "write unit tests" --q-learning', description: 'Use Q-Learning routing' },
{ command: 'claude-flow route task "review code" --agent reviewer', description: 'Force specific agent' },
],
action: async (ctx) => {
const taskDescription = ctx.args[0];
const forceAgent = ctx.flags.agent;
const useExploration = ctx.flags.explore;
const jsonOutput = ctx.flags.json;
if (!taskDescription) {
output.printError('Task description is required');
output.writeln(output.dim('Usage: claude-flow route task "task description"'));
return { success: false, exitCode: 1 };
}
const spinner = output.createSpinner({ text: 'Analyzing task...', spinner: 'dots' });
spinner.start();
try {
if (forceAgent) {
// Bypass Q-Learning, use specified agent
const agent = getAgentType(forceAgent) ||
AGENT_TYPES.find(a => a.name.toLowerCase() === forceAgent.toLowerCase());
if (!agent) {
spinner.fail(`Agent "${forceAgent}" not found`);
output.writeln();
output.writeln('Available agents:');
output.printList(AGENT_TYPES.map(a => `${output.highlight(a.id)} - ${a.description}`));
return { success: false, exitCode: 1 };
}
spinner.succeed(`Routed to ${agent.name}`);
if (jsonOutput) {
output.printJson({
task: taskDescription,
agentId: agent.id,
agentName: agent.name,
confidence: 1.0,
method: 'forced',
});
}
else {
output.writeln();
output.printBox([
`Task: ${taskDescription}`,
`Agent: ${output.highlight(agent.name)} (${agent.id})`,
`Confidence: ${output.success('100%')} (forced)`,
`Description: ${agent.description}`,
].join('\n'), 'Routing Result');
}
return { success: true, data: { agentId: agent.id, agentName: agent.name } };
}
// Use Q-Learning routing
const router = await getRouter();
const result = router.route(taskDescription, useExploration);
const agent = getAgentType(result.route) || AGENT_TYPES[0];
spinner.succeed(`Routed to ${agent.name}`);
if (jsonOutput) {
output.printJson({
task: taskDescription,
agentId: result.route,
agentName: agent.name,
confidence: result.confidence,
qValues: result.qValues,
explored: result.explored,
alternatives: result.alternatives.map(a => ({
agentId: a.route,
agentName: getAgentType(a.route)?.name || a.route,
score: a.score,
})),
});
}
else {
output.writeln();
const confidence = result.confidence ?? 0;
// Use bound methods to preserve `this` context when calling output methods
const confidenceColor = confidence >= 0.7
? (text) => output.success(text)
: confidence >= 0.4
? (text) => output.warning(text)
: (text) => output.error(text);
const qValues = result.qValues || [0];
const maxQValue = Math.max(...qValues);
const capabilities = agent.capabilities || [];
const alternatives = result.alternatives || [];
output.printBox([
`Task: ${taskDescription}`,
``,
`Agent: ${output.highlight(agent.name)} (${result.route})`,
`Confidence: ${confidenceColor(`${(confidence * 100).toFixed(1)}%`)}`,
`Q-Value: ${maxQValue.toFixed(3)}`,
`Exploration: ${result.explored ? output.warning('Yes') : 'No'}`,
``,
`Description: ${agent.description}`,
`Capabilities: ${capabilities.join(', ')}`,
].join('\n'), 'Q-Learning Routing');
if (alternatives.length > 0) {
output.writeln();
output.writeln(output.bold('Alternatives:'));
output.printTable({
columns: [
{ key: 'agent', header: 'Agent', width: 20 },
{ key: 'score', header: 'Score', width: 12, align: 'right' },
],
data: alternatives.map(a => ({
agent: getAgentType(a.route)?.name || a.route,
score: (a.score ?? 0).toFixed(3),
})),
});
}
}
return { success: true, data: { agentId: result.route, result } };
}
catch (error) {
spinner.fail('Routing failed');
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// List Agents Subcommand
// ============================================================================
const listAgentsCommand = {
name: 'list-agents',
aliases: ['agents', 'ls'],
description: 'List all available agent types for routing',
options: [
{
name: 'json',
short: 'j',
description: 'Output in JSON format',
type: 'boolean',
default: false,
},
],
examples: [
{ command: 'claude-flow route list-agents', description: 'List all agents' },
{ command: 'claude-flow route agents --json', description: 'List agents as JSON' },
],
action: async (ctx) => {
const jsonOutput = ctx.flags.json;
try {
if (jsonOutput) {
output.printJson(AGENT_TYPES);
}
else {
output.writeln();
output.writeln(output.bold('Available Agent Types'));
output.writeln(output.dim('Ordered by priority (highest first)'));
output.writeln();
output.printTable({
columns: [
{ key: 'id', header: 'ID', width: 15 },
{ key: 'name', header: 'Name', width: 15 },
{ key: 'priority', header: 'Priority', width: 10, align: 'right' },
{ key: 'description', header: 'Description', width: 45 },
],
data: AGENT_TYPES.map(a => ({
id: output.highlight(a.id),
name: a.name,
priority: String(a.priority),
description: a.description,
})),
});
output.writeln();
output.writeln(output.dim(`Total: ${AGENT_TYPES.length} agent types`));
}
return { success: true, data: AGENT_TYPES };
}
catch (error) {
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// Stats Subcommand
// ============================================================================
const statsCommand = {
name: 'stats',
description: 'Show Q-Learning router statistics',
options: [
{
name: 'json',
short: 'j',
description: 'Output in JSON format',
type: 'boolean',
default: false,
},
],
examples: [
{ command: 'claude-flow route stats', description: 'Show routing statistics' },
],
action: async (ctx) => {
const jsonOutput = ctx.flags.json;
try {
const router = await getRouter();
const stats = router.getStats();
const ruvectorAvailable = await isRuvectorAvailable();
const ruvectorStatus = {
available: ruvectorAvailable,
wasmAccelerated: stats.useNative === 1,
backend: stats.useNative === 1 ? 'ruvector-native' : 'fallback',
};
if (jsonOutput) {
output.printJson({ stats, ruvector: ruvectorStatus });
}
else {
output.writeln();
output.writeln(output.bold('Q-Learning Router Statistics'));
output.writeln();
output.printTable({
columns: [
{ key: 'metric', header: 'Metric', width: 25 },
{ key: 'value', header: 'Value', width: 20, align: 'right' },
],
data: [
{ metric: 'Update Count', value: String(stats.updateCount) },
{ metric: 'Q-Table Size', value: String(stats.qTableSize) },
{ metric: 'Step Count', value: String(stats.stepCount) },
{ metric: 'Epsilon', value: stats.epsilon.toFixed(4) },
{ metric: 'Avg TD Error', value: stats.avgTDError.toFixed(4) },
{ metric: 'Native Backend', value: stats.useNative === 1 ? 'Yes' : 'No' },
],
});
output.writeln();
output.writeln(output.bold('RuVector Status'));
output.printList([
`Available: ${ruvectorStatus.available ? output.success('Yes') : output.warning('No (using fallback)')}`,
`WASM Accelerated: ${ruvectorStatus.wasmAccelerated ? output.success('Yes') : 'No'}`,
`Backend: ${ruvectorStatus.backend}`,
]);
}
return { success: true, data: { stats, ruvector: ruvectorStatus } };
}
catch (error) {
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// Feedback Subcommand
// ============================================================================
const feedbackCommand = {
name: 'feedback',
description: 'Provide feedback on a routing decision',
options: [
{
name: 'task',
short: 't',
description: 'Task description (context for learning)',
type: 'string',
required: true,
},
{
name: 'agent',
short: 'a',
description: 'Agent that was used',
type: 'string',
required: true,
},
{
name: 'reward',
short: 'r',
description: 'Reward value (-1 to 1, where 1 is best)',
type: 'number',
default: 0.8,
},
{
name: 'next-task',
short: 'n',
description: 'Next task description (for multi-step learning)',
type: 'string',
},
],
examples: [
{ command: 'claude-flow route feedback -t "implement auth" -a coder -r 0.9', description: 'Positive feedback' },
{ command: 'claude-flow route feedback -t "write tests" -a tester -r -0.5', description: 'Negative feedback' },
],
action: async (ctx) => {
const taskDescription = ctx.flags.task;
const agentId = ctx.flags.agent;
const reward = ctx.flags.reward;
const nextTask = ctx.flags['next-task'];
if (!taskDescription || !agentId) {
output.printError('Task description and agent are required');
return { success: false, exitCode: 1 };
}
// Validate agent
const agent = getAgentType(agentId);
if (!agent) {
output.printError(`Unknown agent: ${agentId}`);
output.writeln('Available agents:');
output.printList(AGENT_TYPES.map(a => a.id));
return { success: false, exitCode: 1 };
}
try {
const router = await getRouter();
const clampedReward = Math.max(-1, Math.min(1, reward));
const tdError = router.update(taskDescription, agentId, clampedReward, nextTask);
output.printSuccess(`Feedback recorded for agent "${agent.name}"`);
output.writeln();
output.printBox([
`Task: ${taskDescription}`,
`Agent: ${agent.name} (${agentId})`,
`Reward: ${clampedReward >= 0 ? output.success(clampedReward.toFixed(2)) : output.error(clampedReward.toFixed(2))}`,
`TD Error: ${Math.abs(tdError).toFixed(4)}`,
nextTask ? `Next Task: ${nextTask}` : '',
].filter(Boolean).join('\n'), 'Feedback Recorded');
return { success: true, data: { tdError } };
}
catch (error) {
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// Reset Subcommand
// ============================================================================
const resetCommand = {
name: 'reset',
description: 'Reset the Q-Learning router state',
options: [
{
name: 'force',
short: 'f',
description: 'Force reset without confirmation',
type: 'boolean',
default: false,
},
],
examples: [
{ command: 'claude-flow route reset', description: 'Reset router state' },
{ command: 'claude-flow route reset --force', description: 'Force reset' },
],
action: async (ctx) => {
const force = ctx.flags.force;
if (!force && ctx.interactive) {
output.printWarning('This will reset all learned Q-values and statistics.');
output.writeln(output.dim('Use --force to skip this confirmation.'));
return { success: false, exitCode: 1 };
}
try {
const router = await getRouter();
router.reset();
output.printSuccess('Q-Learning router state has been reset');
return { success: true };
}
catch (error) {
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// Export/Import Subcommands
// ============================================================================
const exportCommand = {
name: 'export',
description: 'Export Q-table for persistence',
options: [
{
name: 'file',
short: 'f',
description: 'Output file path (outputs to stdout if not specified)',
type: 'string',
},
],
examples: [
{ command: 'claude-flow route export', description: 'Export Q-table to stdout' },
{ command: 'claude-flow route export -f qtable.json', description: 'Export to file' },
],
action: async (ctx) => {
const filePath = ctx.flags.file;
try {
const router = await getRouter();
const data = router.export();
if (filePath) {
const fs = await import('node:fs/promises');
await fs.writeFile(filePath, JSON.stringify(data, null, 2));
output.printSuccess(`Q-table exported to ${filePath}`);
}
else {
output.printJson(data);
}
return { success: true, data };
}
catch (error) {
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
const importCommand = {
name: 'import',
description: 'Import Q-table from file',
options: [
{
name: 'file',
short: 'f',
description: 'Input file path',
type: 'string',
required: true,
},
],
examples: [
{ command: 'claude-flow route import -f qtable.json', description: 'Import Q-table from file' },
],
action: async (ctx) => {
const filePath = ctx.flags.file;
if (!filePath) {
output.printError('File path is required');
return { success: false, exitCode: 1 };
}
try {
const fs = await import('node:fs/promises');
const content = await fs.readFile(filePath, 'utf-8');
const data = JSON.parse(content);
const router = await getRouter();
router.import(data);
output.printSuccess(`Q-table imported from ${filePath}`);
output.writeln(output.dim(`Loaded ${Object.keys(data).length} state entries`));
return { success: true };
}
catch (error) {
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// Coverage-Aware Routing Subcommand
// ============================================================================
const coverageRouteCommand = {
name: 'coverage',
aliases: ['cov'],
description: 'Route tasks based on test coverage analysis (ADR-017)',
options: [
{
name: 'path',
short: 'p',
description: 'Path to analyze for coverage',
type: 'string',
},
{
name: 'threshold',
short: 't',
description: 'Coverage threshold percentage (default: 80)',
type: 'number',
default: 80,
},
{
name: 'suggest',
short: 's',
description: 'Get suggestions for improving coverage',
type: 'boolean',
default: false,
},
{
name: 'gaps',
short: 'g',
description: 'List coverage gaps with agent assignments',
type: 'boolean',
default: false,
},
{
name: 'json',
short: 'j',
description: 'Output in JSON format',
type: 'boolean',
default: false,
},
],
examples: [
{ command: 'claude-flow route coverage', description: 'Analyze coverage and suggest routing' },
{ command: 'claude-flow route coverage --suggest', description: 'Get improvement suggestions' },
{ command: 'claude-flow route coverage --gaps', description: 'List coverage gaps by agent' },
{ command: 'claude-flow route coverage -p src/auth -t 90', description: 'Analyze specific path with threshold' },
],
action: async (ctx) => {
const path = ctx.flags.path || '';
const threshold = ctx.flags.threshold || 80;
const suggestMode = ctx.flags.suggest;
const gapsMode = ctx.flags.gaps;
const jsonOutput = ctx.flags.json;
const spinner = output.createSpinner({ text: 'Analyzing coverage...', spinner: 'dots' });
spinner.start();
try {
// Lazy load coverage router
const { coverageRoute, coverageSuggest, coverageGaps } = await import('../ruvector/coverage-router.js');
if (gapsMode) {
// List coverage gaps with agent assignments
const result = await coverageGaps({ threshold, groupByAgent: true });
spinner.succeed(`Found ${result.totalGaps} coverage gaps`);
if (jsonOutput) {
output.printJson(result);
}
else {
output.writeln();
output.writeln(output.bold('Coverage Gaps by Agent'));
output.writeln(output.dim(result.summary));
output.writeln();
if (Object.keys(result.byAgent).length > 0) {
for (const [agent, files] of Object.entries(result.byAgent)) {
output.writeln(`${output.highlight(agent)} (${files.length} files)`);
for (const file of files.slice(0, 5)) {
output.writeln(` ${output.dim('•')} ${file}`);
}
if (files.length > 5) {
output.writeln(output.dim(` ... and ${files.length - 5} more`));
}
output.writeln();
}
}
else {
output.printSuccess('No coverage gaps found!');
}
output.writeln();
output.writeln(output.bold('Top Gaps:'));
output.printTable({
columns: [
{ key: 'file', header: 'File', width: 50 },
{ key: 'coverage', header: 'Coverage', width: 12, align: 'right' },
{ key: 'gap', header: 'Gap', width: 10, align: 'right' },
{ key: 'agent', header: 'Agent', width: 15 },
],
data: result.gaps.slice(0, 10).map(g => ({
file: g.file.length > 48 ? '...' + g.file.slice(-45) : g.file,
coverage: `${g.currentCoverage.toFixed(1)}%`,
gap: `${g.gap.toFixed(1)}%`,
agent: g.suggestedAgent,
})),
});
}
return { success: true, data: result };
}
if (suggestMode || path) {
// Suggest improvements for path
const result = await coverageSuggest(path || '.', { threshold, limit: 20 });
spinner.succeed(`Found ${result.suggestions.length} coverage suggestions`);
if (jsonOutput) {
output.printJson(result);
}
else {
output.writeln();
output.writeln(output.bold('Coverage Improvement Suggestions'));
output.writeln(output.dim(`Path: ${result.path}, Threshold: ${threshold}%`));
output.writeln();
if (result.suggestions.length === 0) {
output.printSuccess('All files meet coverage threshold!');
}
else {
output.writeln(`Total Gap: ${output.warning(`${result.totalGap.toFixed(1)}%`)}`);
output.writeln(`Estimated Effort: ${output.dim(`${result.estimatedEffort.toFixed(1)} hours`)}`);
output.writeln();
output.printTable({
columns: [
{ key: 'file', header: 'File', width: 45 },
{ key: 'current', header: 'Current', width: 10, align: 'right' },
{ key: 'target', header: 'Target', width: 10, align: 'right' },
{ key: 'priority', header: 'Priority', width: 10, align: 'right' },
],
data: result.suggestions.slice(0, 15).map(s => ({
file: s.file.length > 43 ? '...' + s.file.slice(-40) : s.file,
current: `${s.currentCoverage.toFixed(1)}%`,
target: `${s.targetCoverage.toFixed(1)}%`,
priority: String(s.priority),
})),
});
// Show test suggestions for top file
if (result.suggestions.length > 0 && result.suggestions[0].suggestedTests.length > 0) {
output.writeln();
output.writeln(output.bold('Suggested Tests for Top Priority File:'));
output.printList(result.suggestions[0].suggestedTests);
}
}
}
return { success: true, data: result };
}
// Default: Route based on coverage analysis
const routeResult = await coverageRoute('', { threshold });
spinner.succeed('Coverage analysis complete');
if (jsonOutput) {
output.printJson(routeResult);
}
else {
output.writeln();
output.writeln(output.bold('Coverage-Aware Routing'));
output.writeln();
const actionColors = {
'add-tests': (s) => output.error(s),
'review-coverage': (s) => output.warning(s),
'prioritize': (s) => output.error(s),
'skip': (s) => output.success(s),
};
const colorFn = actionColors[routeResult.action] || ((s) => s);
output.printBox([
`Action: ${colorFn(routeResult.action.toUpperCase())}`,
`Priority: ${routeResult.priority}/10`,
`Impact Score: ${routeResult.impactScore}%`,
`Estimated Effort: ${routeResult.estimatedEffort} hours`,
``,
`Test Types: ${routeResult.testTypes.join(', ')}`,
`Target Files: ${routeResult.targetFiles.length}`,
].join('\n'), 'Coverage Analysis');
if (routeResult.targetFiles.length > 0) {
output.writeln();
output.writeln(output.bold('Target Files:'));
output.printList(routeResult.targetFiles.slice(0, 5).map(f => f.length > 60 ? '...' + f.slice(-57) : f));
if (routeResult.targetFiles.length > 5) {
output.writeln(output.dim(` ... and ${routeResult.targetFiles.length - 5} more`));
}
}
if (routeResult.gaps.length > 0) {
output.writeln();
output.writeln(output.bold('Coverage Gaps:'));
output.printTable({
columns: [
{ key: 'file', header: 'File', width: 40 },
{ key: 'current', header: 'Current', width: 10, align: 'right' },
{ key: 'gap', header: 'Gap', width: 10, align: 'right' },
],
data: routeResult.gaps.slice(0, 5).map(g => ({
file: g.file.length > 38 ? '...' + g.file.slice(-35) : g.file,
current: `${g.currentCoverage.toFixed(1)}%`,
gap: `${g.gap.toFixed(1)}%`,
})),
});
}
}
return { success: true, data: routeResult };
}
catch (error) {
spinner.fail('Coverage analysis failed');
output.printError(error instanceof Error ? error.message : String(error));
return { success: false, exitCode: 1 };
}
},
};
// ============================================================================
// Main Route Command
// ============================================================================
export const routeCommand = {
name: 'route',
description: 'Intelligent task-to-agent routing using Q-Learning',
subcommands: [
routeTaskCommand,
listAgentsCommand,
statsCommand,
feedbackCommand,
resetCommand,
exportCommand,
importCommand,
coverageRouteCommand,
],
options: [
{
name: 'q-learning',
short: 'q',
description: 'Use Q-Learning for agent selection',
type: 'boolean',
default: true,
},
{
name: 'agent',
short: 'a',
description: 'Force specific agent',
type: 'string',
},
],
examples: [
{ command: 'claude-flow route "implement feature"', description: 'Route task to best agent' },
{ command: 'claude-flow route "write tests" --q-learning', description: 'Use Q-Learning routing' },
{ command: 'claude-flow route --agent coder "fix bug"', description: 'Force specific agent' },
{ command: 'claude-flow route list-agents', description: 'List available agents' },
{ command: 'claude-flow route stats', description: 'Show routing statistics' },
],
action: async (ctx) => {
// If task description provided directly, route it
if (ctx.args.length > 0 && routeTaskCommand.action) {
const result = await routeTaskCommand.action(ctx);
if (result)
return result;
return { success: true };
}
// Show help
output.writeln();
output.writeln(output.bold('Q-Learning Agent Router'));
output.writeln(output.dim('Intelligent task-to-agent routing using reinforcement learning'));
output.writeln();
output.writeln('Usage: claude-flow route <task> [options]');
output.writeln(' claude-flow route <subcommand>');
output.writeln();
output.writeln(output.bold('Subcommands:'));
output.printList([
`${output.highlight('task')} - Route a task to optimal agent`,
`${output.highlight('list-agents')} - List available agent types`,
`${output.highlight('stats')} - Show router statistics`,
`${output.highlight('feedback')} - Provide routing feedback`,
`${output.highlight('reset')} - Reset router state`,
`${output.highlight('export')} - Export Q-table`,
`${output.highlight('import')} - Import Q-table`,
]);
output.writeln();
output.writeln(output.bold('How It Works:'));
output.printList([
'Analyzes task description using hash-based state encoding',
'Uses Q-Learning to learn from routing outcomes',
'Epsilon-greedy exploration for continuous improvement',
'Provides confidence scores and alternatives',
]);
output.writeln();
// Show quick status
const ruvectorAvailable = await isRuvectorAvailable();
output.writeln(output.bold('Backend Status:'));
output.printList([
`RuVector: ${ruvectorAvailable ? output.success('Available') : output.warning('Fallback mode')}`,
`Backend: ${ruvectorAvailable ? 'ruvector-native' : 'JavaScript fallback'}`,
]);
output.writeln();
output.writeln(output.dim('Run "claude-flow route <subcommand> --help" for more info'));
return { success: true };
},
};
export default routeCommand;
//# sourceMappingURL=route.js.map