diff options
Diffstat (limited to 'admin-panel/src/server/agents/base-agent.ts')
| -rw-r--r-- | admin-panel/src/server/agents/base-agent.ts | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/admin-panel/src/server/agents/base-agent.ts b/admin-panel/src/server/agents/base-agent.ts new file mode 100644 index 0000000..f9e49f7 --- /dev/null +++ b/admin-panel/src/server/agents/base-agent.ts @@ -0,0 +1,83 @@ +import { readFileSync } from 'fs'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; +import { callClaude, type ClaudeResponse } from '../services/claude.service.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const TEMPLATES_DIR = join(__dirname, '..', 'templates'); + +export interface AgentContext { + taskId: number; + title: string; + description: string; + plan?: string; + decisionLog?: string; + doneWhen?: string; + implementationNotes?: string; + diff?: string; + testResults?: string; + reviewComments?: string; + branchName?: string; + repoPath?: string; + workspacePath?: string; + fileList?: string; + fileContents?: string; +} + +export interface AgentResult { + success: boolean; + content: string; + tokensUsed: number; + model: string; + updates: Record<string, unknown>; // Fields to update on the task + verdict?: 'approve' | 'reject'; // For review agents + message: string; // Log message +} + +export abstract class BaseAgent { + abstract name: string; + abstract templateFile: string; + abstract model: string; + + protected loadTemplate(): string { + return readFileSync(join(TEMPLATES_DIR, this.templateFile), 'utf-8'); + } + + protected fillTemplate(template: string, ctx: AgentContext): string { + return template + .replace(/\{\{taskId\}\}/g, String(ctx.taskId)) + .replace(/\{\{title\}\}/g, ctx.title || '') + .replace(/\{\{description\}\}/g, ctx.description || '') + .replace(/\{\{plan\}\}/g, ctx.plan || '') + .replace(/\{\{decision_log\}\}/g, ctx.decisionLog || '') + .replace(/\{\{done_when\}\}/g, ctx.doneWhen || '') + .replace(/\{\{implementation_notes\}\}/g, ctx.implementationNotes || '') + .replace(/\{\{diff\}\}/g, ctx.diff || '') + .replace(/\{\{test_results\}\}/g, ctx.testResults || '') + .replace(/\{\{review_comments\}\}/g, ctx.reviewComments || '') + .replace(/\{\{branch_name\}\}/g, ctx.branchName || '') + .replace(/\{\{file_list\}\}/g, ctx.fileList || '') + .replace(/\{\{file_contents\}\}/g, ctx.fileContents || ''); + } + + async execute(ctx: AgentContext): Promise<AgentResult> { + const template = this.loadTemplate(); + const prompt = this.fillTemplate(template, ctx); + + const response = await callClaude({ + model: this.model, + systemPrompt: this.getSystemPrompt(), + userMessage: prompt, + maxTokens: this.getMaxTokens(), + }); + + return this.parseResponse(response, ctx); + } + + protected abstract getSystemPrompt(): string; + protected abstract parseResponse(response: ClaudeResponse, ctx: AgentContext): AgentResult; + + protected getMaxTokens(): number { + return 8192; + } +} |
