Skip to content

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:

NameTypeDescription
configResolvedAiFixConfigAI fixing configuration
failureSourceFailureSourceSource 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 only

Example 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:

NameTypeDescription
failureCaseFailureCaseFailure 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): void

Parameters:

NameTypeDescription
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 source
const source = new FileFailureSource('./failure-cases');
// Create fixing loop with full configuration
const 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 events
fixingLoop.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 failure
const failureCase: FailureCase = /* from Stepwright */;
await fixingLoop.processFailure(failureCase);

See Also