Documentation Index
Fetch the complete documentation index at: https://docs.namastex.ai/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Custom executors allow you to integrate any AI coding tool or custom logic into Forge. Extend beyond the 8 built-in agents to add your own automation.Use cases: Custom APIs, internal tools, specialized workflows, proprietary models
Executor Interface
Create a custom executor by implementing the Executor interface:interface Executor {
name: string;
version: string;
// Execute a task
execute(task: Task, options: ExecutorOptions): Promise<ExecutionResult>;
// Check if executor can handle this task
canHandle(task: Task): boolean;
// Optional: Estimate cost
estimateCost?(task: Task): Promise<number>;
// Optional: Cancel execution
cancel?(executionId: string): Promise<void>;
}
Basic Custom Executor
Simple Example
import { Executor, Task, ExecutionResult } from '@automagik/forge-sdk';
export class MyCustomExecutor implements Executor {
name = 'my-custom-executor';
version = '1.0.0';
async execute(task: Task, options: any): Promise<ExecutionResult> {
console.log(`Executing task: ${task.title}`);
// Your custom logic here
const result = await this.runCustomLogic(task);
return {
success: true,
output: result,
filesChanged: [],
cost: 0
};
}
canHandle(task: Task): boolean {
// Only handle tasks with specific label
return task.labels.includes('custom-executor');
}
private async runCustomLogic(task: Task): Promise<string> {
// Implement your custom logic
return `Task ${task.title} completed!`;
}
}
Register Executor
import { ForgeClient } from '@automagik/forge-sdk';
import { MyCustomExecutor } from './my-custom-executor';
const forge = new ForgeClient();
// Register custom executor
forge.executors.register(new MyCustomExecutor());
// Now use it
await forge.tasks.create({
title: 'Custom task',
labels: ['custom-executor'] // Will use MyCustomExecutor
});
Advanced Executor
Full-Featured Executor
import { Executor, Task, ExecutionResult } from '@automagik/forge-sdk';
import axios from 'axios';
export class CustomAPIExecutor implements Executor {
name = 'custom-api-executor';
version = '1.0.0';
private apiKey: string;
private baseUrl: string;
private activeExecutions: Map<string, AbortController>;
constructor(config: { apiKey: string; baseUrl: string }) {
this.apiKey = config.apiKey;
this.baseUrl = config.baseUrl;
this.activeExecutions = new Map();
}
async execute(task: Task, options: any): Promise<ExecutionResult> {
const executionId = this.generateExecutionId();
const controller = new AbortController();
this.activeExecutions.set(executionId, controller);
try {
// Call your custom API
const response = await axios.post(
`${this.baseUrl}/execute`,
{
task: {
title: task.title,
description: task.description,
files: task.metadata?.files || []
},
options
},
{
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
signal: controller.signal
}
);
// Process response
const result = this.processResponse(response.data);
return {
success: true,
output: result.output,
filesChanged: result.filesChanged,
cost: result.cost || 0,
metadata: {
executionId,
duration: result.duration
}
};
} catch (error) {
return {
success: false,
error: error.message,
cost: 0
};
} finally {
this.activeExecutions.delete(executionId);
}
}
canHandle(task: Task): boolean {
// Handle tasks with specific metadata
return task.metadata?.executor === 'custom-api';
}
async estimateCost(task: Task): Promise<number> {
// Estimate based on task complexity
const complexity = this.calculateComplexity(task);
return complexity * 0.01; // $0.01 per complexity point
}
async cancel(executionId: string): Promise<void> {
const controller = this.activeExecutions.get(executionId);
if (controller) {
controller.abort();
this.activeExecutions.delete(executionId);
}
}
private generateExecutionId(): string {
return `exec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
}
private calculateComplexity(task: Task): number {
const descriptionLength = task.description?.length || 0;
const fileCount = task.metadata?.files?.length || 0;
return Math.ceil(descriptionLength / 100) + fileCount * 10;
}
private processResponse(data: any): any {
// Process your API response
return {
output: data.result,
filesChanged: data.files || [],
cost: data.cost,
duration: data.duration
};
}
}
Use Cases
Internal Company Tool
export class InternalToolExecutor implements Executor {
name = 'company-internal-tool';
version = '1.0.0';
async execute(task: Task, options: any): Promise<ExecutionResult> {
// Connect to internal tool
const tool = await this.connectToInternalTool();
// Run company-specific workflow
const result = await tool.generateCode({
spec: task.description,
template: options.template,
standards: 'company-standards-v2'
});
return {
success: true,
output: result.code,
filesChanged: result.files,
cost: 0 // Internal tool, no cost
};
}
canHandle(task: Task): boolean {
return task.labels.includes('company-tool');
}
private async connectToInternalTool() {
// Internal API connection
return {
generateCode: async (params: any) => {
// Implementation
return {
code: '// Generated code',
files: []
};
}
};
}
}
Proprietary Model
export class ProprietaryModelExecutor implements Executor {
name = 'proprietary-model';
version = '1.0.0';
private modelEndpoint: string;
async execute(task: Task, options: any): Promise<ExecutionResult> {
// Use your proprietary model
const response = await fetch(this.modelEndpoint, {
method: 'POST',
body: JSON.stringify({
prompt: this.buildPrompt(task),
temperature: options.temperature || 0.7
})
});
const result = await response.json();
return {
success: true,
output: result.generated_code,
filesChanged: this.extractFiles(result),
cost: result.cost || 0
};
}
canHandle(task: Task): boolean {
return task.metadata?.model === 'proprietary';
}
private buildPrompt(task: Task): string {
return `
Task: ${task.title}
Description: ${task.description}
Generate production-ready code following our standards.
`;
}
private extractFiles(result: any): string[] {
// Extract changed files from result
return result.files || [];
}
}
Hybrid Human-AI
export class HumanReviewExecutor implements Executor {
name = 'human-review';
version = '1.0.0';
async execute(task: Task, options: any): Promise<ExecutionResult> {
// Step 1: AI generates initial code
const aiResult = await this.runAI(task);
// Step 2: Send to human for review
const reviewRequest = await this.requestHumanReview({
task,
aiOutput: aiResult,
reviewers: options.reviewers || ['team-lead']
});
// Step 3: Wait for human approval
const approved = await this.waitForApproval(reviewRequest.id);
if (approved.status === 'approved') {
return {
success: true,
output: approved.finalCode,
filesChanged: aiResult.filesChanged,
cost: aiResult.cost,
metadata: {
aiGenerated: true,
humanReviewed: true,
reviewer: approved.reviewer
}
};
} else {
return {
success: false,
error: 'Human review rejected',
cost: aiResult.cost
};
}
}
canHandle(task: Task): boolean {
return task.priority === 'critical';
}
private async runAI(task: Task) {
// Use AI to generate initial code
return {
code: '// AI generated code',
filesChanged: [],
cost: 0.23
};
}
private async requestHumanReview(params: any) {
// Send to review system (Slack, email, etc.)
return { id: 'review_123' };
}
private async waitForApproval(reviewId: string) {
// Poll or wait for webhook
return {
status: 'approved',
finalCode: '// Reviewed code',
reviewer: 'john@company.com'
};
}
}
Configuration
Executor Config File
Create.forge/executors/my-executor.json:
{
"name": "my-custom-executor",
"enabled": true,
"config": {
"apiKey": "${CUSTOM_API_KEY}",
"baseUrl": "https://api.example.com",
"timeout": 60000,
"retries": 3
},
"priority": 10,
"capabilities": ["code-generation", "refactoring"],
"supportedLanguages": ["typescript", "python", "go"]
}
Load Executors
import { ForgeClient } from '@automagik/forge-sdk';
import { loadExecutorConfig } from './executor-loader';
const forge = new ForgeClient();
// Load from config
const executorConfig = loadExecutorConfig('.forge/executors');
for (const config of executorConfig) {
const executor = createExecutor(config);
forge.executors.register(executor);
}
Testing Custom Executors
Unit Tests
import { MyCustomExecutor } from './my-custom-executor';
describe('MyCustomExecutor', () => {
let executor: MyCustomExecutor;
beforeEach(() => {
executor = new MyCustomExecutor({
apiKey: 'test-key',
baseUrl: 'http://localhost:3000'
});
});
it('should execute task successfully', async () => {
const task = {
id: 'task_1',
title: 'Test task',
description: 'Test description',
labels: ['custom-executor']
};
const result = await executor.execute(task, {});
expect(result.success).toBe(true);
expect(result.output).toBeDefined();
});
it('should handle errors gracefully', async () => {
const task = {
id: 'task_2',
title: 'Failing task',
description: 'This will fail',
labels: ['custom-executor']
};
const result = await executor.execute(task, {});
expect(result.success).toBe(false);
expect(result.error).toBeDefined();
});
it('should estimate cost accurately', async () => {
const task = {
id: 'task_3',
title: 'Cost estimation test',
description: 'A'.repeat(1000), // 1000 chars
labels: []
};
const cost = await executor.estimateCost(task);
expect(cost).toBeGreaterThan(0);
});
});
Publishing Executors
NPM Package
// package.json
{
"name": "@your-org/forge-executor-custom",
"version": "1.0.0",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"peerDependencies": {
"@automagik/forge-sdk": "^0.4.0"
}
}
Usage by Others
# Install your executor
npm install @your-org/forge-executor-custom
import { CustomExecutor } from '@your-org/forge-executor-custom';
import { ForgeClient } from '@automagik/forge-sdk';
const forge = new ForgeClient();
forge.executors.register(new CustomExecutor({
apiKey: process.env.CUSTOM_API_KEY
}));
Best Practices
Error Handling
async execute(task: Task): Promise<ExecutionResult> {
try {
const result = await this.runTask(task);
return { success: true, ...result };
} catch (error) {
return {
success: false,
error: error.message,
cost: 0
};
}
}
Cost Tracking
async execute(task: Task): Promise<ExecutionResult> {
const startCost = await this.getCurrentCost();
// Execute task
const result = await this.runTask(task);
const endCost = await this.getCurrentCost();
return {
success: true,
output: result,
cost: endCost - startCost
};
}
Cancellation Support
private activeExecutions = new Map();
async cancel(executionId: string) {
const controller = this.activeExecutions.get(executionId);
if (controller) {
controller.abort();
}
}
Progress Updates
async execute(task: Task, options: any) {
const onProgress = options.onProgress;
onProgress?.({ stage: 'initializing', percent: 0 });
// ... work ...
onProgress?.({ stage: 'generating', percent: 50 });
// ... work ...
onProgress?.({ stage: 'complete', percent: 100 });
}
Next Steps
Specialized Agents
Combine with specialized agents
Webhooks & Events
Trigger executors via webhooks
API Reference
Integrate with Forge API
SDK Documentation
Full SDK reference

