automagik-genie
Version:
Self-evolving AI agent orchestration framework with Model Context Protocol support
114 lines (113 loc) • 4.98 kB
JavaScript
;
/**
* Create Subtask Tool - Create child task under master orchestrator
*
* Allows master orchestrators to delegate work as subtasks.
* Subtasks have parent relationship and can be used for complex work breakdown.
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createSubtaskToolSchema = void 0;
exports.executeCreateSubtaskTool = executeCreateSubtaskTool;
const zod_1 = require("zod");
const path_1 = __importDefault(require("path"));
const child_process_1 = require("child_process");
const task_title_formatter_js_1 = require("../lib/task-title-formatter.js");
const service_config_js_1 = require("../lib/service-config.js");
// Load ForgeClient from src/lib (resolves from package root)
// Compiled location: dist/mcp/tools/create-subtask-tool.js
// Target: src/lib/forge-client.js
const geniePackageRoot = path_1.default.resolve(__dirname, '../../..');
const ForgeClient = require(path_1.default.join(geniePackageRoot, 'src/lib/forge-client.js')).ForgeClient;
const { baseUrl: FORGE_URL } = (0, service_config_js_1.getForgeConfig)();
/**
* Create subtask parameters
*/
exports.createSubtaskToolSchema = zod_1.z.object({
parent_attempt_id: zod_1.z.string().describe('Parent task attempt ID (the master orchestrator)'),
title: zod_1.z.string().describe('Subtask title'),
prompt: zod_1.z.string().describe('Subtask prompt/description'),
executor: zod_1.z.string().optional().default('CLAUDE_CODE:DEFAULT').describe('Executor variant (e.g., "CLAUDE_CODE:CODE_INSTALL", "CLAUDE_CODE:CODE_EXPLORE", "CLAUDE_CODE:MASTER"). Format: EXECUTOR:VARIANT where VARIANT is {COLLECTIVE}_{AGENT} like CODE_INSTALL or CREATE_INSTALL, or special variants like MASTER, DEFAULT.')
});
/**
* Create subtask execution
*/
async function executeCreateSubtaskTool(args, context) {
const { streamContent } = context;
let fullOutput = `📦 Creating subtask under parent: ${args.parent_attempt_id}\n\n`;
await streamContent({
type: 'text',
text: fullOutput
});
const forgeClient = new ForgeClient(FORGE_URL);
try {
// Get project ID dynamically from workspace
const projects = await forgeClient.listProjects();
const projectId = projects[0]?.id;
if (!projectId) {
throw new Error('No Forge project found. Run genie init first.');
}
// Parse executor string (format: "CLAUDE_CODE:DEFAULT" or "CLAUDE_CODE")
const [executor, variant = 'DEFAULT'] = args.executor.split(':');
// Detect current git branch (same logic as forge-executor.ts)
let baseBranch = 'main'; // Default fallback
try {
baseBranch = (0, child_process_1.execSync)('git rev-parse --abbrev-ref HEAD', {
encoding: 'utf8',
cwd: process.cwd(),
stdio: ['pipe', 'pipe', 'pipe'] // Suppress stderr
}).trim();
}
catch (error) {
// If git detection fails, try to get default_base_branch from project
try {
const project = await forgeClient.getProject(projectId);
if (project.default_base_branch) {
baseBranch = project.default_base_branch;
}
}
catch {
// Use fallback 'main'
}
}
const result = await forgeClient.createAndStartTask({
task: {
project_id: projectId,
title: (0, task_title_formatter_js_1.formatTaskTitle)('MCP', `Subtask: ${args.title}`),
description: args.prompt,
parent_task_attempt: args.parent_attempt_id
},
executor_profile_id: {
executor: executor.toUpperCase(),
variant: variant.toUpperCase()
},
base_branch: baseBranch
});
const taskId = result.id;
const attemptId = result.attempts?.[0]?.id || result.id;
const url = `${FORGE_URL}/projects/${projectId}/tasks/${taskId}/attempts/${attemptId}?view=diffs`;
const successMsg = `✅ Subtask created successfully\n\n` +
`📋 Title: ${args.title}\n` +
`🔗 URL: ${url}\n` +
`🆔 Task ID: ${taskId}\n` +
`🔄 Attempt ID: ${attemptId}\n\n` +
`💡 Subtask will execute in background as child of master orchestrator.\n`;
fullOutput += successMsg;
await streamContent({
type: 'text',
text: successMsg
});
return fullOutput;
}
catch (error) {
const errorMsg = `❌ Failed to create subtask: ${error.message}\n`;
fullOutput += errorMsg;
await streamContent({
type: 'text',
text: errorMsg
});
return fullOutput;
}
}