@stackmemoryai/stackmemory
Version:
Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.
649 lines (647 loc) • 19.4 kB
JavaScript
import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import blessed from "blessed";
import { logger } from "../../core/monitoring/logger.js";
import { SwarmDashboard } from "../../integrations/ralph/monitoring/swarm-dashboard.js";
import { SwarmRegistry } from "../../integrations/ralph/monitoring/swarm-registry.js";
import { execSync } from "child_process";
class SwarmTUI {
screen;
commitsTable;
statusBox;
agentsTable;
metricsBox;
logBox;
swarmCoordinator = null;
swarmDashboard = null;
refreshInterval = null;
commitMetrics = /* @__PURE__ */ new Map();
constructor() {
const isGhostty = process.env.TERM_PROGRAM === "ghostty" || process.env.TERM?.includes("ghostty");
const isBasicTerm = process.env.TERM === "dumb" || process.env.TERM === "unknown";
this.screen = blessed.screen({
smartCSR: !isGhostty,
// Disable smart CSR for ghostty
title: "Ralph Swarm Monitor",
terminal: isGhostty ? "xterm-256color" : void 0,
fullUnicode: !isBasicTerm,
dockBorders: false,
ignoreDockContrast: true,
useBCE: false,
// Disable background color erase for compatibility
forceUnicode: false,
debug: false
});
this.screen.on("error", (err) => {
logger.error("TUI screen error:", err);
console.log(
"\u26A0\uFE0F TUI display error detected. Try setting TERM=xterm-256color"
);
console.log(
" Alternative: Use stackmemory ralph status for text-based monitoring"
);
});
this.createUI();
this.setupKeyHandlers();
logger.info("SwarmTUI initialized");
}
createUI() {
const container = blessed.box({
parent: this.screen,
top: 0,
left: 0,
width: "100%",
height: "100%",
style: {
bg: "black"
}
});
const isGhostty = process.env.TERM_PROGRAM === "ghostty" || process.env.TERM?.includes("ghostty");
const safeColors = isGhostty;
blessed.box({
parent: container,
top: 0,
left: 0,
width: "100%",
height: 3,
content: "\u{1F9BE} Ralph Swarm Monitor - Real-time Swarm Operations",
style: safeColors ? {
fg: "white",
bold: false
} : {
bg: "blue",
fg: "white",
bold: true
},
border: {
type: "line"
}
});
this.statusBox = blessed.box({
parent: container,
top: 3,
left: "50%",
width: "50%",
height: 8,
label: " Swarm Status ",
content: "No active swarm",
style: {
bg: "black",
fg: "white"
},
border: {
type: "line",
fg: "cyan"
}
});
this.metricsBox = blessed.box({
parent: container,
top: 3,
left: 0,
width: "50%",
height: 8,
label: " Performance Metrics ",
content: "Waiting for data...",
style: {
bg: "black",
fg: "white"
},
border: {
type: "line",
fg: "green"
}
});
this.agentsTable = blessed.table({
parent: container,
top: 11,
left: 0,
width: "50%",
height: 12,
label: " Active Agents ",
style: {
bg: "black",
fg: "white",
header: {
bg: "blue",
fg: "white",
bold: true
},
cell: {
selected: {
bg: "blue",
fg: "white"
}
}
},
border: {
type: "line",
fg: "yellow"
},
data: [["Role", "Status", "Iteration", "Task", "Last Active"]]
});
this.commitsTable = blessed.table({
parent: container,
top: 11,
left: "50%",
width: "50%",
height: 12,
label: " Recent Commits ",
style: {
bg: "black",
fg: "white",
header: {
bg: "blue",
fg: "white",
bold: true
},
cell: {
selected: {
bg: "blue",
fg: "white"
}
}
},
border: {
type: "line",
fg: "magenta"
},
data: [["Agent", "Message", "Lines +/-", "Time"]]
});
this.logBox = blessed.log({
parent: container,
top: 23,
left: 0,
width: "100%",
height: "100%-23",
label: " Swarm Logs ",
style: {
bg: "black",
fg: "white"
},
border: {
type: "line",
fg: "white"
},
scrollable: true,
alwaysScroll: true,
mouse: true
});
blessed.box({
parent: container,
bottom: 0,
left: 0,
width: "100%",
height: 1,
content: "q=quit | r=refresh | s=start swarm | t=stop swarm | h=help | c=clear logs | d=detect swarms",
style: {
bg: "white",
fg: "black"
}
});
}
setupKeyHandlers() {
this.screen.key(["escape", "q", "C-c"], () => {
this.cleanup();
process.exit(0);
});
this.screen.key(["r"], () => {
this.refreshData();
this.logBox.log("Manual refresh triggered");
});
this.screen.key(["s"], () => {
this.startSwarmInteractive();
});
this.screen.key(["t"], () => {
this.stopSwarmInteractive();
});
this.screen.key(["h"], () => {
this.showHelp();
});
this.screen.key(["c"], () => {
this.clearLogs();
});
this.screen.key(["d"], () => {
this.showDetectedSwarms();
});
}
/**
* Initialize swarm monitoring
*/
async initialize(swarmCoordinator, swarmId) {
try {
if (swarmId) {
const registry = SwarmRegistry.getInstance();
const swarm = registry.getSwarm(swarmId);
if (swarm) {
this.swarmCoordinator = swarm.coordinator;
this.logBox.log(`Connected to swarm: ${swarmId}`);
} else {
this.logBox.log(`Swarm not found: ${swarmId}`);
}
} else if (swarmCoordinator) {
this.swarmCoordinator = swarmCoordinator;
} else {
const registry = SwarmRegistry.getInstance();
const activeSwarms = registry.listActiveSwarms();
if (activeSwarms.length > 0) {
this.swarmCoordinator = activeSwarms[0].coordinator;
this.logBox.log(`Auto-connected to swarm: ${activeSwarms[0].id}`);
}
}
if (this.swarmCoordinator) {
this.swarmDashboard = new SwarmDashboard(this.swarmCoordinator);
this.swarmDashboard.startMonitoring(2e3);
this.swarmDashboard.on("metricsUpdated", (metrics) => {
this.updateUI(metrics);
});
}
this.refreshInterval = setInterval(() => {
this.refreshData();
}, 3e3);
this.logBox.log("SwarmTUI monitoring initialized");
} catch (error) {
logger.error("Failed to initialize SwarmTUI", error);
this.logBox.log(`Error: ${error.message}`);
}
}
/**
* Start the TUI display
*/
start() {
this.screen.render();
this.logBox.log("Ralph Swarm Monitor started");
this.logBox.log("Monitoring for active swarms...");
}
/**
* Refresh all data
*/
async refreshData() {
try {
await this.updateCommitMetrics();
if (this.swarmCoordinator) {
const status = this.getSwarmStatus();
this.updateStatusDisplay(status);
} else {
await this.detectActiveSwarms();
}
this.screen.render();
} catch (error) {
logger.error("Failed to refresh TUI data", error);
this.logBox.log(`Refresh error: ${error.message}`);
}
}
/**
* Update commit metrics for all agents
*/
async updateCommitMetrics() {
try {
const gitLog = execSync(
'git log --oneline --since="1 hour ago" --pretty=format:"%H|%an|%s|%ct" --numstat',
{ encoding: "utf8", cwd: process.cwd() }
);
const commits = this.parseGitCommits(gitLog);
this.updateCommitsTable(commits);
} catch {
this.logBox.log("No recent commits found");
}
}
/**
* Parse git log output into commit data
*/
parseGitCommits(gitLog) {
const commits = [];
const lines = gitLog.split("\n").filter(Boolean);
let currentCommit = null;
for (const line of lines) {
if (line.includes("|")) {
const [hash, author, message, timestamp] = line.split("|");
currentCommit = {
hash: hash.substring(0, 8),
agent: this.extractAgentFromAuthor(author),
message: message.substring(0, 50),
timestamp: parseInt(timestamp),
linesAdded: 0,
linesDeleted: 0
};
} else if (currentCommit && line.match(/^\d+\s+\d+/)) {
const [added, deleted] = line.split(" ")[0].split(" ");
currentCommit.linesAdded += parseInt(added) || 0;
currentCommit.linesDeleted += parseInt(deleted) || 0;
commits.push({ ...currentCommit });
currentCommit = null;
}
}
return commits.slice(0, 10);
}
/**
* Extract agent info from git author
*/
extractAgentFromAuthor(author) {
const agentMatch = author.match(/\[(\w+)\]/);
if (agentMatch) {
return agentMatch[1];
}
const roles = [
"developer",
"tester",
"optimizer",
"documenter",
"architect"
];
for (const role of roles) {
if (author.toLowerCase().includes(role)) {
return role;
}
}
return "user";
}
/**
* Update commits table display
*/
updateCommitsTable(commits) {
const tableData = [["Agent", "Message", "Lines +/-", "Time"]];
for (const commit of commits) {
const timeAgo = this.formatTimeAgo(commit.timestamp * 1e3);
const linesChange = `+${commit.linesAdded}/-${commit.linesDeleted}`;
tableData.push([commit.agent, commit.message, linesChange, timeAgo]);
}
this.commitsTable.setData(tableData);
}
/**
* Get current swarm status
*/
getSwarmStatus() {
if (!this.swarmCoordinator) return null;
const usage = this.swarmCoordinator.getResourceUsage();
const swarmState = this.swarmCoordinator.swarmState;
if (!swarmState) return null;
return {
swarmId: swarmState.id,
status: swarmState.status,
startTime: swarmState.startTime,
uptime: Date.now() - swarmState.startTime,
agents: usage.activeAgents ? Array.from(
this.swarmCoordinator.activeAgents?.values() || []
).map((agent) => ({
id: agent.id,
role: agent.role,
status: agent.status,
currentTask: agent.currentTask,
iteration: agent.performance?.tasksCompleted || 0,
lastActivity: agent.performance?.lastFreshStart || Date.now()
})) : [],
performance: {
throughput: swarmState.performance?.throughput || 0,
efficiency: swarmState.performance?.efficiency || 0,
totalTasks: swarmState.totalTaskCount || 0,
completedTasks: swarmState.completedTaskCount || 0
}
};
}
/**
* Update status display
*/
updateStatusDisplay(status) {
if (!status) {
this.statusBox.setContent("No active swarm detected");
this.agentsTable.setData([
["Role", "Status", "Iteration", "Task", "Last Active"]
]);
this.metricsBox.setContent("Waiting for swarm data...");
return;
}
const uptimeStr = this.formatDuration(status.uptime);
const statusContent = `Swarm: ${status.swarmId.substring(0, 8)}
Status: ${status.status.toUpperCase()}
Uptime: ${uptimeStr}
Agents: ${status.agents.length}`;
this.statusBox.setContent(statusContent);
const agentData = [["Role", "Status", "Iteration", "Task", "Last Active"]];
for (const agent of status.agents) {
const lastActivity = this.formatTimeAgo(agent.lastActivity);
const task = agent.currentTask ? agent.currentTask.substring(0, 20) : "idle";
agentData.push([
agent.role,
agent.status,
agent.iteration.toString(),
task,
lastActivity
]);
}
this.agentsTable.setData(agentData);
const metricsContent = `Throughput: ${status.performance.throughput.toFixed(2)} tasks/min
Efficiency: ${(status.performance.efficiency * 100).toFixed(1)}%
Tasks: ${status.performance.completedTasks}/${status.performance.totalTasks}
Success Rate: ${status.performance.efficiency > 0 ? (status.performance.efficiency * 100).toFixed(1) : "N/A"}%`;
this.metricsBox.setContent(metricsContent);
}
/**
* Update UI with metrics from dashboard
*/
updateUI(metrics) {
this.logBox.log(
`Metrics updated: ${metrics.status} - ${metrics.activeAgents} agents`
);
}
/**
* Detect active swarms in the system
*/
async detectActiveSwarms() {
try {
const registry = SwarmRegistry.getInstance();
const activeSwarms = registry.listActiveSwarms();
const stats = registry.getStatistics();
if (activeSwarms.length > 0) {
let statusContent = `Available Swarms (${activeSwarms.length}):
`;
for (const swarm of activeSwarms.slice(0, 3)) {
const uptime = this.formatDuration(Date.now() - swarm.startTime);
statusContent += `\u2022 ${swarm.id.substring(0, 8)}: ${swarm.status} (${uptime})
`;
}
if (activeSwarms.length > 3) {
statusContent += `... and ${activeSwarms.length - 3} more`;
}
this.statusBox.setContent(statusContent);
this.logBox.log(
`Found ${activeSwarms.length} active swarms in registry`
);
} else {
const ralphProcesses = execSync(
'ps aux | grep "ralph" | grep -v grep',
{ encoding: "utf8" }
);
if (ralphProcesses.trim()) {
this.logBox.log("Detected Ralph processes running");
this.statusBox.setContent(
"External Ralph processes detected\n(Use swarm coordinator for full monitoring)"
);
} else {
this.statusBox.setContent(`No active swarms detected
Total swarms: ${stats.totalSwarms}
Completed: ${stats.completedSwarms}
Run: stackmemory ralph swarm <task>`);
}
}
} catch {
}
}
/**
* Format time ago string
*/
formatTimeAgo(timestamp) {
const diff = Date.now() - timestamp;
const minutes = Math.floor(diff / 6e4);
const hours = Math.floor(minutes / 60);
const days = Math.floor(hours / 24);
if (days > 0) return `${days}d ago`;
if (hours > 0) return `${hours}h ago`;
if (minutes > 0) return `${minutes}m ago`;
return "just now";
}
/**
* Format duration string
*/
formatDuration(ms) {
const seconds = Math.floor(ms / 1e3);
const minutes = Math.floor(seconds / 60);
const hours = Math.floor(minutes / 60);
if (hours > 0) return `${hours}h ${minutes % 60}m`;
if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
return `${seconds}s`;
}
/**
* Start swarm interactively
*/
startSwarmInteractive() {
this.logBox.log("\u{1F680} Start Swarm Interactive Mode:");
this.logBox.log(
'Example: stackmemory ralph swarm "Implement feature" --agents developer,tester'
);
this.logBox.log(
'Tip: Run the command in another terminal, then press "d" to detect it'
);
}
/**
* Stop swarm interactively
*/
stopSwarmInteractive() {
if (this.swarmCoordinator) {
this.logBox.log("\u{1F6D1} Stopping current swarm...");
this.logBox.log(
"Note: Swarm stopping not yet implemented - use Ctrl+C in swarm terminal"
);
} else {
this.logBox.log("\u274C No active swarm coordinator to stop");
this.logBox.log("External Ralph processes must be stopped manually");
}
}
/**
* Show help dialog
*/
showHelp() {
this.logBox.log("\u{1F9BE} Ralph Swarm Monitor - Help");
this.logBox.log("");
this.logBox.log("Keyboard Shortcuts:");
this.logBox.log(" q, Esc, Ctrl+C - Quit TUI");
this.logBox.log(" r - Refresh data manually");
this.logBox.log(" s - Show start swarm commands");
this.logBox.log(" t - Stop current swarm");
this.logBox.log(" h - Show this help");
this.logBox.log(" c - Clear log output");
this.logBox.log(" d - Detect and list available swarms");
this.logBox.log("");
this.logBox.log("Usage:");
this.logBox.log(
" stackmemory ralph tui # Auto-detect swarms"
);
this.logBox.log(
" stackmemory ralph tui --swarm-id <id> # Monitor specific swarm"
);
this.logBox.log("");
this.logBox.log("Starting Swarms:");
this.logBox.log(
' stackmemory ralph swarm "Task description" --agents developer,tester'
);
this.logBox.log("");
}
/**
* Clear log output
*/
clearLogs() {
this.logBox.setContent("");
this.logBox.log("\u{1F4DD} Logs cleared - monitoring continues...");
}
/**
* Show detected swarms
*/
async showDetectedSwarms() {
this.logBox.log("\u{1F50D} Detecting active swarms...");
try {
const registry = SwarmRegistry.getInstance();
const activeSwarms = registry.listActiveSwarms();
const stats = registry.getStatistics();
this.logBox.log("");
this.logBox.log("\u{1F4CA} Swarm Registry Status:");
this.logBox.log(` Total swarms: ${stats.totalSwarms}`);
this.logBox.log(` Active swarms: ${stats.activeSwarms}`);
this.logBox.log(` Completed swarms: ${stats.completedSwarms}`);
if (activeSwarms.length > 0) {
this.logBox.log("");
this.logBox.log("\u{1F9BE} Active Swarms:");
for (const swarm of activeSwarms) {
const uptime = this.formatDuration(Date.now() - swarm.startTime);
this.logBox.log(` \u2022 ${swarm.id}: ${swarm.description} (${uptime})`);
}
this.logBox.log("");
this.logBox.log("\u{1F4A1} Use --swarm-id to connect to specific swarm");
} else {
this.logBox.log("");
this.logBox.log("\u274C No active swarms in registry");
try {
const ralphProcesses = execSync(
'ps aux | grep "ralph" | grep -v grep',
{ encoding: "utf8" }
);
if (ralphProcesses.trim()) {
this.logBox.log("\u{1F50D} External Ralph processes detected:");
ralphProcesses.split("\n").filter((line) => line.trim()).forEach((line) => {
const parts = line.split(/\s+/);
this.logBox.log(
` PID ${parts[1]}: ${parts.slice(10).join(" ").slice(0, 60)}`
);
});
}
} catch {
this.logBox.log("\u{1F50D} No external Ralph processes found");
}
this.logBox.log("");
this.logBox.log(
'\u{1F4A1} Start a swarm: stackmemory ralph swarm "Task" --agents developer'
);
}
} catch (error) {
this.logBox.log(`\u274C Detection failed: ${error.message}`);
}
}
/**
* Cleanup resources
*/
cleanup() {
if (this.refreshInterval) {
clearInterval(this.refreshInterval);
}
if (this.swarmDashboard) {
this.swarmDashboard.stopMonitoring();
}
logger.info("SwarmTUI cleaned up");
}
}
var swarm_monitor_default = SwarmTUI;
export {
SwarmTUI,
swarm_monitor_default as default
};
//# sourceMappingURL=swarm-monitor.js.map