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
583 lines • 23.8 kB
JavaScript
/**
* Hive-Mind MCP Tools for CLI
*
* Tool definitions for collective intelligence and swarm coordination.
*/
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
// Storage paths
const STORAGE_DIR = '.claude-flow';
const HIVE_DIR = 'hive-mind';
const HIVE_FILE = 'state.json';
function getHiveDir() {
return join(process.cwd(), STORAGE_DIR, HIVE_DIR);
}
function getHivePath() {
return join(getHiveDir(), HIVE_FILE);
}
function ensureHiveDir() {
const dir = getHiveDir();
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
function loadHiveState() {
try {
const path = getHivePath();
if (existsSync(path)) {
const data = readFileSync(path, 'utf-8');
return JSON.parse(data);
}
}
catch {
// Return default state on error
}
return {
initialized: false,
topology: 'mesh',
workers: [],
consensus: { pending: [], history: [] },
sharedMemory: {},
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
}
function saveHiveState(state) {
ensureHiveDir();
state.updatedAt = new Date().toISOString();
writeFileSync(getHivePath(), JSON.stringify(state, null, 2), 'utf-8');
}
// Import agent store helpers for spawn functionality
import { existsSync as agentStoreExists, readFileSync as readAgentStore, writeFileSync as writeAgentStore, mkdirSync as mkdirAgentStore } from 'node:fs';
function loadAgentStore() {
const storePath = join(process.cwd(), '.claude-flow', 'agents.json');
try {
if (agentStoreExists(storePath)) {
return JSON.parse(readAgentStore(storePath, 'utf-8'));
}
}
catch { /* ignore */ }
return { agents: {} };
}
function saveAgentStore(store) {
const storeDir = join(process.cwd(), '.claude-flow');
if (!agentStoreExists(storeDir)) {
mkdirAgentStore(storeDir, { recursive: true });
}
writeAgentStore(join(storeDir, 'agents.json'), JSON.stringify(store, null, 2), 'utf-8');
}
export const hiveMindTools = [
{
name: 'hive-mind_spawn',
description: 'Spawn workers and automatically join them to the hive-mind (combines agent/spawn + hive-mind/join)',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
count: { type: 'number', description: 'Number of workers to spawn (default: 1)', default: 1 },
role: { type: 'string', enum: ['worker', 'specialist', 'scout'], description: 'Worker role in hive', default: 'worker' },
agentType: { type: 'string', description: 'Agent type for spawned workers', default: 'worker' },
prefix: { type: 'string', description: 'Prefix for worker IDs', default: 'hive-worker' },
},
},
handler: async (input) => {
const state = loadHiveState();
if (!state.initialized) {
return { success: false, error: 'Hive-mind not initialized. Run hive-mind/init first.' };
}
const count = Math.min(Math.max(1, input.count || 1), 20); // Cap at 20
const role = input.role || 'worker';
const agentType = input.agentType || 'worker';
const prefix = input.prefix || 'hive-worker';
const agentStore = loadAgentStore();
const spawnedWorkers = [];
for (let i = 0; i < count; i++) {
const agentId = `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
// Create agent record (like agent/spawn)
agentStore.agents[agentId] = {
agentId,
agentType,
status: 'idle',
health: 1.0,
taskCount: 0,
config: { role, hiveRole: role },
createdAt: new Date().toISOString(),
domain: 'hive-mind',
};
// Join to hive-mind (like hive-mind/join)
if (!state.workers.includes(agentId)) {
state.workers.push(agentId);
}
spawnedWorkers.push({
agentId,
role,
joinedAt: new Date().toISOString(),
});
}
saveAgentStore(agentStore);
saveHiveState(state);
return {
success: true,
spawned: count,
workers: spawnedWorkers,
totalWorkers: state.workers.length,
hiveStatus: 'active',
message: `Spawned ${count} worker(s) and joined them to the hive-mind`,
};
},
},
{
name: 'hive-mind_init',
description: 'Initialize the hive-mind collective',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
topology: { type: 'string', enum: ['mesh', 'hierarchical', 'ring', 'star'], description: 'Network topology' },
queenId: { type: 'string', description: 'Initial queen agent ID' },
},
},
handler: async (input) => {
const state = loadHiveState();
const hiveId = `hive-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const queenId = input.queenId || `queen-${Date.now()}`;
state.initialized = true;
state.topology = input.topology || 'mesh';
state.createdAt = new Date().toISOString();
state.queen = {
agentId: queenId,
electedAt: new Date().toISOString(),
term: 1,
};
saveHiveState(state);
return {
success: true,
hiveId,
topology: state.topology,
consensus: input.consensus || 'byzantine',
queenId,
status: 'initialized',
config: {
topology: state.topology,
consensus: input.consensus || 'byzantine',
maxAgents: input.maxAgents || 15,
persist: input.persist !== false,
memoryBackend: input.memoryBackend || 'hybrid',
},
createdAt: state.createdAt,
};
},
},
{
name: 'hive-mind_status',
description: 'Get hive-mind status',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
verbose: { type: 'boolean', description: 'Include detailed information' },
},
},
handler: async (input) => {
const state = loadHiveState();
const uptime = state.createdAt ? Date.now() - new Date(state.createdAt).getTime() : 0;
const status = {
// CLI expected fields
hiveId: `hive-${state.createdAt ? new Date(state.createdAt).getTime() : Date.now()}`,
status: state.initialized ? 'active' : 'offline',
topology: state.topology,
consensus: 'byzantine', // Default consensus type
queen: state.queen ? {
id: state.queen.agentId,
agentId: state.queen.agentId,
status: 'active',
load: 0.3 + Math.random() * 0.4, // Simulated load
tasksQueued: state.consensus.pending.length,
electedAt: state.queen.electedAt,
term: state.queen.term,
} : { id: 'N/A', status: 'offline', load: 0, tasksQueued: 0 },
workers: state.workers.map(w => ({
id: w,
type: 'worker',
status: 'idle',
currentTask: null,
tasksCompleted: 0,
})),
metrics: {
totalTasks: state.consensus.history.length + state.consensus.pending.length,
completedTasks: state.consensus.history.length,
failedTasks: 0,
avgTaskTime: 150,
consensusRounds: state.consensus.history.length,
memoryUsage: `${Object.keys(state.sharedMemory).length * 2} KB`,
},
health: {
overall: 'healthy',
queen: state.queen ? 'healthy' : 'unhealthy',
workers: state.workers.length > 0 ? 'healthy' : 'degraded',
consensus: 'healthy',
memory: 'healthy',
},
// Additional fields
id: `hive-${state.createdAt ? new Date(state.createdAt).getTime() : Date.now()}`,
initialized: state.initialized,
workerCount: state.workers.length,
pendingConsensus: state.consensus.pending.length,
sharedMemoryKeys: Object.keys(state.sharedMemory).length,
uptime,
createdAt: state.createdAt,
updatedAt: state.updatedAt,
};
if (input.verbose) {
return {
...status,
workerDetails: state.workers,
consensusHistory: state.consensus.history.slice(-10),
sharedMemory: state.sharedMemory,
};
}
return status;
},
},
{
name: 'hive-mind_join',
description: 'Join an agent to the hive-mind',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
agentId: { type: 'string', description: 'Agent ID to join' },
role: { type: 'string', enum: ['worker', 'specialist', 'scout'], description: 'Agent role in hive' },
},
required: ['agentId'],
},
handler: async (input) => {
const state = loadHiveState();
const agentId = input.agentId;
if (!state.initialized) {
return { success: false, error: 'Hive-mind not initialized' };
}
if (!state.workers.includes(agentId)) {
state.workers.push(agentId);
saveHiveState(state);
}
return {
success: true,
agentId,
role: input.role || 'worker',
totalWorkers: state.workers.length,
joinedAt: new Date().toISOString(),
};
},
},
{
name: 'hive-mind_leave',
description: 'Remove an agent from the hive-mind',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
agentId: { type: 'string', description: 'Agent ID to remove' },
},
required: ['agentId'],
},
handler: async (input) => {
const state = loadHiveState();
const agentId = input.agentId;
const index = state.workers.indexOf(agentId);
if (index > -1) {
state.workers.splice(index, 1);
saveHiveState(state);
return {
success: true,
agentId,
leftAt: new Date().toISOString(),
remainingWorkers: state.workers.length,
};
}
return { success: false, agentId, error: 'Agent not in hive' };
},
},
{
name: 'hive-mind_consensus',
description: 'Propose or vote on consensus',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
action: { type: 'string', enum: ['propose', 'vote', 'status', 'list'], description: 'Consensus action' },
proposalId: { type: 'string', description: 'Proposal ID (for vote/status)' },
type: { type: 'string', description: 'Proposal type (for propose)' },
value: { description: 'Proposal value (for propose)' },
vote: { type: 'boolean', description: 'Vote (true=for, false=against)' },
voterId: { type: 'string', description: 'Voter agent ID' },
},
required: ['action'],
},
handler: async (input) => {
const state = loadHiveState();
const action = input.action;
if (action === 'propose') {
const proposalId = `proposal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const proposal = {
proposalId,
type: input.type || 'general',
value: input.value,
proposedBy: input.voterId || 'system',
proposedAt: new Date().toISOString(),
votes: {},
status: 'pending',
};
state.consensus.pending.push(proposal);
saveHiveState(state);
return {
action,
proposalId,
type: proposal.type,
status: 'pending',
requiredVotes: Math.ceil(state.workers.length / 2) + 1,
};
}
if (action === 'vote') {
const proposal = state.consensus.pending.find(p => p.proposalId === input.proposalId);
if (!proposal) {
return { action, error: 'Proposal not found' };
}
const voterId = input.voterId;
proposal.votes[voterId] = input.vote;
// Check if we have majority
const votesFor = Object.values(proposal.votes).filter(v => v).length;
const votesAgainst = Object.values(proposal.votes).filter(v => !v).length;
const majority = Math.ceil(state.workers.length / 2) + 1;
if (votesFor >= majority) {
proposal.status = 'approved';
state.consensus.history.push({
proposalId: proposal.proposalId,
type: proposal.type,
result: 'approved',
votes: { for: votesFor, against: votesAgainst },
decidedAt: new Date().toISOString(),
});
state.consensus.pending = state.consensus.pending.filter(p => p.proposalId !== proposal.proposalId);
}
else if (votesAgainst >= majority) {
proposal.status = 'rejected';
state.consensus.history.push({
proposalId: proposal.proposalId,
type: proposal.type,
result: 'rejected',
votes: { for: votesFor, against: votesAgainst },
decidedAt: new Date().toISOString(),
});
state.consensus.pending = state.consensus.pending.filter(p => p.proposalId !== proposal.proposalId);
}
saveHiveState(state);
return {
action,
proposalId: proposal.proposalId,
voterId,
vote: input.vote,
votesFor,
votesAgainst,
status: proposal.status,
};
}
if (action === 'status') {
const proposal = state.consensus.pending.find(p => p.proposalId === input.proposalId);
if (!proposal) {
// Check history
const historical = state.consensus.history.find(h => h.proposalId === input.proposalId);
if (historical) {
return { action, ...historical, historical: true };
}
return { action, error: 'Proposal not found' };
}
const votesFor = Object.values(proposal.votes).filter(v => v).length;
const votesAgainst = Object.values(proposal.votes).filter(v => !v).length;
return {
action,
proposalId: proposal.proposalId,
type: proposal.type,
status: proposal.status,
votesFor,
votesAgainst,
totalVotes: Object.keys(proposal.votes).length,
requiredMajority: Math.ceil(state.workers.length / 2) + 1,
};
}
if (action === 'list') {
return {
action,
pending: state.consensus.pending.map(p => ({
proposalId: p.proposalId,
type: p.type,
proposedAt: p.proposedAt,
totalVotes: Object.keys(p.votes).length,
})),
recentHistory: state.consensus.history.slice(-5),
};
}
return { action, error: 'Unknown action' };
},
},
{
name: 'hive-mind_broadcast',
description: 'Broadcast message to all workers',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
message: { type: 'string', description: 'Message to broadcast' },
priority: { type: 'string', enum: ['low', 'normal', 'high', 'critical'], description: 'Message priority' },
fromId: { type: 'string', description: 'Sender agent ID' },
},
required: ['message'],
},
handler: async (input) => {
const state = loadHiveState();
if (!state.initialized) {
return { success: false, error: 'Hive-mind not initialized' };
}
const messageId = `msg-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
// Store in shared memory
const messages = state.sharedMemory.broadcasts || [];
messages.push({
messageId,
message: input.message,
priority: input.priority || 'normal',
fromId: input.fromId || 'system',
timestamp: new Date().toISOString(),
});
// Keep only last 100 broadcasts
state.sharedMemory.broadcasts = messages.slice(-100);
saveHiveState(state);
return {
success: true,
messageId,
recipients: state.workers.length,
priority: input.priority || 'normal',
broadcastAt: new Date().toISOString(),
};
},
},
{
name: 'hive-mind_shutdown',
description: 'Shutdown the hive-mind and terminate all workers',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
graceful: { type: 'boolean', description: 'Graceful shutdown (wait for pending tasks)', default: true },
force: { type: 'boolean', description: 'Force immediate shutdown', default: false },
},
},
handler: async (input) => {
const state = loadHiveState();
if (!state.initialized) {
return { success: false, error: 'Hive-mind not initialized or already shut down' };
}
const graceful = input.graceful !== false;
const force = input.force === true;
const workerCount = state.workers.length;
const pendingConsensus = state.consensus.pending.length;
// If graceful and there are pending consensus items, warn (unless forced)
if (graceful && pendingConsensus > 0 && !force) {
return {
success: false,
error: `Cannot gracefully shutdown with ${pendingConsensus} pending consensus items. Use force: true to override.`,
pendingConsensus,
workerCount,
};
}
// Clear workers from agent store
const agentStore = loadAgentStore();
for (const workerId of state.workers) {
if (agentStore.agents[workerId]) {
delete agentStore.agents[workerId];
}
}
saveAgentStore(agentStore);
// Reset hive state
const shutdownTime = new Date().toISOString();
const previousQueen = state.queen?.agentId;
state.initialized = false;
state.queen = undefined;
state.workers = [];
state.consensus.pending = [];
// Keep history for reference
state.sharedMemory = {};
saveHiveState(state);
return {
success: true,
shutdownAt: shutdownTime,
graceful,
workersTerminated: workerCount,
previousQueen,
consensusCleared: pendingConsensus,
message: `Hive-mind shutdown complete. ${workerCount} workers terminated.`,
};
},
},
{
name: 'hive-mind_memory',
description: 'Access hive shared memory',
category: 'hive-mind',
inputSchema: {
type: 'object',
properties: {
action: { type: 'string', enum: ['get', 'set', 'delete', 'list'], description: 'Memory action' },
key: { type: 'string', description: 'Memory key' },
value: { description: 'Value to store (for set)' },
},
required: ['action'],
},
handler: async (input) => {
const state = loadHiveState();
const action = input.action;
const key = input.key;
if (action === 'get') {
if (!key)
return { action, error: 'Key required' };
return {
action,
key,
value: state.sharedMemory[key],
exists: key in state.sharedMemory,
};
}
if (action === 'set') {
if (!key)
return { action, error: 'Key required' };
state.sharedMemory[key] = input.value;
saveHiveState(state);
return {
action,
key,
success: true,
updatedAt: new Date().toISOString(),
};
}
if (action === 'delete') {
if (!key)
return { action, error: 'Key required' };
const existed = key in state.sharedMemory;
delete state.sharedMemory[key];
saveHiveState(state);
return {
action,
key,
deleted: existed,
};
}
if (action === 'list') {
return {
action,
keys: Object.keys(state.sharedMemory),
count: Object.keys(state.sharedMemory).length,
};
}
return { action, error: 'Unknown action' };
},
},
];
//# sourceMappingURL=hive-mind-tools.js.map