UNPKG

automagik-genie

Version:

Self-evolving AI agent orchestration framework with Model Context Protocol support

184 lines (183 loc) 7.63 kB
"use strict"; /** * Prompt Tool - Synchronous prompt transformer using an agent * * Uses an executor to transform/enhance/rewrite prompts in real-time. * Runs synchronously (no background task, no worktree). * * This is the modern equivalent of the old "background off" mode. * Creates a temporary Forge task but polls synchronously for completion. */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.promptToolSchema = void 0; exports.executePromptTool = executePromptTool; const zod_1 = require("zod"); const child_process_1 = require("child_process"); const path_1 = __importDefault(require("path")); const project_detector_js_1 = require("../lib/project-detector.js"); 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 Genie package root (not user's cwd) // Compiled location: dist/mcp/tools/prompt-tool.js // Target: src/lib/forge-client.js // Path: ../../../src/lib/forge-client.js (dist/mcp/tools -> root -> src/lib) 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)(); /** * Prompt tool parameters */ exports.promptToolSchema = zod_1.z.object({ prompt: zod_1.z.string().describe('Prompt to transform/enhance (e.g., "Help me write a better prompt for implementing dark mode")'), agent: zod_1.z.string().optional().default('prompt').describe('Agent to use for transformation (default: "prompt")') }); /** * Prompt tool execution */ async function executePromptTool(args, context) { const { streamContent } = context; const agent = args.agent || 'prompt'; let fullOutput = `💭 Transforming prompt with agent: ${agent}\n\n`; await streamContent({ type: 'text', text: fullOutput }); // Note: No git validation needed - this runs synchronously in current workspace // The prompt transformer agent doesn't spawn a worktree, it just processes input/output // Create temporary task (we'll poll for completion) const forgeClient = new ForgeClient(FORGE_URL); const projectId = await (0, project_detector_js_1.getOrCreateGenieProject)(forgeClient); // Detect current git branch (same logic as other neuron tools) 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' } } let taskResult; try { taskResult = await forgeClient.createAndStartTask({ task: { project_id: projectId, title: (0, task_title_formatter_js_1.formatTaskTitle)('MCP', `Prompt Transform: ${args.prompt.substring(0, 50)}...`), description: `Transform this prompt:\n\n${args.prompt}\n\nAgent: ${agent}` }, executor_profile_id: { executor: 'CLAUDE_CODE', variant: 'DEFAULT' }, base_branch: baseBranch }); } catch (error) { const errorMsg = `❌ Failed to process prompt: ${error.message}\n`; fullOutput += errorMsg; await streamContent({ type: 'text', text: errorMsg }); return fullOutput; } const taskId = taskResult.task?.id || 'unknown'; const attemptId = taskResult.task_attempt?.id || 'unknown'; const waitMsg = `⏳ Waiting for response...\n\n`; fullOutput += waitMsg; await streamContent({ type: 'text', text: waitMsg }); // Poll for completion (simple polling, no WebSocket) let attempts = 0; const maxAttempts = 30; // 30 seconds max let completed = false; let answer = ''; while (attempts < maxAttempts && !completed) { await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second try { const attempt = await forgeClient.getTaskAttempt(attemptId); // Check if execution is complete if (attempt.execution_processes && attempt.execution_processes.length > 0) { const process = attempt.execution_processes[0]; if (process.status === 'COMPLETED' || process.status === 'FAILED') { completed = true; if (process.status === 'COMPLETED' && process.normalized_logs) { // Extract answer from logs answer = process.normalized_logs .filter((log) => log.type === 'text') .map((log) => log.content) .join('\n\n'); const successMsg = `✅ Prompt processed successfully\n\n` + `📝 Answer:\n\n${answer || 'No answer available'}\n\n`; fullOutput += successMsg; await streamContent({ type: 'text', text: successMsg }); } else if (process.status === 'FAILED') { const failMsg = `❌ Prompt processing failed\n\n`; fullOutput += failMsg; await streamContent({ type: 'text', text: failMsg }); } } } } catch (error) { // Ignore polling errors, continue } attempts++; } if (!completed) { const timeoutMsg = `⏱️ Timeout: Response took too long (30s limit)\n\n` + `📊 Check progress at Forge URL below\n\n`; fullOutput += timeoutMsg; await streamContent({ type: 'text', text: timeoutMsg }); } // Output data links (minimal for prompt tool) const dataLinksMsg = `🔗 Data Links:\n` + ` Agent: ${agent}\n` + ` Mode: synchronous (no background task)\n` + ` Task ID: ${taskId} (temporary)\n\n`; fullOutput += dataLinksMsg; await streamContent({ type: 'text', text: dataLinksMsg }); // Genie self-guidance tips const tipsMsg = `💡 Genie Tips:\n` + ` - This was a synchronous prompt transformation (no worktree spawned)\n` + ` - For implementation work, use the forge tool instead\n` + ` - Prompt tool is for transforming/enhancing prompts only\n` + ` - No git validation required (runs in current workspace)\n` + ` - Temporary task will be cleaned up automatically\n`; fullOutput += tipsMsg; await streamContent({ type: 'text', text: tipsMsg }); // Return full accumulated output (Fix Bug #4: Silent failure) return fullOutput; }