FixingLoop Class
The main class for processing failure cases and applying AI-powered fixes using the Claude Agent SDK.
Import
import { FixingLoop, FileFailureSource } from '@autowright/fixwright';Constructor
constructor(config: ResolvedAiFixConfig, failureSource: FailureSource)Parameters:
| Name | Type | Description |
|---|---|---|
config | ResolvedAiFixConfig | AI fixing configuration |
failureSource | FailureSource | Source for failure cases |
Example:
import { FixingLoop, FileFailureSource } from '@autowright/fixwright';
const fixingLoop = new FixingLoop({ ai: { apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-20250514', }, git: { baseBranch: 'main', fixBranchPrefix: 'fix/stepwright-', }, github: { token: process.env.GITHUB_TOKEN!, owner: 'your-org', repo: 'your-repo', }, validation: { stepwrightPath: 'npx stepwright', timeout: 120000, }, fixing: { maxAttempts: 3, maxRetriesPerAttempt: 2, }, workDir: process.cwd(),}, new FileFailureSource('./failure-cases'));Configuration Options
AI Configuration (Claude Agent SDK)
The ai configuration controls how the Claude Agent SDK is used for analyzing and fixing failures.
interface AiConfig { // Required apiKey: string; // Anthropic API key
// Model Selection model?: string; // Default: 'claude-sonnet-4-20250514' fallbackModel?: string; // Fallback if primary fails
// Token Limits maxTokens?: number; // Max tokens for responses maxThinkingTokens?: number; // Max tokens for thinking maxTurns?: number; // Max agent turns (default: 20)
// Budget Control maxBudgetUsd?: number; // Max budget in USD
// Temperature temperature?: number; // 0-1, controls randomness
// Permissions permissionMode?: PermissionMode; // Tool permission handling allowDangerouslySkipPermissions?: boolean;
// Tools allowedTools?: string[]; // Allowed tool names disallowedTools?: string[]; // Blocked tool names tools?: string[] | { type: 'preset'; preset: 'claude_code' };
// System Prompt systemPrompt?: string | { type: 'preset'; preset: 'claude_code'; append?: string; };
// MCP Servers mcpServers?: Record<string, McpServerConfig>;
// Advanced betas?: SdkBeta[]; // Beta features settingSources?: SettingSource[]; // Config sources additionalDirectories?: string[]; // Extra accessible dirs env?: Record<string, string>; // Environment variables executable?: JsRuntime; // 'node' | 'bun' | 'deno' executableArgs?: string[]; // Runtime arguments
// Streaming includePartialMessages?: boolean; // Stream partial messages enableFileCheckpointing?: boolean; // Track file changes
// Callbacks stderr?: (data: string) => void; // stderr handler canUseTool?: ToolPermissionFn; // Custom tool filtering}Permission Modes
type PermissionMode = | 'default' // Ask for permission on sensitive ops | 'acceptEdits' // Auto-accept file edits | 'bypassPermissions' // Skip all permission checks | 'plan'; // Planning mode onlyExample with permissions:
const fixingLoop = new FixingLoop({ ai: { apiKey: process.env.ANTHROPIC_API_KEY!, permissionMode: 'bypassPermissions', allowDangerouslySkipPermissions: true, allowedTools: ['Read', 'Edit', 'Write', 'Glob', 'Grep', 'Bash'], disallowedTools: ['WebSearch'], // Block web searches }, // ... other config}, source);MCP Server Configuration
Connect to MCP (Model Context Protocol) servers for extended capabilities:
type McpServerConfig = | McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig;
interface McpStdioServerConfig { type?: 'stdio'; command: string; args?: string[]; env?: Record<string, string>;}
interface McpSSEServerConfig { type: 'sse'; url: string; headers?: Record<string, string>;}
interface McpHttpServerConfig { type: 'http'; url: string; headers?: Record<string, string>;}Example with MCP:
const fixingLoop = new FixingLoop({ ai: { apiKey: process.env.ANTHROPIC_API_KEY!, mcpServers: { 'my-server': { type: 'stdio', command: 'node', args: ['./my-mcp-server.js'], }, 'remote-server': { type: 'sse', url: 'https://my-server.com/mcp', headers: { 'Authorization': 'Bearer token' }, }, }, }, // ... other config}, source);Git Configuration
interface GitConfig { baseBranch: string; // e.g., 'main' fixBranchPrefix: string; // e.g., 'fix/stepwright-' commitAuthor?: { name: string; email: string; };}GitHub Configuration
interface GitHubConfig { token: string; // GitHub PAT owner: string; // Repository owner repo: string; // Repository name}Validation Configuration
interface ValidationConfig { stepwrightPath: string; // Path to stepwright CLI timeout?: number; // Validation timeout (ms) retries?: number; // Max validation retries}Fixing Configuration
interface FixingConfig { maxAttempts: number; // Max fix attempts per failure maxRetriesPerAttempt: number; // Max retries within attempt cooldownMs?: number; // Delay between attempts}Methods
processFailure(failureCase)
Process a single failure case.
async processFailure(failureCase: FailureCase): Promise<FixAttempt>Parameters:
| Name | Type | Description |
|---|---|---|
failureCase | FailureCase | Failure case to process |
Returns: Promise<FixAttempt>
Example:
import { FailureCase } from '@autowright/shared';
const failureCase: FailureCase = JSON.parse( await fs.readFile('./failure.json', 'utf-8'));
const attempt = await fixingLoop.processFailure(failureCase);console.log('Result:', attempt.status);setVerbosity(level)
Set the verbosity level for output.
setVerbosity(level: VerbosityLevel): voidParameters:
| Name | Type | Description |
|---|---|---|
level | 'quiet' | 'normal' | 'verbose' | Verbosity level |
Events
FixingLoop extends EventEmitter and emits the following events:
Attempt Lifecycle Events
fixingLoop.on('attempt:start', (attempt: FixAttempt) => void);fixingLoop.on('attempt:analyzing', (attempt: FixAttempt) => void);fixingLoop.on('attempt:fixing', (attempt: FixAttempt) => void);fixingLoop.on('attempt:validating', (attempt: FixAttempt) => void);fixingLoop.on('attempt:success', (attempt: FixAttempt) => void);fixingLoop.on('attempt:failed', (attempt: FixAttempt) => void);fixingLoop.on('attempt:regression', (attempt: FixAttempt) => void);fixingLoop.on('attempt:new_error', (attempt: FixAttempt, newError: string) => void);fixingLoop.on('failure:completed', (summary: FixCompletionSummary) => void);Agent Events
fixingLoop.on('agent:start', () => void);fixingLoop.on('agent:complete', (result: AgentFixResult) => void);fixingLoop.on('agent:message', (message: SDKMessage) => void);fixingLoop.on('agent:error', (error: Error) => void);fixingLoop.on('agent:thinking', (text: string) => void);fixingLoop.on('agent:text', (text: string) => void);fixingLoop.on('agent:tool_use', (toolName: string, input: unknown) => void);Tool Events (Verbose Mode)
fixingLoop.on('agent:read', (info: ReadInfo) => void);fixingLoop.on('agent:edit', (info: EditInfo) => void);fixingLoop.on('agent:bash', (info: BashInfo) => void);fixingLoop.on('agent:grep', (info: GrepInfo) => void);fixingLoop.on('agent:glob', (info: GlobInfo) => void);Result Events
fixingLoop.on('pr:created', (prUrl: string) => void);fixingLoop.on('error', (error: Error) => void);Complete Example
import { FixingLoop, FileFailureSource } from '@autowright/fixwright';import type { FailureCase } from '@autowright/shared';
// Create failure sourceconst source = new FileFailureSource('./failure-cases');
// Create fixing loop with full configurationconst fixingLoop = new FixingLoop({ ai: { apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-sonnet-4-20250514', maxTurns: 30, permissionMode: 'bypassPermissions', allowDangerouslySkipPermissions: true, systemPrompt: { type: 'preset', preset: 'claude_code', append: 'Focus on fixing Playwright selectors and timing issues.', }, }, git: { baseBranch: 'main', fixBranchPrefix: 'fix/stepwright-', commitAuthor: { name: 'Fixwright Bot', email: 'fixwright@example.com', }, }, github: { token: process.env.GITHUB_TOKEN!, owner: 'my-org', repo: 'my-repo', }, validation: { stepwrightPath: 'npx stepwright', timeout: 120000, retries: 2, }, fixing: { maxAttempts: 3, maxRetriesPerAttempt: 2, cooldownMs: 1000, }, workDir: process.cwd(), verbosity: 'normal',}, source);
// Listen to eventsfixingLoop.on('attempt:start', (a) => { console.log(`Starting attempt ${a.attemptNumber}`);});
fixingLoop.on('agent:tool_use', (tool, input) => { console.log(`Agent using: ${tool}`);});
fixingLoop.on('attempt:success', (a) => { console.log('Fix successful!'); console.log('Cause:', a.analysis?.causeType); console.log('Explanation:', a.analysis?.explanation);});
fixingLoop.on('pr:created', (url) => { console.log('PR created:', url);});
fixingLoop.on('error', (error) => { console.error('Error:', error);});
// Process a failureconst failureCase: FailureCase = /* from Stepwright */;await fixingLoop.processFailure(failureCase);See Also
- AgentFixer - Direct agent access
- FailureSource - Custom sources
- Types - All type definitions