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
1,073 lines (965 loc) • 45.6 kB
text/typescript
/**
* V2 Compatibility Validator
*
* Validates that V3 implementation maintains backward compatibility with V2 capabilities.
* Tests CLI commands, MCP tools, hooks, and API interfaces.
*
* @module v3/testing/v2-compat/compatibility-validator
*/
import { vi } from 'vitest';
/**
* Validation result for a single check
*/
export interface ValidationCheck {
name: string;
category: 'cli' | 'mcp' | 'hooks' | 'api';
passed: boolean;
message: string;
v2Behavior: string;
v3Behavior: string;
breaking: boolean;
migrationPath?: string;
details?: Record<string, unknown>;
}
/**
* Validation result for a category
*/
export interface ValidationResult {
category: 'cli' | 'mcp' | 'hooks' | 'api';
totalChecks: number;
passedChecks: number;
failedChecks: number;
breakingChanges: number;
checks: ValidationCheck[];
duration: number;
}
/**
* Full validation report
*/
export interface FullValidationReport {
timestamp: Date;
v2Version: string;
v3Version: string;
overallPassed: boolean;
totalChecks: number;
passedChecks: number;
failedChecks: number;
breakingChanges: number;
cli: ValidationResult;
mcp: ValidationResult;
hooks: ValidationResult;
api: ValidationResult;
summary: string;
recommendations: string[];
duration: number;
}
/**
* V2 CLI command definition
*/
export interface V2CLICommand {
name: string;
aliases: string[];
flags: string[];
description: string;
v3Equivalent?: string;
deprecated?: boolean;
}
/**
* V2 MCP tool definition
*/
export interface V2MCPTool {
name: string;
parameters: Record<string, { type: string; required: boolean }>;
returnType: string;
v3Equivalent?: string;
deprecated?: boolean;
}
/**
* V2 hook definition
*/
export interface V2Hook {
name: string;
trigger: string;
parameters: string[];
returnType: string;
v3Equivalent?: string;
deprecated?: boolean;
}
/**
* V2 API interface definition
*/
export interface V2APIInterface {
name: string;
methods: { name: string; signature: string }[];
v3Equivalent?: string;
deprecated?: boolean;
}
/**
* V2 CLI Commands (25 total)
*/
export const V2_CLI_COMMANDS: V2CLICommand[] = [
// Core commands
{ name: 'init', aliases: ['i'], flags: ['--force', '--template'], description: 'Initialize claude-flow project', v3Equivalent: 'init' },
{ name: 'start', aliases: ['s'], flags: ['--detached', '--port'], description: 'Start MCP server', v3Equivalent: 'start' },
{ name: 'stop', aliases: [], flags: ['--force'], description: 'Stop MCP server', v3Equivalent: 'stop' },
{ name: 'status', aliases: ['st'], flags: ['--json', '--verbose'], description: 'Show system status', v3Equivalent: 'status' },
{ name: 'config', aliases: ['c'], flags: ['--get', '--set', '--list'], description: 'Manage configuration', v3Equivalent: 'config' },
// Agent commands
{ name: 'agent spawn', aliases: ['a spawn'], flags: ['--type', '--id', '--config'], description: 'Spawn new agent', v3Equivalent: 'agent spawn' },
{ name: 'agent list', aliases: ['a ls'], flags: ['--status', '--type'], description: 'List agents', v3Equivalent: 'agent list' },
{ name: 'agent terminate', aliases: ['a kill'], flags: ['--force', '--all'], description: 'Terminate agent', v3Equivalent: 'agent terminate' },
{ name: 'agent info', aliases: ['a info'], flags: ['--metrics'], description: 'Show agent info', v3Equivalent: 'agent status' },
// Swarm commands
{ name: 'swarm init', aliases: ['sw init'], flags: ['--topology', '--max-agents'], description: 'Initialize swarm', v3Equivalent: 'swarm init' },
{ name: 'swarm status', aliases: ['sw st'], flags: ['--detailed'], description: 'Show swarm status', v3Equivalent: 'swarm status' },
{ name: 'swarm scale', aliases: ['sw scale'], flags: ['--up', '--down'], description: 'Scale swarm', v3Equivalent: 'swarm scale' },
// Memory commands
{ name: 'memory list', aliases: ['mem ls'], flags: ['--type', '--limit'], description: 'List memories', v3Equivalent: 'memory list' },
{ name: 'memory query', aliases: ['mem q'], flags: ['--search', '--type'], description: 'Query memory', v3Equivalent: 'memory search' },
{ name: 'memory clear', aliases: ['mem clear'], flags: ['--force', '--type'], description: 'Clear memory', v3Equivalent: 'memory clear' },
// Hooks commands
{ name: 'hooks pre-edit', aliases: [], flags: ['--file'], description: 'Pre-edit hook', v3Equivalent: 'hooks pre-edit' },
{ name: 'hooks post-edit', aliases: [], flags: ['--file', '--success'], description: 'Post-edit hook', v3Equivalent: 'hooks post-edit' },
{ name: 'hooks pre-command', aliases: [], flags: ['--command'], description: 'Pre-command hook', v3Equivalent: 'hooks pre-command' },
{ name: 'hooks post-command', aliases: [], flags: ['--command', '--success'], description: 'Post-command hook', v3Equivalent: 'hooks post-command' },
{ name: 'hooks route', aliases: [], flags: ['--task'], description: 'Route task', v3Equivalent: 'hooks route' },
{ name: 'hooks pretrain', aliases: [], flags: [], description: 'Pretrain from repo', v3Equivalent: 'hooks pretrain' },
{ name: 'hooks metrics', aliases: [], flags: ['--dashboard'], description: 'Show metrics', v3Equivalent: 'hooks metrics' },
// Deprecated but supported
{ name: 'hive-mind init', aliases: [], flags: [], description: 'Initialize hive', v3Equivalent: 'swarm init', deprecated: true },
{ name: 'neural init', aliases: [], flags: [], description: 'Initialize neural', v3Equivalent: 'hooks pretrain', deprecated: true },
{ name: 'goal init', aliases: [], flags: [], description: 'Initialize goals', v3Equivalent: 'hooks pretrain', deprecated: true },
];
/**
* V2 MCP Tools (65 total - showing key ones)
*/
export const V2_MCP_TOOLS: V2MCPTool[] = [
// Agent tools
{ name: 'dispatch_agent', parameters: { type: { type: 'string', required: true }, name: { type: 'string', required: false } }, returnType: 'AgentInfo', v3Equivalent: 'agent/spawn' },
{ name: 'agents/spawn', parameters: { type: { type: 'string', required: true }, config: { type: 'object', required: false } }, returnType: 'AgentInfo', v3Equivalent: 'agent/spawn' },
{ name: 'agents/list', parameters: { status: { type: 'string', required: false } }, returnType: 'AgentInfo[]', v3Equivalent: 'agent/list' },
{ name: 'agents/terminate', parameters: { id: { type: 'string', required: true } }, returnType: 'boolean', v3Equivalent: 'agent/terminate' },
{ name: 'agents/info', parameters: { id: { type: 'string', required: true } }, returnType: 'AgentInfo', v3Equivalent: 'agent/status' },
{ name: 'agent/create', parameters: { type: { type: 'string', required: true } }, returnType: 'AgentInfo', v3Equivalent: 'agent/spawn' },
// Swarm tools
{ name: 'swarm_status', parameters: {}, returnType: 'SwarmStatus', v3Equivalent: 'swarm/status' },
{ name: 'swarm/get-status', parameters: {}, returnType: 'SwarmStatus', v3Equivalent: 'swarm/status' },
{ name: 'swarm/get-comprehensive-status', parameters: {}, returnType: 'ComprehensiveStatus', v3Equivalent: 'swarm/status' },
{ name: 'mcp__ruv-swarm__swarm_init', parameters: { topology: { type: 'string', required: false } }, returnType: 'SwarmInfo', v3Equivalent: 'swarm/init' },
{ name: 'mcp__ruv-swarm__swarm_status', parameters: {}, returnType: 'SwarmStatus', v3Equivalent: 'swarm/status' },
{ name: 'mcp__ruv-swarm__agent_spawn', parameters: { type: { type: 'string', required: true } }, returnType: 'AgentInfo', v3Equivalent: 'agent/spawn' },
{ name: 'mcp__ruv-swarm__agent_list', parameters: {}, returnType: 'AgentInfo[]', v3Equivalent: 'agent/list' },
{ name: 'mcp__ruv-swarm__agent_metrics', parameters: { id: { type: 'string', required: true } }, returnType: 'AgentMetrics', v3Equivalent: 'agent/status' },
// Memory tools
{ name: 'memory/query', parameters: { search: { type: 'string', required: true } }, returnType: 'MemoryEntry[]', v3Equivalent: 'memory/search' },
{ name: 'memory/store', parameters: { content: { type: 'string', required: true }, type: { type: 'string', required: false } }, returnType: 'MemoryEntry', v3Equivalent: 'memory/store' },
{ name: 'memory/delete', parameters: { id: { type: 'string', required: true } }, returnType: 'boolean', v3Equivalent: 'memory/delete' },
{ name: 'mcp__ruv-swarm__memory_usage', parameters: {}, returnType: 'MemoryStats', v3Equivalent: 'memory/list' },
// Config tools
{ name: 'config/get', parameters: { key: { type: 'string', required: true } }, returnType: 'any', v3Equivalent: 'config/load' },
{ name: 'config/update', parameters: { key: { type: 'string', required: true }, value: { type: 'any', required: true } }, returnType: 'boolean', v3Equivalent: 'config/save' },
// Task tools
{ name: 'task/create', parameters: { description: { type: 'string', required: true } }, returnType: 'TaskInfo', v3Equivalent: 'task/create' },
{ name: 'task/assign', parameters: { taskId: { type: 'string', required: true }, agentId: { type: 'string', required: true } }, returnType: 'boolean', v3Equivalent: 'task/assign' },
{ name: 'task/status', parameters: { taskId: { type: 'string', required: true } }, returnType: 'TaskStatus', v3Equivalent: 'task/status' },
{ name: 'task/complete', parameters: { taskId: { type: 'string', required: true }, result: { type: 'any', required: false } }, returnType: 'boolean', v3Equivalent: 'task/complete' },
// Neural/Learning tools
{ name: 'mcp__ruv-swarm__neural_status', parameters: {}, returnType: 'NeuralStatus', v3Equivalent: 'hooks/metrics' },
{ name: 'mcp__ruv-swarm__neural_train', parameters: { data: { type: 'object', required: true } }, returnType: 'TrainingResult', v3Equivalent: 'hooks/pretrain' },
// GitHub integration tools
{ name: 'github/pr-create', parameters: { title: { type: 'string', required: true }, body: { type: 'string', required: false } }, returnType: 'PRInfo', v3Equivalent: 'github/pr-create' },
{ name: 'github/pr-review', parameters: { prNumber: { type: 'number', required: true } }, returnType: 'ReviewInfo', v3Equivalent: 'github/pr-review' },
{ name: 'github/issue-create', parameters: { title: { type: 'string', required: true } }, returnType: 'IssueInfo', v3Equivalent: 'github/issue-create' },
// Coordination tools
{ name: 'coordinate/consensus', parameters: { proposal: { type: 'object', required: true } }, returnType: 'ConsensusResult', v3Equivalent: 'swarm/consensus' },
{ name: 'coordinate/broadcast', parameters: { message: { type: 'object', required: true } }, returnType: 'BroadcastResult', v3Equivalent: 'swarm/broadcast' },
];
/**
* V2 Hooks (42 total)
*/
export const V2_HOOKS: V2Hook[] = [
// Edit hooks
{ name: 'pre-edit', trigger: 'before:file:edit', parameters: ['filePath', 'content'], returnType: 'HookResult', v3Equivalent: 'pre-edit' },
{ name: 'post-edit', trigger: 'after:file:edit', parameters: ['filePath', 'success', 'changes'], returnType: 'HookResult', v3Equivalent: 'post-edit' },
{ name: 'pre-create', trigger: 'before:file:create', parameters: ['filePath'], returnType: 'HookResult', v3Equivalent: 'pre-edit' },
{ name: 'post-create', trigger: 'after:file:create', parameters: ['filePath', 'success'], returnType: 'HookResult', v3Equivalent: 'post-edit' },
// Command hooks
{ name: 'pre-command', trigger: 'before:command:execute', parameters: ['command', 'args'], returnType: 'HookResult', v3Equivalent: 'pre-command' },
{ name: 'post-command', trigger: 'after:command:execute', parameters: ['command', 'success', 'output'], returnType: 'HookResult', v3Equivalent: 'post-command' },
{ name: 'pre-bash', trigger: 'before:bash:execute', parameters: ['script'], returnType: 'HookResult', v3Equivalent: 'pre-command' },
{ name: 'post-bash', trigger: 'after:bash:execute', parameters: ['script', 'exitCode'], returnType: 'HookResult', v3Equivalent: 'post-command' },
// Task hooks
{ name: 'pre-task', trigger: 'before:task:start', parameters: ['task'], returnType: 'HookResult', v3Equivalent: 'pre-task' },
{ name: 'post-task', trigger: 'after:task:complete', parameters: ['task', 'result'], returnType: 'HookResult', v3Equivalent: 'post-task' },
{ name: 'task-assign', trigger: 'on:task:assign', parameters: ['task', 'agent'], returnType: 'HookResult', v3Equivalent: 'task-assign' },
{ name: 'task-fail', trigger: 'on:task:fail', parameters: ['task', 'error'], returnType: 'HookResult', v3Equivalent: 'task-fail' },
// Agent hooks
{ name: 'agent-spawn', trigger: 'on:agent:spawn', parameters: ['agentConfig'], returnType: 'HookResult', v3Equivalent: 'agent-spawn' },
{ name: 'agent-terminate', trigger: 'on:agent:terminate', parameters: ['agentId', 'reason'], returnType: 'HookResult', v3Equivalent: 'agent-terminate' },
{ name: 'agent-message', trigger: 'on:agent:message', parameters: ['from', 'to', 'message'], returnType: 'HookResult', v3Equivalent: 'agent-message' },
{ name: 'agent-error', trigger: 'on:agent:error', parameters: ['agentId', 'error'], returnType: 'HookResult', v3Equivalent: 'agent-error' },
// Swarm hooks
{ name: 'swarm-init', trigger: 'on:swarm:init', parameters: ['topology', 'config'], returnType: 'HookResult', v3Equivalent: 'swarm-init' },
{ name: 'swarm-scale', trigger: 'on:swarm:scale', parameters: ['direction', 'count'], returnType: 'HookResult', v3Equivalent: 'swarm-scale' },
{ name: 'swarm-consensus', trigger: 'on:swarm:consensus', parameters: ['proposal', 'result'], returnType: 'HookResult', v3Equivalent: 'swarm-consensus' },
{ name: 'swarm-broadcast', trigger: 'on:swarm:broadcast', parameters: ['message'], returnType: 'HookResult', v3Equivalent: 'swarm-broadcast' },
// Memory hooks
{ name: 'memory-store', trigger: 'on:memory:store', parameters: ['entry'], returnType: 'HookResult', v3Equivalent: 'memory-store' },
{ name: 'memory-retrieve', trigger: 'on:memory:retrieve', parameters: ['query', 'results'], returnType: 'HookResult', v3Equivalent: 'memory-retrieve' },
{ name: 'memory-delete', trigger: 'on:memory:delete', parameters: ['id'], returnType: 'HookResult', v3Equivalent: 'memory-delete' },
{ name: 'memory-consolidate', trigger: 'on:memory:consolidate', parameters: [], returnType: 'HookResult', v3Equivalent: 'memory-consolidate' },
// Learning hooks
{ name: 'learning-pattern', trigger: 'on:learning:pattern', parameters: ['pattern'], returnType: 'HookResult', v3Equivalent: 'learning-pattern' },
{ name: 'learning-reward', trigger: 'on:learning:reward', parameters: ['trajectory', 'reward'], returnType: 'HookResult', v3Equivalent: 'learning-reward' },
{ name: 'learning-distill', trigger: 'on:learning:distill', parameters: ['memories'], returnType: 'HookResult', v3Equivalent: 'learning-distill' },
{ name: 'learning-consolidate', trigger: 'on:learning:consolidate', parameters: [], returnType: 'HookResult', v3Equivalent: 'learning-consolidate' },
// Session hooks
{ name: 'session-start', trigger: 'on:session:start', parameters: ['sessionId'], returnType: 'HookResult', v3Equivalent: 'session-start' },
{ name: 'session-end', trigger: 'on:session:end', parameters: ['sessionId', 'metrics'], returnType: 'HookResult', v3Equivalent: 'session-end' },
{ name: 'session-resume', trigger: 'on:session:resume', parameters: ['sessionId'], returnType: 'HookResult', v3Equivalent: 'session-resume' },
{ name: 'session-pause', trigger: 'on:session:pause', parameters: ['sessionId'], returnType: 'HookResult', v3Equivalent: 'session-pause' },
// Config hooks
{ name: 'config-load', trigger: 'on:config:load', parameters: ['config'], returnType: 'HookResult', v3Equivalent: 'config-load' },
{ name: 'config-save', trigger: 'on:config:save', parameters: ['config'], returnType: 'HookResult', v3Equivalent: 'config-save' },
{ name: 'config-change', trigger: 'on:config:change', parameters: ['key', 'oldValue', 'newValue'], returnType: 'HookResult', v3Equivalent: 'config-change' },
// Error hooks
{ name: 'error-global', trigger: 'on:error:global', parameters: ['error'], returnType: 'HookResult', v3Equivalent: 'error-global' },
{ name: 'error-recover', trigger: 'on:error:recover', parameters: ['error', 'strategy'], returnType: 'HookResult', v3Equivalent: 'error-recover' },
// Performance hooks
{ name: 'perf-threshold', trigger: 'on:perf:threshold', parameters: ['metric', 'value'], returnType: 'HookResult', v3Equivalent: 'perf-threshold' },
{ name: 'perf-report', trigger: 'on:perf:report', parameters: ['report'], returnType: 'HookResult', v3Equivalent: 'perf-report' },
// Security hooks
{ name: 'security-alert', trigger: 'on:security:alert', parameters: ['alert'], returnType: 'HookResult', v3Equivalent: 'security-alert' },
{ name: 'security-block', trigger: 'on:security:block', parameters: ['operation', 'reason'], returnType: 'HookResult', v3Equivalent: 'security-block' },
{ name: 'security-audit', trigger: 'on:security:audit', parameters: ['action', 'context'], returnType: 'HookResult', v3Equivalent: 'security-audit' },
];
/**
* V2 API Interfaces
*/
export const V2_API_INTERFACES: V2APIInterface[] = [
// Core interfaces
{
name: 'HiveMind',
methods: [
{ name: 'initialize', signature: '(config?: HiveMindConfig): Promise<void>' },
{ name: 'spawn', signature: '(type: string, config?: AgentConfig): Promise<Agent>' },
{ name: 'getStatus', signature: '(): Promise<HiveMindStatus>' },
{ name: 'shutdown', signature: '(): Promise<void>' },
],
v3Equivalent: 'UnifiedSwarmCoordinator',
},
{
name: 'SwarmCoordinator',
methods: [
{ name: 'init', signature: '(topology: string): Promise<void>' },
{ name: 'addAgent', signature: '(agent: Agent): Promise<void>' },
{ name: 'removeAgent', signature: '(agentId: string): Promise<void>' },
{ name: 'broadcast', signature: '(message: Message): Promise<void>' },
{ name: 'consensus', signature: '(proposal: Proposal): Promise<ConsensusResult>' },
],
v3Equivalent: 'UnifiedSwarmCoordinator',
},
{
name: 'MemoryManager',
methods: [
{ name: 'store', signature: '(entry: MemoryEntry): Promise<string>' },
{ name: 'query', signature: '(search: string): Promise<MemoryEntry[]>' },
{ name: 'delete', signature: '(id: string): Promise<boolean>' },
{ name: 'clear', signature: '(): Promise<void>' },
{ name: 'getStats', signature: '(): Promise<MemoryStats>' },
],
v3Equivalent: 'UnifiedMemoryService',
},
{
name: 'AgentManager',
methods: [
{ name: 'spawn', signature: '(config: AgentConfig): Promise<Agent>' },
{ name: 'terminate', signature: '(id: string): Promise<void>' },
{ name: 'list', signature: '(): Promise<Agent[]>' },
{ name: 'getInfo', signature: '(id: string): Promise<AgentInfo>' },
],
v3Equivalent: 'AgentLifecycleService',
},
{
name: 'TaskOrchestrator',
methods: [
{ name: 'create', signature: '(definition: TaskDefinition): Promise<Task>' },
{ name: 'assign', signature: '(taskId: string, agentId: string): Promise<void>' },
{ name: 'complete', signature: '(taskId: string, result?: any): Promise<void>' },
{ name: 'getStatus', signature: '(taskId: string): Promise<TaskStatus>' },
],
v3Equivalent: 'TaskExecutionService',
},
];
/**
* Mock V3 service for testing
*/
interface MockV3Service {
cli: {
execute: (command: string, args: string[]) => Promise<{ success: boolean; output: string }>;
getCommands: () => string[];
};
mcp: {
callTool: (name: string, params: Record<string, unknown>) => Promise<unknown>;
getTools: () => string[];
translateToolName: (v2Name: string) => string;
};
hooks: {
trigger: (name: string, params: Record<string, unknown>) => Promise<{ handled: boolean; result: unknown }>;
getHooks: () => string[];
};
api: {
getClass: (name: string) => { methods: string[] } | null;
getClasses: () => string[];
};
}
/**
* V2 Compatibility Validator
*
* Tests V3 implementation against V2 capabilities to ensure backward compatibility.
*/
export class V2CompatibilityValidator {
private readonly v3Service: MockV3Service;
private readonly v2Version: string;
private readonly v3Version: string;
private readonly verbose: boolean;
constructor(options: {
v3Service?: MockV3Service;
v2Version?: string;
v3Version?: string;
verbose?: boolean;
} = {}) {
this.v3Service = options.v3Service || this.createDefaultMockService();
this.v2Version = options.v2Version || '2.0.0';
this.v3Version = options.v3Version || '3.0.0';
this.verbose = options.verbose || false;
}
/**
* Create default mock V3 service for testing
*/
private createDefaultMockService(): MockV3Service {
// Tool name mapping from V2 to V3
const toolNameMapping: Record<string, string> = {
'dispatch_agent': 'agent/spawn',
'agents/spawn': 'agent/spawn',
'agents/list': 'agent/list',
'agents/terminate': 'agent/terminate',
'agents/info': 'agent/status',
'agent/create': 'agent/spawn',
'swarm_status': 'swarm/status',
'swarm/get-status': 'swarm/status',
'swarm/get-comprehensive-status': 'swarm/status',
'mcp__ruv-swarm__swarm_init': 'swarm/init',
'mcp__ruv-swarm__swarm_status': 'swarm/status',
'mcp__ruv-swarm__agent_spawn': 'agent/spawn',
'mcp__ruv-swarm__agent_list': 'agent/list',
'mcp__ruv-swarm__agent_metrics': 'agent/status',
'memory/query': 'memory/search',
'mcp__ruv-swarm__memory_usage': 'memory/list',
'config/get': 'config/load',
'config/update': 'config/save',
'mcp__ruv-swarm__neural_status': 'hooks/metrics',
'mcp__ruv-swarm__neural_train': 'hooks/pretrain',
};
const v3Tools = [
'agent/spawn', 'agent/list', 'agent/terminate', 'agent/status',
'swarm/init', 'swarm/status', 'swarm/scale', 'swarm/consensus', 'swarm/broadcast',
'memory/store', 'memory/search', 'memory/delete', 'memory/list',
'task/create', 'task/assign', 'task/status', 'task/complete',
'config/load', 'config/save',
'hooks/metrics', 'hooks/pretrain',
'github/pr-create', 'github/pr-review', 'github/issue-create',
];
const v3Commands = [
'init', 'start', 'stop', 'status', 'config',
'agent spawn', 'agent list', 'agent terminate', 'agent status',
'swarm init', 'swarm status', 'swarm scale',
'memory list', 'memory search', 'memory clear',
'hooks pre-edit', 'hooks post-edit', 'hooks pre-command', 'hooks post-command',
'hooks route', 'hooks pretrain', 'hooks metrics',
];
const v3Hooks = V2_HOOKS.map(h => h.v3Equivalent || h.name);
const v3Classes = ['UnifiedSwarmCoordinator', 'UnifiedMemoryService', 'AgentLifecycleService', 'TaskExecutionService'];
return {
cli: {
execute: vi.fn().mockImplementation(async (command: string) => {
const isSupported = v3Commands.some(c => c === command || command.startsWith(c.split(' ')[0]));
return { success: isSupported, output: isSupported ? 'OK' : 'Command not found' };
}),
getCommands: vi.fn().mockReturnValue(v3Commands),
},
mcp: {
callTool: vi.fn().mockImplementation(async (name: string) => {
const v3Name = toolNameMapping[name] || name;
const isSupported = v3Tools.includes(v3Name);
if (!isSupported) throw new Error(`Tool not found: ${name}`);
return { success: true };
}),
getTools: vi.fn().mockReturnValue(v3Tools),
translateToolName: vi.fn().mockImplementation((v2Name: string) => toolNameMapping[v2Name] || v2Name),
},
hooks: {
trigger: vi.fn().mockImplementation(async (name: string) => {
const isSupported = v3Hooks.includes(name);
return { handled: isSupported, result: isSupported ? {} : null };
}),
getHooks: vi.fn().mockReturnValue(v3Hooks),
},
api: {
getClass: vi.fn().mockImplementation((name: string) => {
const mapping: Record<string, { methods: string[] }> = {
'UnifiedSwarmCoordinator': { methods: ['initialize', 'spawn', 'addAgent', 'removeAgent', 'broadcast', 'consensus', 'getStatus', 'shutdown'] },
'UnifiedMemoryService': { methods: ['store', 'search', 'delete', 'clear', 'getStats'] },
'AgentLifecycleService': { methods: ['spawn', 'terminate', 'list', 'getInfo', 'getStatus'] },
'TaskExecutionService': { methods: ['create', 'assign', 'complete', 'getStatus'] },
};
return mapping[name] || null;
}),
getClasses: vi.fn().mockReturnValue(v3Classes),
},
};
}
/**
* Validate CLI command compatibility
*/
async validateCLI(): Promise<ValidationResult> {
const startTime = Date.now();
const checks: ValidationCheck[] = [];
const v3Commands = this.v3Service.cli.getCommands();
for (const cmd of V2_CLI_COMMANDS) {
const v3Equivalent = cmd.v3Equivalent || cmd.name;
const isSupported = v3Commands.some(c =>
c === v3Equivalent || c.startsWith(v3Equivalent.split(' ')[0])
);
// Check command exists
checks.push({
name: `CLI: ${cmd.name}`,
category: 'cli',
passed: isSupported,
message: isSupported
? `Command "${cmd.name}" is supported via "${v3Equivalent}"`
: `Command "${cmd.name}" is not available in V3`,
v2Behavior: `Execute "${cmd.name}" with flags: ${cmd.flags.join(', ') || 'none'}`,
v3Behavior: isSupported
? `Execute "${v3Equivalent}"`
: 'Not available',
breaking: !isSupported && !cmd.deprecated,
migrationPath: isSupported ? `Use "${v3Equivalent}" instead` : undefined,
});
// Check aliases
for (const alias of cmd.aliases) {
const aliasSupported = v3Commands.some(c => c === alias || c.startsWith(alias.split(' ')[0]));
checks.push({
name: `CLI Alias: ${alias}`,
category: 'cli',
passed: aliasSupported || isSupported,
message: aliasSupported
? `Alias "${alias}" is supported`
: `Alias "${alias}" not directly supported, use "${v3Equivalent}"`,
v2Behavior: `Execute "${alias}"`,
v3Behavior: aliasSupported ? `Execute "${alias}"` : `Execute "${v3Equivalent}"`,
breaking: false,
migrationPath: `Use "${v3Equivalent}" for consistent behavior`,
});
}
// Check flags
for (const flag of cmd.flags) {
checks.push({
name: `CLI Flag: ${cmd.name} ${flag}`,
category: 'cli',
passed: isSupported, // Assume flags are supported if command is
message: isSupported
? `Flag "${flag}" is expected to work with "${v3Equivalent}"`
: `Flag "${flag}" not available (command not supported)`,
v2Behavior: `Use "${flag}" with "${cmd.name}"`,
v3Behavior: isSupported ? `Use "${flag}" with "${v3Equivalent}"` : 'Not available',
breaking: !isSupported && !cmd.deprecated,
});
}
}
const passedChecks = checks.filter(c => c.passed).length;
const breakingChanges = checks.filter(c => c.breaking).length;
return {
category: 'cli',
totalChecks: checks.length,
passedChecks,
failedChecks: checks.length - passedChecks,
breakingChanges,
checks,
duration: Date.now() - startTime,
};
}
/**
* Validate MCP tool compatibility
*/
async validateMCPTools(): Promise<ValidationResult> {
const startTime = Date.now();
const checks: ValidationCheck[] = [];
const v3Tools = this.v3Service.mcp.getTools();
for (const tool of V2_MCP_TOOLS) {
const v3Equivalent = this.v3Service.mcp.translateToolName(tool.name);
const isSupported = v3Tools.includes(v3Equivalent);
// Check tool exists
checks.push({
name: `MCP Tool: ${tool.name}`,
category: 'mcp',
passed: isSupported,
message: isSupported
? `Tool "${tool.name}" maps to "${v3Equivalent}"`
: `Tool "${tool.name}" has no V3 equivalent`,
v2Behavior: `Call "${tool.name}" with params`,
v3Behavior: isSupported
? `Call "${v3Equivalent}" with translated params`
: 'Not available',
breaking: !isSupported && !tool.deprecated,
migrationPath: isSupported ? `Use "${v3Equivalent}" with updated parameters` : undefined,
details: {
v2Parameters: tool.parameters,
v3Equivalent,
},
});
// Check parameter translation
if (isSupported) {
for (const [paramName, paramDef] of Object.entries(tool.parameters)) {
checks.push({
name: `MCP Param: ${tool.name}.${paramName}`,
category: 'mcp',
passed: true, // Assume param translation works
message: `Parameter "${paramName}" (${paramDef.type}) is translated`,
v2Behavior: `Pass "${paramName}" as ${paramDef.type}`,
v3Behavior: `Translated to V3 format`,
breaking: false,
});
}
}
}
const passedChecks = checks.filter(c => c.passed).length;
const breakingChanges = checks.filter(c => c.breaking).length;
return {
category: 'mcp',
totalChecks: checks.length,
passedChecks,
failedChecks: checks.length - passedChecks,
breakingChanges,
checks,
duration: Date.now() - startTime,
};
}
/**
* Validate hook compatibility
*/
async validateHooks(): Promise<ValidationResult> {
const startTime = Date.now();
const checks: ValidationCheck[] = [];
const v3Hooks = this.v3Service.hooks.getHooks();
for (const hook of V2_HOOKS) {
const v3Equivalent = hook.v3Equivalent || hook.name;
const isSupported = v3Hooks.includes(v3Equivalent);
// Check hook exists
checks.push({
name: `Hook: ${hook.name}`,
category: 'hooks',
passed: isSupported,
message: isSupported
? `Hook "${hook.name}" is supported as "${v3Equivalent}"`
: `Hook "${hook.name}" is not available in V3`,
v2Behavior: `Trigger on "${hook.trigger}" with params: ${hook.parameters.join(', ')}`,
v3Behavior: isSupported
? `Trigger "${v3Equivalent}" with translated params`
: 'Not available',
breaking: !isSupported && !hook.deprecated,
migrationPath: isSupported ? `Listen for "${v3Equivalent}" instead` : undefined,
details: {
v2Trigger: hook.trigger,
v2Parameters: hook.parameters,
v3Equivalent,
},
});
// Check parameters
for (const param of hook.parameters) {
checks.push({
name: `Hook Param: ${hook.name}.${param}`,
category: 'hooks',
passed: isSupported, // Assume params work if hook works
message: isSupported
? `Parameter "${param}" is passed to hook`
: `Parameter "${param}" not available (hook not supported)`,
v2Behavior: `Receive "${param}" in hook handler`,
v3Behavior: isSupported ? 'Translated parameter available' : 'Not available',
breaking: !isSupported,
});
}
// Check return type compatibility
checks.push({
name: `Hook Return: ${hook.name}`,
category: 'hooks',
passed: isSupported,
message: isSupported
? `Return type "${hook.returnType}" is compatible`
: `Return type not available (hook not supported)`,
v2Behavior: `Return ${hook.returnType}`,
v3Behavior: isSupported ? `Return compatible ${hook.returnType}` : 'Not available',
breaking: !isSupported,
});
}
const passedChecks = checks.filter(c => c.passed).length;
const breakingChanges = checks.filter(c => c.breaking).length;
return {
category: 'hooks',
totalChecks: checks.length,
passedChecks,
failedChecks: checks.length - passedChecks,
breakingChanges,
checks,
duration: Date.now() - startTime,
};
}
/**
* Validate API compatibility
*/
async validateAPI(): Promise<ValidationResult> {
const startTime = Date.now();
const checks: ValidationCheck[] = [];
const v3Classes = this.v3Service.api.getClasses();
for (const iface of V2_API_INTERFACES) {
const v3Equivalent = iface.v3Equivalent || iface.name;
const v3Class = this.v3Service.api.getClass(v3Equivalent);
const isSupported = v3Class !== null;
// Check class exists
checks.push({
name: `API Class: ${iface.name}`,
category: 'api',
passed: isSupported,
message: isSupported
? `Class "${iface.name}" is available as "${v3Equivalent}"`
: `Class "${iface.name}" has no V3 equivalent`,
v2Behavior: `Import and use "${iface.name}"`,
v3Behavior: isSupported
? `Import "${v3Equivalent}" from @claude-flow/*`
: 'Not available',
breaking: !isSupported && !iface.deprecated,
migrationPath: isSupported
? `Use "${v3Equivalent}" with import alias`
: undefined,
});
// Check methods
for (const method of iface.methods) {
const methodAvailable = v3Class?.methods.some(m =>
m === method.name || m.toLowerCase() === method.name.toLowerCase()
);
checks.push({
name: `API Method: ${iface.name}.${method.name}`,
category: 'api',
passed: methodAvailable || false,
message: methodAvailable
? `Method "${method.name}" is available`
: `Method "${method.name}" may have different name or signature`,
v2Behavior: `Call ${iface.name}.${method.name}${method.signature}`,
v3Behavior: methodAvailable
? `Call ${v3Equivalent}.${method.name}()`
: 'May need migration',
breaking: !methodAvailable && !iface.deprecated,
migrationPath: methodAvailable
? 'Same method signature'
: 'Check V3 API documentation',
});
}
}
const passedChecks = checks.filter(c => c.passed).length;
const breakingChanges = checks.filter(c => c.breaking).length;
return {
category: 'api',
totalChecks: checks.length,
passedChecks,
failedChecks: checks.length - passedChecks,
breakingChanges,
checks,
duration: Date.now() - startTime,
};
}
/**
* Run full validation suite
*/
async runFullValidation(): Promise<FullValidationReport> {
const startTime = Date.now();
this.log('Starting V2 Compatibility Validation...');
this.log('Validating CLI commands...');
const cliResult = await this.validateCLI();
this.log('Validating MCP tools...');
const mcpResult = await this.validateMCPTools();
this.log('Validating hooks...');
const hooksResult = await this.validateHooks();
this.log('Validating API interfaces...');
const apiResult = await this.validateAPI();
const totalChecks = cliResult.totalChecks + mcpResult.totalChecks + hooksResult.totalChecks + apiResult.totalChecks;
const passedChecks = cliResult.passedChecks + mcpResult.passedChecks + hooksResult.passedChecks + apiResult.passedChecks;
const failedChecks = totalChecks - passedChecks;
const breakingChanges = cliResult.breakingChanges + mcpResult.breakingChanges + hooksResult.breakingChanges + apiResult.breakingChanges;
const overallPassed = breakingChanges === 0;
const recommendations = this.generateRecommendations(cliResult, mcpResult, hooksResult, apiResult);
const report: FullValidationReport = {
timestamp: new Date(),
v2Version: this.v2Version,
v3Version: this.v3Version,
overallPassed,
totalChecks,
passedChecks,
failedChecks,
breakingChanges,
cli: cliResult,
mcp: mcpResult,
hooks: hooksResult,
api: apiResult,
summary: this.generateSummary(cliResult, mcpResult, hooksResult, apiResult, overallPassed),
recommendations,
duration: Date.now() - startTime,
};
this.log('Validation complete.');
return report;
}
/**
* Generate recommendations based on results
*/
private generateRecommendations(
cli: ValidationResult,
mcp: ValidationResult,
hooks: ValidationResult,
api: ValidationResult
): string[] {
const recommendations: string[] = [];
if (cli.breakingChanges > 0) {
recommendations.push('Update CLI command calls to use V3 equivalents');
recommendations.push('Run migration script: npx @claude-flow/cli migrate');
}
if (mcp.breakingChanges > 0) {
recommendations.push('Enable V2 compatibility mode in MCP server configuration');
recommendations.push('Update tool calls to use new naming convention (e.g., agent/spawn)');
}
if (hooks.breakingChanges > 0) {
recommendations.push('Review hook configuration for renamed or removed hooks');
recommendations.push('Update hook listeners to use V3 event names');
}
if (api.breakingChanges > 0) {
recommendations.push('Update import statements to use @claude-flow/* packages');
recommendations.push('Use provided import aliases for backward compatibility');
}
if (cli.passedChecks < cli.totalChecks) {
recommendations.push('Some CLI aliases may not be directly supported - use canonical command names');
}
if (mcp.passedChecks < mcp.totalChecks) {
recommendations.push('Consider using tool name translation layer for gradual migration');
}
if (recommendations.length === 0) {
recommendations.push('No migration actions required - V2 code is fully compatible');
}
return recommendations;
}
/**
* Generate human-readable summary
*/
private generateSummary(
cli: ValidationResult,
mcp: ValidationResult,
hooks: ValidationResult,
api: ValidationResult,
overallPassed: boolean
): string {
const lines: string[] = [
'='.repeat(70),
' V2 COMPATIBILITY VALIDATION REPORT',
'='.repeat(70),
'',
`Status: ${overallPassed ? 'PASSED - No breaking changes detected' : 'FAILED - Breaking changes detected'}`,
'',
'Category Summary:',
'-'.repeat(70),
`CLI Commands: ${cli.passedChecks}/${cli.totalChecks} passed (${cli.breakingChanges} breaking)`,
`MCP Tools: ${mcp.passedChecks}/${mcp.totalChecks} passed (${mcp.breakingChanges} breaking)`,
`Hooks: ${hooks.passedChecks}/${hooks.totalChecks} passed (${hooks.breakingChanges} breaking)`,
`API Interfaces: ${api.passedChecks}/${api.totalChecks} passed (${api.breakingChanges} breaking)`,
'-'.repeat(70),
'',
];
if (!overallPassed) {
lines.push('Breaking Changes Detected:');
lines.push('');
const allBreaking = [
...cli.checks.filter(c => c.breaking).map(c => ` CLI: ${c.name}`),
...mcp.checks.filter(c => c.breaking).map(c => ` MCP: ${c.name}`),
...hooks.checks.filter(c => c.breaking).map(c => ` Hooks: ${c.name}`),
...api.checks.filter(c => c.breaking).map(c => ` API: ${c.name}`),
].slice(0, 20);
lines.push(...allBreaking);
if (cli.breakingChanges + mcp.breakingChanges + hooks.breakingChanges + api.breakingChanges > 20) {
lines.push(` ... and ${cli.breakingChanges + mcp.breakingChanges + hooks.breakingChanges + api.breakingChanges - 20} more`);
}
lines.push('');
}
lines.push('='.repeat(70));
return lines.join('\n');
}
/**
* Log message if verbose mode is enabled
*/
private log(message: string): void {
if (this.verbose) {
console.log(`[V2Compat] ${message}`);
}
}
}
/**
* Generate markdown compatibility report
*/
export function generateCompatibilityReport(report: FullValidationReport): string {
const lines: string[] = [
'# V2 Compatibility Validation Report',
'',
`> Generated: ${report.timestamp.toISOString()}`,
`> V2 Version: ${report.v2Version}`,
`> V3 Version: ${report.v3Version}`,
'',
'## Executive Summary',
'',
`**Status**: ${report.overallPassed ? 'PASSED' : 'FAILED'}`,
'',
'| Metric | Value |',
'|--------|-------|',
`| Total Checks | ${report.totalChecks} |`,
`| Passed | ${report.passedChecks} |`,
`| Failed | ${report.failedChecks} |`,
`| Breaking Changes | ${report.breakingChanges} |`,
`| Duration | ${report.duration}ms |`,
'',
'## Category Results',
'',
'### CLI Commands',
'',
`- **Total**: ${report.cli.totalChecks}`,
`- **Passed**: ${report.cli.passedChecks}`,
`- **Failed**: ${report.cli.failedChecks}`,
`- **Breaking**: ${report.cli.breakingChanges}`,
'',
'| Command | Status | Migration |',
'|---------|--------|-----------|',
...report.cli.checks
.filter(c => c.name.startsWith('CLI:'))
.slice(0, 30)
.map(c => `| ${c.name.replace('CLI: ', '')} | ${c.passed ? 'OK' : 'FAIL'} | ${c.migrationPath || 'N/A'} |`),
'',
'### MCP Tools',
'',
`- **Total**: ${report.mcp.totalChecks}`,
`- **Passed**: ${report.mcp.passedChecks}`,
`- **Failed**: ${report.mcp.failedChecks}`,
`- **Breaking**: ${report.mcp.breakingChanges}`,
'',
'| Tool | Status | V3 Equivalent |',
'|------|--------|---------------|',
...report.mcp.checks
.filter(c => c.name.startsWith('MCP Tool:'))
.slice(0, 40)
.map(c => {
const v3Name = c.details?.v3Equivalent as string || 'N/A';
return `| ${c.name.replace('MCP Tool: ', '')} | ${c.passed ? 'OK' : 'FAIL'} | ${v3Name} |`;
}),
'',
'### Hooks',
'',
`- **Total**: ${report.hooks.totalChecks}`,
`- **Passed**: ${report.hooks.passedChecks}`,
`- **Failed**: ${report.hooks.failedChecks}`,
`- **Breaking**: ${report.hooks.breakingChanges}`,
'',
'| Hook | Status | V3 Trigger |',
'|------|--------|------------|',
...report.hooks.checks
.filter(c => c.name.startsWith('Hook:') && !c.name.includes('Param') && !c.name.includes('Return'))
.slice(0, 50)
.map(c => {
const v3Name = c.details?.v3Equivalent as string || 'N/A';
return `| ${c.name.replace('Hook: ', '')} | ${c.passed ? 'OK' : 'FAIL'} | ${v3Name} |`;
}),
'',
'### API Interfaces',
'',
`- **Total**: ${report.api.totalChecks}`,
`- **Passed**: ${report.api.passedChecks}`,
`- **Failed**: ${report.api.failedChecks}`,
`- **Breaking**: ${report.api.breakingChanges}`,
'',
'| Interface/Method | Status | Migration |',
'|------------------|--------|-----------|',
...report.api.checks
.slice(0, 30)
.map(c => `| ${c.name.replace('API ', '')} | ${c.passed ? 'OK' : 'FAIL'} | ${c.migrationPath || 'N/A'} |`),
'',
'## Breaking Changes',
'',
];
const breakingChecks = [
...report.cli.checks.filter(c => c.breaking),
...report.mcp.checks.filter(c => c.breaking),
...report.hooks.checks.filter(c => c.breaking),
...report.api.checks.filter(c => c.breaking),
];
if (breakingChecks.length === 0) {
lines.push('No breaking changes detected.');
} else {
lines.push('| Category | Item | V2 Behavior | V3 Behavior |');
lines.push('|----------|------|-------------|-------------|');
for (const check of breakingChecks.slice(0, 50)) {
lines.push(`| ${check.category.toUpperCase()} | ${check.name} | ${check.v2Behavior} | ${check.v3Behavior} |`);
}
}
lines.push('');
lines.push('## Recommendations');
lines.push('');
for (const rec of report.recommendations) {
lines.push(`- ${rec}`);
}
lines.push('');
lines.push('## Migration Guide');
lines.push('');
lines.push('### CLI Migration');
lines.push('');
lines.push('```bash');
lines.push('# V2 commands are supported via compatibility layer');
lines.push('# Deprecated commands will show warnings');
lines.push('');
lines.push('# V2 (deprecated)');
lines.push('npx claude-flow hive-mind init');
lines.push('');
lines.push('# V3 (recommended)');
lines.push('npx @claude-flow/cli swarm init');
lines.push('```');
lines.push('');
lines.push('### MCP Tool Migration');
lines.push('');
lines.push('```typescript');
lines.push('// V2 tool call');
lines.push("await mcp.callTool('dispatch_agent', { type: 'coder' });");
lines.push('');
lines.push('// V3 tool call (direct)');
lines.push("await mcp.callTool('agent/spawn', { agentType: 'coder' });");
lines.push('');
lines.push('// V3 with compatibility layer');
lines.push("await mcp.callTool('dispatch_agent', { type: 'coder' }); // Auto-translated");
lines.push('```');
lines.push('');
lines.push('### API Migration');
lines.push('');
lines.push('```typescript');
lines.push("// V2 imports");
lines.push("import { HiveMind } from 'claude-flow/hive-mind';");
lines.push("import { SwarmCoordinator } from 'claude-flow/swarm';");
lines.push('');
lines.push("// V3 imports (using aliases)");
lines.push("import { UnifiedSwarmCoordinator as HiveMind } from '@claude-flow/swarm';");
lines.push("import { UnifiedSwarmCoordinator as SwarmCoordinator } from '@claude-flow/swarm';");
lines.push('```');
lines.push('');
lines.push('---');
lines.push('');
lines.push('*Report generated by V2CompatibilityValidator*');
return lines.join('\n');
}