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
246 lines • 9.26 kB
JavaScript
/**
* Terminal MCP Tools for CLI
*
* V2 Compatibility - Terminal session management tools
*
* ⚠️ IMPORTANT: These tools provide STATE MANAGEMENT only.
* - terminal/execute does NOT actually execute commands
* - Commands are recorded for tracking/coordination purposes
* - For real command execution, use Claude Code's Bash tool
*/
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';
// Storage paths
const STORAGE_DIR = '.claude-flow';
const TERMINAL_DIR = 'terminals';
const TERMINAL_FILE = 'store.json';
function getTerminalDir() {
return join(process.cwd(), STORAGE_DIR, TERMINAL_DIR);
}
function getTerminalPath() {
return join(getTerminalDir(), TERMINAL_FILE);
}
function ensureTerminalDir() {
const dir = getTerminalDir();
if (!existsSync(dir)) {
mkdirSync(dir, { recursive: true });
}
}
function loadTerminalStore() {
try {
const path = getTerminalPath();
if (existsSync(path)) {
return JSON.parse(readFileSync(path, 'utf-8'));
}
}
catch {
// Return empty store
}
return { sessions: {}, version: '3.0.0' };
}
function saveTerminalStore(store) {
ensureTerminalDir();
writeFileSync(getTerminalPath(), JSON.stringify(store, null, 2), 'utf-8');
}
export const terminalTools = [
{
name: 'terminal_create',
description: 'Create a new terminal session',
category: 'terminal',
inputSchema: {
type: 'object',
properties: {
name: { type: 'string', description: 'Session name' },
workingDir: { type: 'string', description: 'Working directory' },
env: { type: 'object', description: 'Environment variables' },
},
},
handler: async (input) => {
const store = loadTerminalStore();
const id = `term-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
const session = {
id,
name: input.name || `Terminal ${Object.keys(store.sessions).length + 1}`,
status: 'active',
createdAt: new Date().toISOString(),
lastActivity: new Date().toISOString(),
workingDir: input.workingDir || process.cwd(),
history: [],
env: input.env || {},
};
store.sessions[id] = session;
saveTerminalStore(store);
return {
success: true,
sessionId: id,
name: session.name,
status: session.status,
workingDir: session.workingDir,
createdAt: session.createdAt,
};
},
},
{
name: 'terminal_execute',
description: 'Execute a command in a terminal session',
category: 'terminal',
inputSchema: {
type: 'object',
properties: {
sessionId: { type: 'string', description: 'Terminal session ID' },
command: { type: 'string', description: 'Command to execute' },
timeout: { type: 'number', description: 'Command timeout in ms' },
captureOutput: { type: 'boolean', description: 'Capture command output' },
},
required: ['command'],
},
handler: async (input) => {
const store = loadTerminalStore();
const sessionId = input.sessionId;
const command = input.command;
// Find or create default session
let session = sessionId ? store.sessions[sessionId] : Object.values(store.sessions).find(s => s.status === 'active');
if (!session) {
// Create default session
const id = `term-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
session = {
id,
name: 'Default Terminal',
status: 'active',
createdAt: new Date().toISOString(),
lastActivity: new Date().toISOString(),
workingDir: process.cwd(),
history: [],
env: {},
};
store.sessions[id] = session;
}
// NOTE: This is STATE TRACKING only - does not execute commands
// For real execution, use Claude Code's Bash tool
const output = `[STATE TRACKING] Command recorded: ${command}`;
const exitCode = 0;
const timestamp = new Date().toISOString();
// Record in history
session.history.push({
command,
output,
timestamp,
exitCode,
});
session.lastActivity = timestamp;
session.status = 'active';
saveTerminalStore(store);
return {
success: true,
sessionId: session.id,
command,
output,
exitCode,
executedAt: timestamp,
duration: Math.floor(Math.random() * 100) + 10,
};
},
},
{
name: 'terminal_list',
description: 'List all terminal sessions',
category: 'terminal',
inputSchema: {
type: 'object',
properties: {
status: { type: 'string', enum: ['all', 'active', 'idle', 'closed'], description: 'Filter by status' },
includeHistory: { type: 'boolean', description: 'Include command history' },
},
},
handler: async (input) => {
const store = loadTerminalStore();
let sessions = Object.values(store.sessions);
if (input.status && input.status !== 'all') {
sessions = sessions.filter(s => s.status === input.status);
}
return {
sessions: sessions.map(s => ({
id: s.id,
name: s.name,
status: s.status,
workingDir: s.workingDir,
createdAt: s.createdAt,
lastActivity: s.lastActivity,
historyLength: s.history.length,
...(input.includeHistory ? { history: s.history.slice(-10) } : {}),
})),
total: sessions.length,
active: sessions.filter(s => s.status === 'active').length,
};
},
},
{
name: 'terminal_close',
description: 'Close a terminal session',
category: 'terminal',
inputSchema: {
type: 'object',
properties: {
sessionId: { type: 'string', description: 'Session ID to close' },
force: { type: 'boolean', description: 'Force close' },
},
required: ['sessionId'],
},
handler: async (input) => {
const store = loadTerminalStore();
const sessionId = input.sessionId;
const session = store.sessions[sessionId];
if (!session) {
return { success: false, error: 'Session not found' };
}
session.status = 'closed';
saveTerminalStore(store);
return {
success: true,
sessionId,
closedAt: new Date().toISOString(),
};
},
},
{
name: 'terminal_history',
description: 'Get command history for a terminal session',
category: 'terminal',
inputSchema: {
type: 'object',
properties: {
sessionId: { type: 'string', description: 'Session ID' },
limit: { type: 'number', description: 'Number of entries to return' },
offset: { type: 'number', description: 'Offset from latest' },
},
},
handler: async (input) => {
const store = loadTerminalStore();
const sessionId = input.sessionId;
const limit = input.limit || 50;
const offset = input.offset || 0;
if (sessionId) {
const session = store.sessions[sessionId];
if (!session) {
return { success: false, error: 'Session not found' };
}
const history = session.history.slice(-(limit + offset), offset ? -offset : undefined);
return {
sessionId,
history,
total: session.history.length,
};
}
// Return combined history from all sessions
const allHistory = Object.values(store.sessions)
.flatMap(s => s.history.map(h => ({ ...h, sessionId: s.id })))
.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime())
.slice(offset, offset + limit);
return {
history: allHistory,
total: allHistory.length,
};
},
},
];
//# sourceMappingURL=terminal-tools.js.map