arela
Version:
AI-powered CTO with multi-agent orchestration, code summarization, visual testing (web + mobile) for blazing fast development.
182 lines ⢠6.09 kB
JavaScript
import { loadTicketStatus, updateTicketStatus } from "./status.js";
/**
* Get agent capabilities
*/
export function getAgentCapabilities() {
return {
codex: {
name: "OpenAI Codex",
command: "codex",
costPer1kTokens: 0.002,
bestFor: ["simple tasks", "CRUD operations", "boilerplate"],
complexity: "simple",
speed: "fast",
},
claude: {
name: "Claude Sonnet",
command: "claude",
costPer1kTokens: 0.015,
bestFor: ["complex tasks", "refactoring", "architecture"],
complexity: "complex",
speed: "medium",
},
deepseek: {
name: "DeepSeek Coder",
command: "deepseek",
costPer1kTokens: 0.001,
bestFor: ["coding tasks", "cost-sensitive work"],
complexity: "medium",
speed: "fast",
},
ollama: {
name: "Ollama (Local)",
command: "ollama",
costPer1kTokens: 0,
bestFor: ["offline work", "privacy-sensitive"],
complexity: "simple",
speed: "slow",
},
cascade: {
name: "Windsurf Cascade",
command: "windsurf",
costPer1kTokens: 0,
bestFor: ["IDE integration", "interactive work"],
complexity: "complex",
speed: "medium",
},
};
}
/**
* Select best agent for ticket
*/
export function selectBestAgent(complexity, availableAgents) {
const capabilities = getAgentCapabilities();
// Select based on complexity
const complexityLevel = complexity || "medium";
// Priority order based on complexity
const priorities = {
simple: ["codex", "deepseek", "ollama", "claude"],
medium: ["deepseek", "claude", "codex", "ollama"],
complex: ["claude", "cascade", "deepseek"],
};
const priority = priorities[complexityLevel];
// Find first available agent in priority order
for (const agentKey of priority) {
const isAvailable = availableAgents.some((a) => {
const name = a.name.toLowerCase();
return (name.includes(agentKey) ||
a.command === agentKey ||
(agentKey === "cascade" && name.includes("windsurf")));
});
if (isAvailable) {
return agentKey;
}
}
// Fallback to first available
return availableAgents[0]?.command || "codex";
}
/**
* Estimate cost for ticket
*/
export function estimateCost(estimatedTokens, agent) {
const capabilities = getAgentCapabilities();
const agentCap = capabilities[agent];
if (!agentCap) {
return { tokens: estimatedTokens || 1000, cost: 0 };
}
const tokens = estimatedTokens || 1000;
const cost = (tokens / 1000) * agentCap.costPer1kTokens;
return { tokens, cost };
}
/**
* Check if ticket dependencies are met
*/
export async function checkDependencies(cwd, ticketId, status) {
// For now, simplified implementation
// Full implementation would parse ticket files and check dependencies
return { met: true, blocking: [] };
}
/**
* Dispatch tickets to agents
*/
export async function dispatchTickets(config) {
const { cwd, agent, tickets: ticketIds, dryRun } = config;
const status = await loadTicketStatus(cwd);
// Get tickets to dispatch
let ticketsToDispatch;
if (ticketIds && ticketIds.length > 0) {
ticketsToDispatch = ticketIds;
}
else {
// Get all pending tickets (simplified for now)
ticketsToDispatch = Object.keys(status).filter((id) => {
const s = status[id];
return !s || s.status === "pending";
});
}
const dispatched = [];
const blocked = [];
const errors = [];
console.log(`\nš Dispatching ${ticketsToDispatch.length} ticket(s)...\n`);
for (const ticketId of ticketsToDispatch) {
try {
// Check dependencies
const deps = await checkDependencies(cwd, ticketId, status);
if (!deps.met) {
console.log(`āø ${ticketId}: Blocked by ${deps.blocking.join(", ")}`);
blocked.push(ticketId);
status[ticketId] = {
ticketId,
status: "blocked",
};
continue;
}
// Select agent
const selectedAgent = agent || "claude";
// Estimate cost
const estimate = estimateCost(1000, selectedAgent);
// Update status
status[ticketId] = {
ticketId,
status: "pending",
agent: selectedAgent,
assignedAt: new Date().toISOString(),
cost: estimate.cost,
};
console.log(`ā ${ticketId}`);
console.log(` Agent: ${selectedAgent}`);
console.log(` Estimated: ${estimate.tokens} tokens, $${estimate.cost.toFixed(4)}`);
console.log("");
dispatched.push(ticketId);
}
catch (error) {
const msg = `${ticketId}: ${error.message}`;
console.log(`ā ${msg}`);
errors.push(msg);
}
}
// Save status
if (!dryRun) {
await updateTicketStatus(cwd, "", status);
}
// Summary
console.log("\nš Dispatch Summary\n");
console.log(`ā Dispatched: ${dispatched.length}`);
if (blocked.length > 0) {
console.log(`āø Blocked: ${blocked.length}`);
}
if (errors.length > 0) {
console.log(`ā Errors: ${errors.length}`);
}
// Calculate total cost
const totalCost = dispatched.reduce((sum, id) => {
return sum + (status[id]?.cost || 0);
}, 0);
console.log(`\nEstimated cost: $${totalCost.toFixed(4)}`);
if (dryRun) {
console.log("\nā ļø Dry run - no changes saved");
}
console.log("");
return { dispatched, blocked, errors };
}
//# sourceMappingURL=dispatch.js.map