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
315 lines • 11.6 kB
JavaScript
/**
* Session MCP Tools for CLI
*
* Tool definitions for session management with file persistence.
*/
import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync, statSync } from 'node:fs';
import { join } from 'node:path';
// Storage paths
const STORAGE_DIR = '.claude-flow';
const SESSION_DIR = 'sessions';
function getSessionDir() {
return join(process.cwd(), STORAGE_DIR, SESSION_DIR);
}
function getSessionPath(sessionId) {
// Sanitize sessionId to prevent path traversal
const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
return join(getSessionDir(), `${safeId}.json`);
}
function ensureSessionDir() {
const dir = getSessionDir();
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
function loadSession(sessionId) {
try {
const path = getSessionPath(sessionId);
if (existsSync(path)) {
const data = readFileSync(path, 'utf-8');
return JSON.parse(data);
}
}
catch {
// Return null on error
}
return null;
}
function saveSession(session) {
ensureSessionDir();
writeFileSync(getSessionPath(session.sessionId), JSON.stringify(session, null, 2), 'utf-8');
}
function listSessions() {
ensureSessionDir();
const dir = getSessionDir();
const files = readdirSync(dir).filter(f => f.endsWith('.json'));
const sessions = [];
for (const file of files) {
try {
const data = readFileSync(join(dir, file), 'utf-8');
sessions.push(JSON.parse(data));
}
catch {
// Skip invalid files
}
}
return sessions;
}
// Load related stores for session data
function loadRelatedStores(options) {
const data = {};
if (options.includeMemory) {
try {
const memoryPath = join(process.cwd(), STORAGE_DIR, 'memory', 'store.json');
if (existsSync(memoryPath)) {
data.memory = JSON.parse(readFileSync(memoryPath, 'utf-8'));
}
}
catch { /* ignore */ }
}
if (options.includeTasks) {
try {
const taskPath = join(process.cwd(), STORAGE_DIR, 'tasks', 'store.json');
if (existsSync(taskPath)) {
data.tasks = JSON.parse(readFileSync(taskPath, 'utf-8'));
}
}
catch { /* ignore */ }
}
if (options.includeAgents) {
try {
const agentPath = join(process.cwd(), STORAGE_DIR, 'agents', 'store.json');
if (existsSync(agentPath)) {
data.agents = JSON.parse(readFileSync(agentPath, 'utf-8'));
}
}
catch { /* ignore */ }
}
return data;
}
export const sessionTools = [
{
name: 'session_save',
description: 'Save current session state',
category: 'session',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Session name' },
description: { type: 'string', description: 'Session description' },
includeMemory: { type: 'boolean', description: 'Include memory in session' },
includeTasks: { type: 'boolean', description: 'Include tasks in session' },
includeAgents: { type: 'boolean', description: 'Include agents in session' },
},
required: ['name'],
},
handler: async (input) => {
const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
// Load related data based on options
const data = loadRelatedStores({
includeMemory: input.includeMemory,
includeTasks: input.includeTasks,
includeAgents: input.includeAgents,
});
// Calculate stats
const stats = {
tasks: data.tasks ? Object.keys(data.tasks.tasks || {}).length : 0,
agents: data.agents ? Object.keys(data.agents.agents || {}).length : 0,
memoryEntries: data.memory ? Object.keys(data.memory.entries || {}).length : 0,
totalSize: 0,
};
const session = {
sessionId,
name: input.name,
description: input.description,
savedAt: new Date().toISOString(),
stats,
data: Object.keys(data).length > 0 ? data : undefined,
};
// Calculate size
const sessionJson = JSON.stringify(session);
session.stats.totalSize = Buffer.byteLength(sessionJson, 'utf-8');
saveSession(session);
return {
sessionId,
name: session.name,
savedAt: session.savedAt,
stats: session.stats,
path: getSessionPath(sessionId),
};
},
},
{
name: 'session_restore',
description: 'Restore a saved session',
category: 'session',
inputSchema: {
type: 'object',
properties: {
sessionId: { type: 'string', description: 'Session ID to restore' },
name: { type: 'string', description: 'Session name to restore' },
},
},
handler: async (input) => {
let session = null;
// Try to find by sessionId first
if (input.sessionId) {
session = loadSession(input.sessionId);
}
// Try to find by name if sessionId not found
if (!session && input.name) {
const sessions = listSessions();
session = sessions.find(s => s.name === input.name) || null;
}
// Try to find latest if no params
if (!session && !input.sessionId && !input.name) {
const sessions = listSessions();
if (sessions.length > 0) {
sessions.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
session = sessions[0];
}
}
if (session) {
// Restore data to respective stores
if (session.data?.memory) {
const memoryDir = join(process.cwd(), STORAGE_DIR, 'memory');
if (!existsSync(memoryDir))
mkdirSync(memoryDir, { recursive: true });
writeFileSync(join(memoryDir, 'store.json'), JSON.stringify(session.data.memory, null, 2), 'utf-8');
}
if (session.data?.tasks) {
const taskDir = join(process.cwd(), STORAGE_DIR, 'tasks');
if (!existsSync(taskDir))
mkdirSync(taskDir, { recursive: true });
writeFileSync(join(taskDir, 'store.json'), JSON.stringify(session.data.tasks, null, 2), 'utf-8');
}
if (session.data?.agents) {
const agentDir = join(process.cwd(), STORAGE_DIR, 'agents');
if (!existsSync(agentDir))
mkdirSync(agentDir, { recursive: true });
writeFileSync(join(agentDir, 'store.json'), JSON.stringify(session.data.agents, null, 2), 'utf-8');
}
return {
sessionId: session.sessionId,
name: session.name,
restored: true,
restoredAt: new Date().toISOString(),
stats: session.stats,
};
}
return {
sessionId: input.sessionId || input.name || 'latest',
restored: false,
error: 'Session not found',
};
},
},
{
name: 'session_list',
description: 'List saved sessions',
category: 'session',
inputSchema: {
type: 'object',
properties: {
limit: { type: 'number', description: 'Maximum sessions to return' },
sortBy: { type: 'string', description: 'Sort field (date, name, size)' },
},
},
handler: async (input) => {
let sessions = listSessions();
// Sort
const sortBy = input.sortBy || 'date';
if (sortBy === 'date') {
sessions.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
}
else if (sortBy === 'name') {
sessions.sort((a, b) => a.name.localeCompare(b.name));
}
else if (sortBy === 'size') {
sessions.sort((a, b) => b.stats.totalSize - a.stats.totalSize);
}
// Apply limit
const limit = input.limit || 10;
sessions = sessions.slice(0, limit);
return {
sessions: sessions.map(s => ({
sessionId: s.sessionId,
name: s.name,
description: s.description,
savedAt: s.savedAt,
stats: s.stats,
})),
total: sessions.length,
limit,
};
},
},
{
name: 'session_delete',
description: 'Delete a saved session',
category: 'session',
inputSchema: {
type: 'object',
properties: {
sessionId: { type: 'string', description: 'Session ID to delete' },
},
required: ['sessionId'],
},
handler: async (input) => {
const sessionId = input.sessionId;
const path = getSessionPath(sessionId);
if (existsSync(path)) {
unlinkSync(path);
return {
sessionId,
deleted: true,
deletedAt: new Date().toISOString(),
};
}
return {
sessionId,
deleted: false,
error: 'Session not found',
};
},
},
{
name: 'session_info',
description: 'Get detailed session information',
category: 'session',
inputSchema: {
type: 'object',
properties: {
sessionId: { type: 'string', description: 'Session ID' },
},
required: ['sessionId'],
},
handler: async (input) => {
const sessionId = input.sessionId;
const session = loadSession(sessionId);
if (session) {
const path = getSessionPath(sessionId);
const stat = statSync(path);
return {
sessionId: session.sessionId,
name: session.name,
description: session.description,
savedAt: session.savedAt,
stats: session.stats,
fileSize: stat.size,
path,
hasData: {
memory: !!session.data?.memory,
tasks: !!session.data?.tasks,
agents: !!session.data?.agents,
},
};
}
return {
sessionId,
error: 'Session not found',
};
},
},
];
//# sourceMappingURL=session-tools.js.map