summaryrefslogtreecommitdiff
path: root/admin-panel/src/server/agents/base-agent.ts
diff options
context:
space:
mode:
Diffstat (limited to 'admin-panel/src/server/agents/base-agent.ts')
-rw-r--r--admin-panel/src/server/agents/base-agent.ts83
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;
+ }
+}