@stackmemoryai/stackmemory
Version:
Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, a
1,054 lines (1,052 loc) • 45.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 { Command } from "commander";
import { logger } from "../../core/monitoring/logger.js";
import { RalphLoop } from "../../../scripts/ralph-loop-implementation.js";
import { stackMemoryContextLoader } from "../../integrations/ralph/context/stackmemory-context-loader.js";
import { patternLearner } from "../../integrations/ralph/learning/pattern-learner.js";
import { multiLoopOrchestrator } from "../../integrations/ralph/orchestration/multi-loop-orchestrator.js";
import { swarmCoordinator } from "../../integrations/ralph/swarm/swarm-coordinator.js";
import { ralphDebugger } from "../../integrations/ralph/visualization/ralph-debugger.js";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { trace } from "../../core/trace/index.js";
import { SystemError, ErrorCode } from "../../core/errors/index.js";
import { LoopMaxRunner } from "../../integrations/ralph/loopmax.js";
function createRalphCommand() {
const ralph = new Command("ralph").description(
"Ralph Wiggum Loop integration with StackMemory"
);
ralph.command("init").description("Initialize a new Ralph Wiggum loop").argument("<task>", "Task description").option(
"-c, --criteria <criteria>",
"Completion criteria (comma separated)"
).option("--max-iterations <n>", "Maximum iterations", "50").option("--use-context", "Load relevant context from StackMemory").option(
"--learn-from-similar",
"Apply patterns from similar completed tasks"
).action(async (task, options) => {
return trace.command("ralph-init", { task, ...options }, async () => {
try {
console.log("\u{1F3AD} Initializing Ralph Wiggum loop...");
const loop = new RalphLoop({
baseDir: ".ralph",
maxIterations: parseInt(options.maxIterations),
verbose: true
});
const criteria = options.criteria ? options.criteria.split(",").map((c) => `- ${c.trim()}`).join("\n") : "- All tests pass\n- Code works correctly\n- No lint errors";
let enhancedTask = task;
if (options.useContext || options.learnFromSimilar) {
try {
await stackMemoryContextLoader.initialize();
const contextResponse = await stackMemoryContextLoader.loadInitialContext({
task,
usePatterns: true,
useSimilarTasks: options.learnFromSimilar,
maxTokens: 3e3
});
if (contextResponse.context) {
enhancedTask = `${task}
${contextResponse.context}`;
console.log(
`\u{1F4DA} Loaded context from ${contextResponse.sources.length} sources`
);
console.log(
`\u{1F3AF} Context tokens: ${contextResponse.metadata.totalTokens}`
);
}
} catch (error) {
console.log(
`\u26A0\uFE0F Context loading failed: ${error.message}`
);
console.log("Proceeding without context...");
}
}
await loop.initialize(enhancedTask, criteria);
console.log("\u2705 Ralph loop initialized!");
console.log(`\u{1F4CB} Task: ${task}`);
console.log(`\u{1F3AF} Max iterations: ${options.maxIterations}`);
console.log(`\u{1F4C1} Loop directory: .ralph/`);
console.log("\nNext steps:");
console.log(" stackmemory ralph run # Start the loop");
console.log(" stackmemory ralph status # Check status");
} catch (error) {
logger.error("Failed to initialize Ralph loop", error);
console.error("\u274C Initialization failed:", error.message);
process.exit(1);
}
});
});
ralph.command("run").description("Run the Ralph Wiggum loop").option("--verbose", "Verbose output").option("--pause-on-error", "Pause on validation errors").action(async (options) => {
return trace.command("ralph-run", options, async () => {
try {
if (!existsSync(".ralph")) {
console.error(
'\u274C No Ralph loop found. Run "stackmemory ralph init" first.'
);
return;
}
console.log("\u{1F3AD} Starting Ralph Wiggum loop...");
const loop = new RalphLoop({
baseDir: ".ralph",
verbose: options.verbose
});
await loop.run();
} catch (error) {
logger.error("Failed to run Ralph loop", error);
console.error("\u274C Loop execution failed:", error.message);
process.exit(1);
}
});
});
ralph.command("status").description("Show current Ralph loop status").option("--detailed", "Show detailed iteration history").action(async (options) => {
return trace.command("ralph-status", options, async () => {
try {
if (!existsSync(".ralph")) {
console.log("\u274C No Ralph loop found in current directory");
return;
}
const task = readFileSync(".ralph/task.md", "utf8");
const iteration = parseInt(
readFileSync(".ralph/iteration.txt", "utf8") || "0"
);
const isComplete = existsSync(".ralph/work-complete.txt");
const feedback = existsSync(".ralph/feedback.txt") ? readFileSync(".ralph/feedback.txt", "utf8") : "";
console.log("\u{1F3AD} Ralph Loop Status:");
console.log(` Task: ${task.substring(0, 80)}...`);
console.log(` Iteration: ${iteration}`);
console.log(
` Status: ${isComplete ? "\u2705 COMPLETE" : "\u{1F504} IN PROGRESS"}`
);
if (feedback) {
console.log(` Last feedback: ${feedback.substring(0, 100)}...`);
}
if (options.detailed && existsSync(".ralph/progress.jsonl")) {
console.log("\n\u{1F4CA} Iteration History:");
const progressLines = readFileSync(".ralph/progress.jsonl", "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
progressLines.forEach((p) => {
const progress = p;
const status = progress.validation?.testsPass ? "\u2705" : "\u274C";
console.log(
` ${progress.iteration}: ${status} ${progress.changes} changes, ${progress.errors} errors`
);
});
}
} catch (error) {
logger.error("Failed to get Ralph status", error);
console.error("\u274C Status check failed:", error.message);
}
});
});
ralph.command("resume").description("Resume a crashed or paused Ralph loop").option("--from-stackmemory", "Restore from StackMemory backup").action(async (options) => {
return trace.command("ralph-resume", options, async () => {
try {
console.log("\u{1F504} Resuming Ralph loop...");
const loop = new RalphLoop({ baseDir: ".ralph", verbose: true });
if (options.fromStackmemory) {
console.log("\u{1F4DA} StackMemory restore feature coming soon...");
}
await loop.run();
} catch (error) {
logger.error("Failed to resume Ralph loop", error);
console.error("\u274C Resume failed:", error.message);
process.exit(1);
}
});
});
ralph.command("stop").description("Stop the current Ralph loop").option("--save-progress", "Save current progress to StackMemory").action(async (options) => {
return trace.command("ralph-stop", options, async () => {
try {
if (!existsSync(".ralph")) {
console.log("\u274C No active Ralph loop found");
return;
}
console.log("\u{1F6D1} Stopping Ralph loop...");
if (options.saveProgress) {
console.log("\u{1F4BE} StackMemory progress save feature coming soon...");
}
writeFileSync(".ralph/stop-signal.txt", (/* @__PURE__ */ new Date()).toISOString());
console.log("\u2705 Stop signal sent");
} catch (error) {
logger.error("Failed to stop Ralph loop", error);
console.error("\u274C Stop failed:", error.message);
}
});
});
ralph.command("clean").description("Clean up Ralph loop artifacts").option("--keep-history", "Keep iteration history").action(async (options) => {
return trace.command("ralph-clean", options, async () => {
try {
if (!options.keepHistory && existsSync(".ralph/history")) {
const fs = await import("fs");
fs.rmSync(".ralph/history", { recursive: true, force: true });
}
if (existsSync(".ralph/work-complete.txt")) {
const fs = await import("fs");
fs.unlinkSync(".ralph/work-complete.txt");
}
console.log("\u{1F9F9} Ralph loop artifacts cleaned");
} catch (error) {
logger.error("Failed to clean Ralph artifacts", error);
console.error("\u274C Cleanup failed:", error.message);
}
});
});
ralph.command("debug").description("Debug Ralph loop state and diagnostics").option("--reconcile", "Force state reconciliation").option("--validate-context", "Validate context budget").action(async (options) => {
return trace.command("ralph-debug", options, async () => {
try {
console.log("\u{1F50D} Ralph Loop Debug Information:");
if (options.reconcile) {
console.log("\u{1F527} State reconciliation feature coming soon...");
}
if (options.validateContext) {
console.log("\u{1F4CA} Context validation feature coming soon...");
}
if (existsSync(".ralph")) {
console.log("\n\u{1F4C1} Ralph directory structure:");
const { execSync } = await import("child_process");
try {
const tree = execSync("find .ralph -type f | head -20", {
encoding: "utf8"
});
console.log(tree);
} catch {
console.log(" (Unable to show directory tree)");
}
}
} catch (error) {
logger.error("Ralph debug failed", error);
console.error("\u274C Debug failed:", error.message);
}
});
});
ralph.command("swarm").description("Launch a swarm of specialized agents").argument("<project>", "Project description").option(
"--agents <agents>",
"Comma-separated list of agent roles (architect,developer,tester,etc)",
"developer,tester"
).option("--max-agents <n>", "Maximum number of agents", "5").action(async (project, options) => {
return trace.command("ralph-swarm", { project, ...options }, async () => {
try {
console.log("\u{1F9BE} Launching Ralph swarm...");
await swarmCoordinator.initialize();
const agentRoles = options.agents.split(",").map((r) => r.trim());
const agentSpecs = agentRoles.map((role) => ({
role,
conflictResolution: "defer_to_expertise",
collaborationPreferences: []
}));
const swarmId = await swarmCoordinator.launchSwarm(
project,
agentSpecs
);
console.log(`\u2705 Swarm launched with ID: ${swarmId}`);
console.log(`\u{1F465} ${agentSpecs.length} agents working on: ${project}`);
console.log("\nNext steps:");
console.log(
" stackmemory ralph swarm-status <swarmId> # Check progress"
);
console.log(
" stackmemory ralph swarm-stop <swarmId> # Stop swarm"
);
} catch (error) {
logger.error("Swarm launch failed", error);
console.error("\u274C Swarm launch failed:", error.message);
}
});
});
ralph.command("swarm-status").description("Check status of all active swarms or a specific swarm").argument("[swarmId]", "Optional specific swarm ID to check").option("--detailed", "Show detailed agent information").action(async (swarmId, options) => {
return trace.command(
"ralph-swarm-status",
{ swarmId, ...options },
async () => {
try {
await swarmCoordinator.initialize();
if (swarmId) {
const status = swarmCoordinator.getSwarmStatus(swarmId);
if (!status) {
console.log(`\u274C Swarm ${swarmId} not found`);
return;
}
console.log(`\u{1F9BE} Swarm Status: ${swarmId}`);
console.log(` Status: ${status.state}`);
console.log(` Agents: ${status.activeAgents} active`);
console.log(
` Started: ${new Date(status.startTime).toLocaleString()}`
);
if (options.detailed && status.agents) {
console.log("\n\u{1F465} Agent Details:");
status.agents.forEach((agent) => {
console.log(
` - ${agent.role}: ${agent.status} (${agent.task})`
);
});
}
} else {
const activeSwarms = swarmCoordinator.getAllActiveSwarms();
if (activeSwarms.length === 0) {
console.log("\u{1F4CA} No active swarms");
return;
}
console.log(`\u{1F4CA} Active Swarms: ${activeSwarms.length}`);
activeSwarms.forEach((swarm) => {
console.log(`
\u{1F194} ${swarm.id}`);
console.log(
` Description: ${swarm.description?.substring(0, 60)}...`
);
console.log(` Agents: ${swarm.agentCount}`);
console.log(` Status: ${swarm.status}`);
console.log(
` Running for: ${Math.round((Date.now() - swarm.startTime) / 1e3)}s`
);
});
console.log("\nCommands:");
console.log(
" stackmemory ralph swarm-status <id> # Check specific swarm"
);
console.log(
" stackmemory ralph swarm-killall # Stop all swarms"
);
}
} catch (error) {
logger.error("Failed to get swarm status", error);
console.error("\u274C Status check failed:", error.message);
}
}
);
});
ralph.command("oracle-worker").description(
"Launch Oracle/Worker pattern swarm for cost-effective execution"
).argument("<project>", "Project description for Oracle planning").option(
"--oracle <model>",
"Oracle model (default: claude-sonnet-4-5)",
"claude-sonnet-4-5-20250929"
).option(
"--workers <models>",
"Comma-separated worker models",
"claude-haiku-4-5-20251001"
).option("--budget <amount>", "Cost budget in USD", "10.0").option("--max-workers <count>", "Maximum worker agents", "5").option("--hints <hints>", "Comma-separated planning hints").action(async (project, options) => {
return trace.command(
"ralph-oracle-worker",
{ project, ...options },
async () => {
try {
console.log("\u{1F9E0} Launching Oracle/Worker swarm...");
console.log(`\u{1F4CB} Project: ${project}`);
console.log(`\u{1F4B0} Budget: $${options.budget}`);
const { OracleWorkerCoordinator, defaultModelConfigs } = await import("../../../integrations/ralph/patterns/oracle-worker-pattern.js");
const workerModels = options.workers.split(",").map((model) => {
const found = defaultModelConfigs.worker.find(
(w) => w.model.includes(model.trim())
);
return found || defaultModelConfigs.worker[0];
});
const coordinator = new OracleWorkerCoordinator({
oracle: defaultModelConfigs.oracle[0],
workers: workerModels,
reviewers: defaultModelConfigs.reviewer,
maxWorkers: parseInt(options.maxWorkers),
coordinationInterval: 3e4,
costBudget: parseFloat(options.budget)
});
await coordinator.initialize();
const hints = options.hints ? options.hints.split(",").map((h) => h.trim()) : void 0;
const swarmId = await coordinator.launchOracleWorkerSwarm(
project,
hints
);
console.log(`\u2705 Oracle/Worker swarm launched: ${swarmId}`);
console.log("\n\u{1F4CA} Pattern Benefits:");
console.log(" \u2022 Oracle handles strategic planning & review");
console.log(" \u2022 Workers execute parallelizable tasks");
console.log(" \u2022 Cost-optimized model selection");
console.log(" \u2022 Scalable multi-agent coordination");
console.log("\nNext steps:");
console.log(
` stackmemory ralph swarm-status ${swarmId} # Check progress`
);
console.log(
` stackmemory ralph swarm-stop ${swarmId} # Stop swarm`
);
} catch (error) {
logger.error("Oracle/Worker swarm failed", error);
console.error(`\u274C Oracle/Worker failed: ${error.message}`);
throw error;
}
}
);
});
ralph.command("claude-swarm").description("Launch swarm using Claude Code specialized agents").argument("<project>", "Project description for Claude Code agents").option(
"--oracle <agent>",
"Oracle agent (default: staff-architect)",
"staff-architect"
).option(
"--workers <agents>",
"Comma-separated worker agents",
"general-purpose,code-reviewer"
).option(
"--reviewers <agents>",
"Comma-separated reviewer agents",
"code-reviewer"
).option("--budget <amount>", "Cost budget in USD", "10.0").option(
"--complexity <level>",
"Project complexity (low|medium|high|very_high)",
"medium"
).option("--list-agents", "List available Claude Code agents").action(async (project, options) => {
return trace.command(
"ralph-claude-swarm",
{ project, ...options },
async () => {
try {
const { ClaudeCodeAgentBridge, CLAUDE_CODE_AGENTS } = await import("../../integrations/claude-code/agent-bridge.js");
if (options.listAgents) {
console.log("\n\u{1F916} Available Claude Code Agents:\n");
const oracles = Object.values(CLAUDE_CODE_AGENTS).filter(
(a) => a.type === "oracle"
);
const workers = Object.values(CLAUDE_CODE_AGENTS).filter(
(a) => a.type === "worker"
);
const reviewers = Object.values(CLAUDE_CODE_AGENTS).filter(
(a) => a.type === "reviewer"
);
console.log("\u{1F9E0} ORACLE AGENTS (Strategic Planning):");
oracles.forEach((agent) => {
console.log(` ${agent.name}: ${agent.description}`);
console.log(
` Capabilities: ${agent.capabilities.slice(0, 3).join(", ")}...`
);
});
console.log("\n\u26A1 WORKER AGENTS (Task Execution):");
workers.forEach((agent) => {
console.log(` ${agent.name}: ${agent.description}`);
console.log(
` Capabilities: ${agent.capabilities.slice(0, 3).join(", ")}...`
);
});
console.log("\n\u{1F50D} REVIEWER AGENTS (Quality Assurance):");
reviewers.forEach((agent) => {
console.log(` ${agent.name}: ${agent.description}`);
console.log(
` Capabilities: ${agent.capabilities.slice(0, 3).join(", ")}...`
);
});
console.log("\nUsage Examples:");
console.log(
' stackmemory ralph claude-swarm "Build REST API" --oracle staff-architect --workers general-purpose,debugger'
);
console.log(
' stackmemory ralph claude-swarm "Add user auth" --complexity high --workers general-purpose,qa-workflow-validator'
);
return;
}
console.log("\u{1F9E0} Launching Claude Code Agent Swarm...");
console.log(`\u{1F4CB} Project: ${project}`);
console.log(`\u{1F3AF} Oracle: ${options.oracle}`);
console.log(`\u26A1 Workers: ${options.workers}`);
console.log(`\u{1F50D} Reviewers: ${options.reviewers}`);
console.log(`\u{1F4B0} Budget: $${options.budget}`);
console.log(`\u{1F4CA} Complexity: ${options.complexity}`);
const bridge = new ClaudeCodeAgentBridge();
await bridge.initialize();
const workerAgents = options.workers.split(",").map((s) => s.trim());
const reviewerAgents = options.reviewers.split(",").map((s) => s.trim());
const swarmId = await bridge.launchClaudeCodeSwarm(project, {
oracleAgent: options.oracle,
workerAgents,
reviewerAgents,
budget: parseFloat(options.budget),
complexity: options.complexity
});
console.log(`\u2705 Claude Code swarm launched: ${swarmId}`);
console.log("\n\u{1F4CA} Claude Code Benefits:");
console.log(" \u2022 Specialized agents with proven capabilities");
console.log(" \u2022 Seamless integration with Claude Code tools");
console.log(" \u2022 Optimal agent selection for each task type");
console.log(" \u2022 Built-in quality assurance and review processes");
console.log("\nActive Agents:");
console.log(` \u{1F9E0} Oracle: ${options.oracle} (strategic planning)`);
workerAgents.forEach((agent) => {
const agentConfig = CLAUDE_CODE_AGENTS[agent];
console.log(
` \u26A1 Worker: ${agent} (${agentConfig?.specializations.join(", ") || "execution"})`
);
});
reviewerAgents.forEach((agent) => {
const agentConfig = CLAUDE_CODE_AGENTS[agent];
console.log(
` \u{1F50D} Reviewer: ${agent} (${agentConfig?.specializations.join(", ") || "review"})`
);
});
console.log("\nNext steps:");
console.log(
` stackmemory ralph swarm-status ${swarmId} # Check progress`
);
console.log(
` stackmemory ralph swarm-stop ${swarmId} # Stop swarm`
);
console.log(
" stackmemory ralph claude-swarm --list-agents # See all available agents"
);
} catch (error) {
logger.error("Claude Code swarm failed", error);
console.error(`\u274C Claude Code swarm failed: ${error.message}`);
throw error;
}
}
);
});
ralph.command("swarm-killall").description("Stop all active swarms and cleanup resources").option("--force", "Force kill without saving state").action(async (options) => {
return trace.command("ralph-swarm-killall", options, async () => {
try {
await swarmCoordinator.initialize();
const activeSwarms = swarmCoordinator.getAllActiveSwarms();
if (activeSwarms.length === 0) {
console.log("\u{1F4CA} No active swarms to stop");
return;
}
console.log(`\u{1F6D1} Stopping ${activeSwarms.length} active swarm(s)...`);
let stoppedCount = 0;
let failedCount = 0;
for (const swarm of activeSwarms) {
try {
console.log(` Stopping ${swarm.id}...`);
if (options.force) {
await swarmCoordinator.forceStopSwarm(swarm.id);
} else {
await swarmCoordinator.stopSwarm(swarm.id);
}
stoppedCount++;
console.log(` \u2705 Stopped ${swarm.id}`);
} catch (error) {
failedCount++;
console.error(
` \u274C Failed to stop ${swarm.id}: ${error.message}`
);
}
}
try {
const { execSync } = await import("child_process");
const branches = execSync('git branch | grep "swarm/"', {
encoding: "utf8"
}).split("\n").filter(Boolean).map((b) => b.trim());
if (branches.length > 0) {
console.log(
`
\u{1F500} Cleaning up ${branches.length} swarm branches...`
);
for (const branch of branches) {
try {
execSync(`git branch -D ${branch}`, { stdio: "ignore" });
console.log(` Deleted ${branch}`);
} catch {
}
}
}
} catch {
}
console.log(`
\u{1F4CA} Summary:`);
console.log(` \u2705 Stopped: ${stoppedCount} swarms`);
if (failedCount > 0) {
console.log(` \u274C Failed: ${failedCount} swarms`);
}
await swarmCoordinator.cleanup();
console.log("\u{1F9F9} Cleanup completed");
} catch (error) {
logger.error("Swarm killall failed", error);
console.error("\u274C Killall failed:", error.message);
}
});
});
ralph.command("orchestrate").description("Orchestrate multiple Ralph loops for complex tasks").argument("<description>", "Complex task description").option("--criteria <criteria>", "Success criteria (comma separated)").option("--max-loops <n>", "Maximum parallel loops", "3").option("--sequential", "Force sequential execution").action(async (description, options) => {
return trace.command(
"ralph-orchestrate",
{ description, ...options },
async () => {
try {
console.log("\u{1F3AD} Orchestrating complex task...");
await multiLoopOrchestrator.initialize();
const criteria = options.criteria ? options.criteria.split(",").map((c) => c.trim()) : [
"Task completed successfully",
"All components working",
"Tests pass"
];
const result = await multiLoopOrchestrator.orchestrateComplexTask(
description,
criteria,
{
maxLoops: parseInt(options.maxLoops),
forceSequential: options.sequential
}
);
console.log("\u2705 Orchestration completed!");
console.log(
`\u{1F4CA} Results: ${result.completedLoops.length} successful, ${result.failedLoops.length} failed`
);
console.log(
`\u23F1\uFE0F Total duration: ${Math.round(result.totalDuration / 1e3)}s`
);
if (result.insights.length > 0) {
console.log("\n\u{1F4A1} Insights:");
result.insights.forEach(
(insight) => console.log(` \u2022 ${insight}`)
);
}
} catch (error) {
logger.error("Orchestration failed", error);
console.error("\u274C Orchestration failed:", error.message);
}
}
);
});
ralph.command("learn").description("Learn patterns from completed loops").option("--task-type <type>", "Learn patterns for specific task type").action(async (options) => {
return trace.command("ralph-learn", options, async () => {
try {
console.log("\u{1F9E0} Learning patterns from completed loops...");
await patternLearner.initialize();
const patterns = options.taskType ? await patternLearner.learnForTaskType(options.taskType) : await patternLearner.learnFromCompletedLoops();
console.log(`\u2705 Learned ${patterns.length} patterns`);
if (patterns.length > 0) {
console.log("\n\u{1F4CA} Top patterns:");
patterns.slice(0, 5).forEach((pattern) => {
console.log(
` \u2022 ${pattern.pattern} (${Math.round(pattern.confidence * 100)}% confidence)`
);
});
}
} catch (error) {
logger.error("Pattern learning failed", error);
console.error(
"\u274C Pattern learning failed:",
error.message
);
}
});
});
ralph.command("debug-enhanced").description("Advanced debugging with visualization").option("--loop-id <id>", "Specific loop to debug").option("--generate-report", "Generate comprehensive debug report").option("--timeline", "Generate timeline visualization").action(async (options) => {
return trace.command("ralph-debug-enhanced", options, async () => {
try {
if (!existsSync(".ralph") && !options.loopId) {
console.log(
"\u274C No Ralph loop found. Run a loop first or specify --loop-id"
);
return;
}
console.log("\u{1F50D} Starting enhanced debugging...");
await ralphDebugger.initialize();
const loopId = options.loopId || "current";
await ralphDebugger.startDebugSession(loopId, ".ralph");
if (options.generateReport) {
const report = await ralphDebugger.generateDebugReport(loopId);
console.log(`\u{1F4CB} Debug report generated: ${report.exportPath}`);
}
if (options.timeline) {
const timelinePath = await ralphDebugger.generateLoopTimeline(loopId);
console.log(`\u{1F4CA} Timeline visualization: ${timelinePath}`);
}
console.log("\u{1F50D} Debug analysis complete");
} catch (error) {
logger.error("Enhanced debugging failed", error);
console.error("\u274C Debug failed:", error.message);
}
});
});
ralph.command("swarm-test").description("Comprehensive testing and validation for swarm functionality").option("--quick", "Run quick validation tests only").option("--stress", "Run stress tests with multiple parallel swarms").option("--error-injection", "Test error handling with deliberate failures").option("--cleanup-test", "Test cleanup mechanisms").option("--git-test", "Test git workflow integration").option("--report", "Generate detailed test report").action(async (options) => {
return trace.command("ralph-swarm-test", options, async () => {
try {
console.log("\u{1F9EA} Starting swarm testing and validation...");
await swarmCoordinator.initialize();
const testResults = [];
let passedTests = 0;
let totalTests = 0;
if (options.quick || !options.stress) {
console.log("\n\u26A1 Running quick validation tests...");
totalTests++;
try {
await swarmCoordinator.launchSwarm(
"Test: Basic functionality validation",
[{ role: "developer" }]
);
await swarmCoordinator.forceCleanup();
console.log(" \u2705 Basic swarm initialization");
passedTests++;
testResults.push({
test: "basic_init",
status: "passed",
duration: 0
});
} catch (error) {
console.log(
" \u274C Basic swarm initialization failed:",
error.message
);
testResults.push({
test: "basic_init",
status: "failed",
error: error.message
});
}
totalTests++;
try {
const usage = swarmCoordinator.getResourceUsage();
console.log(
` \u2705 Resource monitoring: ${usage.activeAgents} agents, ${usage.memoryEstimate}MB`
);
passedTests++;
testResults.push({
test: "resource_monitoring",
status: "passed",
data: usage
});
} catch (error) {
console.log(
" \u274C Resource monitoring failed:",
error.message
);
testResults.push({
test: "resource_monitoring",
status: "failed",
error: error.message
});
}
}
if (options.stress) {
console.log("\n\u{1F525} Running stress tests...");
totalTests++;
try {
const stressPromises = [];
for (let i = 0; i < 3; i++) {
stressPromises.push(
swarmCoordinator.launchSwarm(`Stress test swarm ${i}`, [
{ role: "developer" },
{ role: "tester" }
])
);
}
await Promise.all(stressPromises);
await swarmCoordinator.forceCleanup();
console.log(" \u2705 Parallel swarm stress test");
passedTests++;
testResults.push({ test: "stress_parallel", status: "passed" });
} catch (error) {
console.log(" \u274C Stress test failed:", error.message);
testResults.push({
test: "stress_parallel",
status: "failed",
error: error.message
});
}
}
if (options.errorInjection) {
console.log("\n\u{1F4A5} Testing error handling...");
totalTests++;
try {
try {
await swarmCoordinator.launchSwarm(
"Error test: Invalid agents",
[]
// Empty agents array
);
} catch {
console.log(" \u2705 Properly handled empty agents array");
passedTests++;
testResults.push({ test: "error_handling", status: "passed" });
}
} catch (error) {
console.log(
" \u274C Error handling test failed:",
error.message
);
testResults.push({
test: "error_handling",
status: "failed",
error: error.message
});
}
}
if (options.cleanupTest) {
console.log("\n\u{1F9F9} Testing cleanup mechanisms...");
totalTests++;
try {
await swarmCoordinator.launchSwarm("Cleanup test swarm", [
{ role: "developer" }
]);
await swarmCoordinator.forceCleanup();
const usage = swarmCoordinator.getResourceUsage();
if (usage.activeAgents === 0) {
console.log(" \u2705 Cleanup mechanism works correctly");
passedTests++;
testResults.push({ test: "cleanup", status: "passed" });
} else {
throw new SystemError(
`Cleanup failed: ${usage.activeAgents} agents still active`,
ErrorCode.RESOURCE_EXHAUSTED,
{ activeAgents: usage.activeAgents, test: "cleanup" }
);
}
} catch (error) {
console.log(
" \u274C Cleanup test failed:",
error.message
);
testResults.push({
test: "cleanup",
status: "failed",
error: error.message
});
}
}
if (options.gitTest) {
console.log("\n\u{1F500} Testing git workflow integration...");
totalTests++;
try {
const gitStatus = swarmCoordinator["gitWorkflowManager"].getGitStatus();
console.log(
` \u2705 Git workflow status: ${gitStatus.enabled ? "enabled" : "disabled"}`
);
passedTests++;
testResults.push({
test: "git_workflow",
status: "passed",
data: gitStatus
});
} catch (error) {
console.log(
" \u274C Git workflow test failed:",
error.message
);
testResults.push({
test: "git_workflow",
status: "failed",
error: error.message
});
}
}
console.log("\n\u{1F4CA} Test Results Summary:");
console.log(` Total tests: ${totalTests}`);
console.log(` Passed: ${passedTests} \u2705`);
console.log(` Failed: ${totalTests - passedTests} \u274C`);
console.log(
` Success rate: ${Math.round(passedTests / totalTests * 100)}%`
);
if (options.report) {
const reportPath = ".swarm/test-report.json";
const fs = await import("fs");
const reportData = {
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
summary: {
totalTests,
passedTests,
failedTests: totalTests - passedTests,
successRate: passedTests / totalTests * 100
},
testResults,
systemInfo: {
nodeVersion: process.version,
platform: process.platform,
arch: process.arch
}
};
fs.writeFileSync(reportPath, JSON.stringify(reportData, null, 2));
console.log(`\u{1F4CB} Detailed report saved to: ${reportPath}`);
}
if (passedTests === totalTests) {
console.log(
"\n\u{1F389} All tests passed! Swarm functionality is working correctly."
);
} else {
console.log(
"\n\u26A0\uFE0F Some tests failed. Check the errors above for details."
);
process.exit(1);
}
} catch (error) {
logger.error("Swarm testing failed", error);
console.error("\u274C Test suite failed:", error.message);
process.exit(1);
}
});
});
ralph.command("tui").description("Launch TUI monitor for active swarms").option("--swarm-id <id>", "Monitor specific swarm ID").option("--simple", "Use simple text mode instead of full TUI").option("--force-tui", "Force full TUI even with compatibility issues").action(async (options) => {
try {
const isGhostty = process.env.TERM_PROGRAM === "ghostty" || process.env.TERM?.includes("ghostty");
const isBasicTerm = process.env.TERM === "dumb" || process.env.TERM === "unknown";
const hasCompatibilityIssues = isGhostty || isBasicTerm;
const useSimpleMode = options.simple || hasCompatibilityIssues && !options.forceTui;
if (useSimpleMode) {
console.log("\u{1F9BE} Starting Simple Swarm Monitor (Text Mode)");
if (hasCompatibilityIssues && !options.simple) {
console.log(
`\u26A0\uFE0F Detected ${isGhostty ? "Ghostty" : "basic"} terminal - using text mode for compatibility`
);
console.log(
" Use --force-tui to override, or --simple to explicitly use text mode"
);
}
const { SimpleSwarmMonitor } = await import("../../features/tui/simple-monitor.js");
const monitor = new SimpleSwarmMonitor();
monitor.start();
} else {
console.log("\u{1F9BE} Starting Full TUI Monitor");
const { SwarmTUI } = await import("../../features/tui/swarm-monitor.js");
const tui = new SwarmTUI();
await tui.initialize(void 0, options.swarmId);
tui.start();
}
} catch (error) {
logger.error("TUI launch failed", error);
console.error("\u274C TUI failed:", error.message);
console.log("\u{1F4A1} Try: stackmemory ralph tui --simple");
process.exit(1);
}
});
ralph.command("linear").description(
"Execute Linear tasks via RLM orchestrator (pull \u2192 execute \u2192 update)"
).argument("[action]", "Action: next, all, task <id>, preview [id]", "next").argument("[taskId]", "Task ID for task/preview actions").option(
"--priority <level>",
"Filter by priority: urgent, high, medium, low"
).option("--tag <tag>", "Filter by tag").option("--dry-run", "Show plan without executing").option(
"--max-concurrent <n>",
"Max parallel tasks (default: 1, sequential)",
"1"
).action(
async (action, taskId, options) => {
return trace.command(
"ralph-linear",
{ action, taskId, ...options },
async () => {
try {
const { LinearTaskManager } = await import("../../features/tasks/linear-task-manager.js");
const { RecursiveAgentOrchestrator } = await import("../../skills/recursive-agent-orchestrator.js");
const { SpecGeneratorSkill } = await import("../../skills/spec-generator-skill.js");
const { LinearTaskRunner } = await import("../../skills/linear-task-runner.js");
console.log("\u{1F50C} Initializing Linear task runner...");
const taskManager = new LinearTaskManager();
const loadedCount = await taskManager.loadFromLinear();
console.log(`\u{1F4CB} Loaded ${loadedCount} tasks from Linear`);
const specSkill = new SpecGeneratorSkill({
projectId: "cli",
userId: "cli-user"
});
const { DualStackManager } = await import("../../core/context/dual-stack-manager.js");
const { ContextRetriever } = await import("../../core/retrieval/context-retriever.js");
const { SQLiteAdapter } = await import("../../core/database/sqlite-adapter.js");
const db = new SQLiteAdapter();
await db.initialize();
const dualStack = new DualStackManager(db);
const contextRetriever = new ContextRetriever(db);
const frameManager = dualStack.getActiveStack();
const rlm = new RecursiveAgentOrchestrator(
frameManager,
dualStack,
contextRetriever,
taskManager
);
const runner = new LinearTaskRunner(
taskManager,
rlm,
{ projectId: "cli", userId: "cli-user" },
specSkill
);
let result;
const runOpts = {
priority: options.priority,
tag: options.tag,
dryRun: options.dryRun,
maxConcurrent: parseInt(options.maxConcurrent)
};
switch (action) {
case "next":
console.log("\u{1F680} Running next task...");
result = await runner.runNext(runOpts);
break;
case "all":
console.log("\u{1F680} Running all tasks...");
result = await runner.runAll(runOpts);
break;
case "task":
if (!taskId) {
console.error(
"\u274C Task ID required: ralph linear task <id>"
);
return;
}
console.log(`\u{1F680} Running task ${taskId}...`);
result = await runner.runTask(taskId, runOpts);
break;
case "preview":
result = await runner.preview(taskId);
break;
default:
console.log("\u{1F680} Running next task...");
result = await runner.runNext(runOpts);
break;
}
if (result.success) {
console.log(`\u2705 ${result.message}`);
} else {
console.log(`\u274C ${result.message}`);
}
if (result.data) {
console.log(JSON.stringify(result.data, null, 2));
}
taskManager.destroy();
} catch (error) {
logger.error("Linear task execution failed", error);
console.error(
"\u274C Linear execution failed:",
error.message
);
}
}
);
}
);
ralph.command("loopmax").description(
"Aggressive autonomous loop: no planning, just go until tests pass"
).argument("<task>", "What to accomplish").option(
"-c, --criteria <criteria>",
"Completion criteria",
"All tests pass, lint clean, build succeeds"
).option("--no-worktree", "Skip git worktree isolation (work in-place)").option("--max-loops <n>", "Max loop iterations (0=infinite)", "0").option("--max-stuck <n>", "Respawn after N stuck loops", "3").option("--commit-every <n>", "Auto-commit every N tool calls", "25").option("--model <model>", "Claude model to use", "sonnet").action(
async (task, options) => {
return trace.command(
"ralph-loopmax",
{ task, ...options },
async () => {
try {
console.log("LOOPMAX MODE ACTIVATED");
console.log(`Task: ${task}`);
console.log(`Criteria: ${options.criteria}`);
console.log(
`Worktree: ${options.worktree !== false ? "yes" : "no"}`
);
console.log(`Max loops: ${options.maxLoops || "infinite"}`);
console.log("");
const runner = new LoopMaxRunner({
task,
criteria: options.criteria,
useWorktree: options.worktree !== false,
maxLoops: parseInt(options.maxLoops) || 0,
maxStuckBeforeRespawn: parseInt(options.maxStuck) || 3,
commitEvery: parseInt(options.commitEvery) || 25,
model: options.model,
verbose: true
});
await runner.run();
await runner.cleanup();
} catch (error) {
logger.error("LoopMax failed", error);
console.error("LoopMax crashed:", error.message);
process.exit(1);
}
}
);
}
);
return ralph;
}
var ralph_default = createRalphCommand;
export {
createRalphCommand,
ralph_default as default
};