cortexweaver
Version:
CortexWeaver is a command-line interface (CLI) tool that orchestrates a swarm of specialized AI agents, powered by Claude Code and Gemini CLI, to assist in software development. It transforms a high-level project plan (plan.md) into a series of coordinate
363 lines • 16.6 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Orchestrator = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const cognitive_canvas_1 = require("../cognitive-canvas");
const plan_parser_1 = require("../plan-parser");
const claude_client_1 = require("../claude-client");
const workspace_1 = require("../workspace");
const session_1 = require("../session");
const critique_1 = require("../agents/critique");
const debugger_1 = require("../agents/debugger");
const cognitive_canvas_navigator_1 = require("../agents/cognitive-canvas-navigator");
// Import modular components
const workflow_manager_1 = require("./workflow-manager");
const task_executor_1 = require("./task-executor");
const agent_spawner_1 = require("./agent-spawner");
const error_handler_1 = require("./error-handler");
const status_manager_1 = require("./status-manager");
const utils_1 = require("./utils");
class Orchestrator {
constructor(config) {
// State
this.parsedPlan = null;
if (!config.neo4j) {
throw new Error('Neo4j configuration is required');
}
if (!config.claude) {
throw new Error('Claude configuration is required');
}
// Initialize core dependencies
this.canvas = new cognitive_canvas_1.CognitiveCanvas(config.neo4j);
this.parser = new plan_parser_1.PlanParser();
this.client = new claude_client_1.ClaudeClient(config.claude);
this.workspace = new workspace_1.WorkspaceManager();
this.sessionManager = new session_1.SessionManager();
this.critiqueAgent = new critique_1.CritiqueAgent(this.client, this.canvas);
this.debuggerAgent = new debugger_1.DebuggerAgent();
this.cognitiveCanvasNavigator = new cognitive_canvas_navigator_1.CognitiveCanvasNavigator();
// Initialize modular components
this.workflowManager = new workflow_manager_1.WorkflowManager();
this.agentSpawner = new agent_spawner_1.AgentSpawner(this.workspace, this.sessionManager);
this.statusManager = new status_manager_1.StatusManager(this.canvas, this.client, this.sessionManager, this.workflowManager);
this.taskExecutor = new task_executor_1.TaskExecutor(this.canvas, this.workspace, this.sessionManager, this.workflowManager);
this.errorHandler = new error_handler_1.ErrorHandler(this.canvas, this.sessionManager, this.agentSpawner, this.workflowManager, this.critiqueAgent, this.debuggerAgent);
}
async initialize(projectPath) {
try {
console.log('Initializing Orchestrator...');
// Initialize Cognitive Canvas schema
await this.canvas.initializeSchema();
// Load and parse plan
const planPath = path.join(projectPath, 'plan.md');
if (!fs.existsSync(planPath)) {
throw new Error(`Plan file not found at ${planPath}`);
}
const planContent = fs.readFileSync(planPath, 'utf-8');
this.parsedPlan = this.parser.parse(planContent);
// Create project in Cognitive Canvas
const projectData = {
id: `project-${Date.now()}`,
name: this.parsedPlan.title,
description: this.parsedPlan.overview,
status: 'initialized',
createdAt: new Date().toISOString()
};
const project = await this.canvas.createProject(projectData);
this.statusManager.setProjectId(project.id);
// Create tasks with dependency ordering
await (0, utils_1.createTasks)(this.parsedPlan, this.canvas, this.statusManager, this.workflowManager);
// Initialize workflow states for all tasks
await (0, utils_1.initializeTaskWorkflowStates)(this.canvas, this.statusManager, this.workflowManager);
// Store architectural decisions
await (0, utils_1.storeArchitecturalDecisions)(this.parsedPlan, this.canvas, this.statusManager);
this.statusManager.setStatus('initialized');
console.log(`Project ${this.parsedPlan.title} initialized successfully`);
}
catch (error) {
this.statusManager.setStatus('error');
throw new Error(`Failed to initialize orchestrator: ${error.message}`);
}
}
async start() {
const projectId = this.statusManager.getProjectId();
if (!projectId || !this.parsedPlan) {
throw new Error('Project must be initialized before starting');
}
this.statusManager.setRunning(true);
this.statusManager.setStatus('running');
console.log('Starting orchestration...');
try {
while (this.statusManager.isRunning()) {
// Check budget before processing
if (!this.statusManager.checkBudgetLimit()) {
console.log('Budget limit reached, stopping orchestration');
break;
}
await this.processNextTask();
await this.taskExecutor.monitorTasks(projectId);
// Check if all tasks completed
const allCompleted = await this.statusManager.areAllTasksCompleted();
if (allCompleted) {
console.log('All tasks completed!');
this.statusManager.setStatus('completed');
break;
}
// Log status summary periodically
await this.statusManager.logStatusSummary();
// Small delay to prevent busy waiting
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
catch (error) {
console.error('Orchestration error:', error);
this.statusManager.setStatus('error');
}
finally {
this.statusManager.setRunning(false);
}
}
async processNextTask() {
try {
const projectId = this.statusManager.getProjectId();
if (!projectId)
return;
const tasks = await this.canvas.getTasksByProject(projectId);
const availableTasks = tasks.filter(task => task.status === 'pending');
for (const task of availableTasks) {
const dependencies = await this.canvas.getTaskDependencies(task.id);
const unmetDependencies = dependencies.filter(dep => dep.status !== 'completed');
if (unmetDependencies.length === 0) {
// Perform critique check
const shouldProceed = await this.errorHandler.performCritiqueCheck(task.id);
if (!shouldProceed) {
console.log(`Task ${task.id} blocked by critique findings`);
continue;
}
// Prime context for task before spawning agent
const contextData = await this.primeContextForTask(task.id, task);
// Process task with workflow awareness
await this.taskExecutor.processTaskWithWorkflow(task);
break; // Process one task at a time
}
}
}
catch (error) {
console.error('Error processing next task:', error);
}
}
async spawnAgent(task, agentType) {
const contextData = await this.primeContextForTask(task.id, task);
const result = await this.agentSpawner.spawnAgent(task, agentType, contextData);
if (!result.success) {
throw new Error(result.error || 'Failed to spawn agent');
}
}
async handleImpasse(taskId) {
const result = await this.errorHandler.handleImpasse(taskId);
if (!result.success) {
console.error(`Failed to handle impasse for task ${taskId}: ${result.message}`);
}
}
async handleTaskCompletion(taskId) {
await this.taskExecutor.handleTaskCompletion(taskId);
}
async handleTaskFailure(taskId, failure) {
const result = await this.errorHandler.handleTaskFailure(taskId, failure);
if (!result.success && result.escalated) {
console.error(`Task failure escalated for ${taskId}: ${result.message}`);
}
}
// Public status methods
checkBudgetLimit() {
return this.statusManager.checkBudgetLimit();
}
getTokenUsage() {
return this.statusManager.getTokenUsage();
}
getStatus() {
return this.statusManager.getStatus();
}
isRunning() {
return this.statusManager.isRunning();
}
async getProjectProgress() {
return this.statusManager.getProjectProgress();
}
async getSystemHealth() {
return this.statusManager.getSystemHealth();
}
async getDetailedStatusReport() {
return this.statusManager.getDetailedStatusReport();
}
// Additional methods for backward compatibility
async monitorTasks() {
const projectId = this.statusManager.getProjectId();
if (projectId) {
await this.taskExecutor.monitorTasks(projectId);
}
}
// Enhanced error handling methods for compatibility
getActiveCodeSavantSessions() {
// Return active sessions tracked by error handler
return this.errorHandler.getActiveCodeSavantSessions();
}
getTaskErrorHistory(taskId) {
// Return error history for a specific task
return this.errorHandler.getTaskErrorHistory(taskId);
}
getErrorRecoveryStatistics() {
// Return error recovery statistics
return this.errorHandler.getErrorRecoveryStatistics();
}
async shutdown() {
console.log('Shutting down Orchestrator...');
this.statusManager.setRunning(false);
// Kill all active sessions
const sessions = this.sessionManager.listSessions();
for (const session of sessions) {
await this.sessionManager.killSession(session.sessionId);
}
// Close database connection
await this.canvas.close();
// Reset status manager
this.statusManager.reset();
console.log('Orchestrator shutdown complete');
}
async primeContextForTask(taskId, task) {
try {
// Get current workflow state for context-aware priming
const workflowState = this.workflowManager.getTaskWorkflowState(taskId);
const currentStep = workflowState?.currentStep || 'DEFINE_REQUIREMENTS';
// Enhanced targeted context retrieval with workflow awareness
const baseQuery = `${task.title} ${task.description}`;
const enhancedQuery = this.workflowManager.enhanceQueryForWorkflowStep(baseQuery, currentStep);
// Prepare navigation task for CognitiveCanvasNavigator
const navigationTask = {
id: `nav-${taskId}`,
title: `Navigate context for ${task.title}`,
description: `Find relevant context for workflow step ${currentStep}`,
status: 'pending',
priority: task.priority,
projectId: task.projectId,
createdAt: new Date().toISOString()
};
const navigationContext = {
query: {
type: 'semantic',
query: enhancedQuery,
context: {
taskId,
taskType: 'feature_implementation',
priority: task.priority,
workflowStep: currentStep,
limit: 50,
nodeType: this.workflowManager.getTargetedNodeTypes(currentStep),
selectProperties: this.workflowManager.getRelevantProperties(currentStep)
},
filters: [
{
type: 'node',
field: 'type',
operator: 'equals',
value: this.workflowManager.getTargetedNodeTypes(currentStep)
}
]
}
};
// Initialize navigator if not already done
if (!this.cognitiveCanvasNavigator.getStatus || this.cognitiveCanvasNavigator.getStatus() === 'uninitialized') {
await this.cognitiveCanvasNavigator.initialize({
id: 'orchestrator-navigator',
role: 'navigator',
capabilities: ['graph-navigation'],
claudeConfig: {
apiKey: this.client.getConfiguration()?.apiKey || 'default',
},
workspaceRoot: process.cwd(),
cognitiveCanvasConfig: {
uri: 'bolt://localhost:7687',
username: 'neo4j',
password: 'password'
}
});
}
// Execute navigation task
await this.cognitiveCanvasNavigator.receiveTask(navigationTask, navigationContext);
const navigationResultTask = await this.cognitiveCanvasNavigator.run();
const navigationResult = navigationResultTask.result || {
nodes: [],
relationships: [],
paths: [],
insights: [],
metadata: { queryTime: 0, resultCount: 0, confidence: 0.5 }
};
// Extract context specific to workflow step
const relevantArtifacts = (0, utils_1.filterArtifactsByWorkflowStep)(navigationResult.nodes, currentStep);
const patterns = (0, utils_1.extractWorkflowRelevantPatterns)(navigationResult.insights, currentStep);
// Get step-specific relationships
const relevantRelationships = navigationResult.relationships
.filter((rel) => (0, utils_1.isRelationshipRelevantForStep)(rel, currentStep))
.slice(0, 20); // Limit relationships
return {
workflowStep: currentStep,
relevantArtifacts,
patterns,
relationships: relevantRelationships,
paths: navigationResult.paths.slice(0, 3),
priming: {
stepSpecificGuidance: this.workflowManager.getStepSpecificGuidance(currentStep),
requiredInputs: this.workflowManager.getRequiredInputsForStep(currentStep),
expectedOutputs: this.workflowManager.getExpectedOutputsForStep(currentStep)
}
};
}
catch (error) {
console.error(`Error priming context for task ${taskId}:`, error);
return {
workflowStep: 'DEFINE_REQUIREMENTS',
relevantArtifacts: [],
patterns: [],
relationships: [],
paths: [],
priming: {}
};
}
}
}
exports.Orchestrator = Orchestrator;
//# sourceMappingURL=index.js.map