Skip to content

Events and Hooks

Events and Hooks

Fixwright emits events throughout the fixing process. Use these to monitor progress, log activity, and extend behavior.

Event Categories

Lifecycle Events

Control flow events for the overall process:

EventDescription
startedFixWright has started processing
stoppedFixWright has stopped
failure:receivedNew failure case received
failure:completedFailure case processing complete

Attempt Events

Events for each fix attempt:

EventDescription
attempt:startFix attempt started
attempt:analyzingAnalyzing the failure
attempt:fixingApplying a fix
attempt:validatingValidating the fix
attempt:successFix validated successfully
attempt:failedFix attempt failed
attempt:regressionFix caused new failures
attempt:new_errorDifferent error occurred

Agent Events

Events from the Claude Agent:

EventDescription
agent:startAgent started working
agent:completeAgent finished
agent:messageRaw SDK message
agent:errorAgent error
agent:thinkingAgent thinking (verbose)
agent:textAgent text output (verbose)
agent:tool_useTool invocation

Tool Events (Verbose Mode)

Specific tool usage events:

EventDescription
agent:readFile read operation
agent:editFile edit operation
agent:bashBash command
agent:grepSearch operation
agent:globGlob pattern match

Result Events

EventDescription
pr:createdPull request created
errorError occurred

Using Events

Basic Event Handling

import { FixWright } from '@korvol/fixwright';
const fixwright = new FixWright(config);
// Lifecycle
fixwright.on('started', () => console.log('FixWright started'));
fixwright.on('stopped', () => console.log('FixWright stopped'));
// Failures
fixwright.on('failure:received', (failureCase) => {
console.log('New failure:', failureCase.script.name);
});
fixwright.on('failure:completed', (summary) => {
console.log('Status:', summary.status);
console.log('Attempts:', summary.attempts);
});
// Attempts
fixwright.on('attempt:success', (attempt) => {
console.log('Fixed!', attempt.proposedFix);
});
fixwright.on('attempt:failed', (attempt) => {
console.log('Failed:', attempt.error);
});
// Results
fixwright.on('pr:created', (prUrl) => {
console.log('PR:', prUrl);
});
fixwright.on('error', (error) => {
console.error('Error:', error);
});

Monitoring Agent Activity

// Standard verbosity
fixwright.setVerbosity('normal');
fixwright.on('agent:tool_use', (toolName, input) => {
console.log(`Using ${toolName}:`, JSON.stringify(input).slice(0, 100));
});
// Verbose mode for debugging
fixwright.setVerbosity('verbose');
fixwright.on('agent:thinking', (text) => {
console.log('Thinking:', text.slice(0, 200));
});
fixwright.on('agent:read', (info) => {
console.log(`Read: ${info.filePath} (${info.linesRead} lines)`);
});
fixwright.on('agent:edit', (info) => {
console.log(`Edit: ${info.filePath}`);
console.log(` - ${info.oldString.slice(0, 50)}...`);
console.log(` + ${info.newString.slice(0, 50)}...`);
});

Event Data Structures

FixAttempt

interface FixAttempt {
id: string;
failureCaseId: string;
attemptNumber: number;
status: FixAttemptStatus;
startedAt: Date;
completedAt?: Date;
analysis?: FailureAnalysis;
proposedFix?: ProposedFix;
validationResult?: ValidationResult;
error?: string;
}

FixCompletionSummary

interface FixCompletionSummary {
failureCaseId: string;
status: 'fixed' | 'unfixable';
attempts: number;
totalDuration: number;
successfulAttempt?: FixAttempt;
prNumber?: number;
prUrl?: string;
reason?: string; // If unfixable
}

EditInfo

interface EditInfo {
filePath: string;
oldString: string;
newString: string;
timestamp: Date;
}

Verbosity Levels

Control which events are emitted:

fixwright.setVerbosity('quiet');

Events emitted:

  • Lifecycle events (started, stopped)
  • Result events (pr:created, error)
  • Completion events (failure:completed)

Common Patterns

Progress Logging

let currentAttempt = 0;
fixwright.on('attempt:start', (attempt) => {
currentAttempt = attempt.attemptNumber;
console.log(`\n=== Attempt ${currentAttempt} ===`);
});
fixwright.on('attempt:analyzing', () => {
console.log('[1/4] Analyzing failure...');
});
fixwright.on('attempt:fixing', () => {
console.log('[2/4] Applying fix...');
});
fixwright.on('attempt:validating', () => {
console.log('[3/4] Validating...');
});
fixwright.on('attempt:success', () => {
console.log('[4/4] Success!');
});
fixwright.on('attempt:failed', (attempt) => {
console.log(`[X] Attempt ${currentAttempt} failed: ${attempt.error}`);
});

Metrics Collection

const metrics = {
totalFailures: 0,
fixed: 0,
unfixable: 0,
totalAttempts: 0,
totalDuration: 0,
totalCost: 0,
};
fixwright.on('failure:received', () => {
metrics.totalFailures++;
});
fixwright.on('attempt:start', () => {
metrics.totalAttempts++;
});
fixwright.on('agent:complete', (result) => {
if (result.totalCostUsd) {
metrics.totalCost += result.totalCostUsd;
}
});
fixwright.on('failure:completed', (summary) => {
if (summary.status === 'fixed') {
metrics.fixed++;
} else {
metrics.unfixable++;
}
metrics.totalDuration += summary.totalDuration;
});
// Report metrics periodically
setInterval(() => {
console.log('Metrics:', metrics);
}, 60000);

Slack Notifications

async function notifySlack(message: string, color: string) {
await fetch(process.env.SLACK_WEBHOOK!, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
attachments: [{
color,
text: message,
}],
}),
});
}
fixwright.on('pr:created', (prUrl) => {
notifySlack(`New fix PR created: ${prUrl}`, 'good');
});
fixwright.on('failure:completed', (summary) => {
if (summary.status === 'unfixable') {
notifySlack(
`Could not fix ${summary.failureCaseId}: ${summary.reason}`,
'danger'
);
}
});

Debug Logging

import { writeFileSync, appendFileSync } from 'fs';
const logFile = `./logs/fixwright-${Date.now()}.log`;
function log(event: string, data: unknown) {
const entry = {
timestamp: new Date().toISOString(),
event,
data,
};
appendFileSync(logFile, JSON.stringify(entry) + '\n');
}
// Log all events
const events = [
'started', 'stopped', 'failure:received', 'failure:completed',
'attempt:start', 'attempt:analyzing', 'attempt:fixing',
'attempt:validating', 'attempt:success', 'attempt:failed',
'agent:tool_use', 'pr:created', 'error',
];
events.forEach(event => {
fixwright.on(event as any, (...args) => {
log(event, args);
});
});

Error Handling

Catching All Errors

fixwright.on('error', (error) => {
console.error('FixWright error:', error);
// Categorize errors
if (error.message.includes('ANTHROPIC')) {
console.error('API error - check your API key');
} else if (error.message.includes('git')) {
console.error('Git error - check repository access');
} else if (error.message.includes('GitHub')) {
console.error('GitHub error - check token permissions');
}
});

Graceful Shutdown

process.on('SIGINT', async () => {
console.log('Shutting down...');
await fixwright.stop();
process.exit(0);
});
process.on('uncaughtException', async (error) => {
console.error('Uncaught exception:', error);
await fixwright.stop();
process.exit(1);
});

Type-Safe Events

Events are fully typed:

import type { FixWrightEvents } from '@korvol/fixwright';
// TypeScript knows the event payload types
fixwright.on('attempt:success', (attempt) => {
// attempt is typed as FixAttempt
console.log(attempt.proposedFix?.filePath);
});
fixwright.on('agent:edit', (info) => {
// info is typed as EditInfo
console.log(info.filePath);
});

Next Steps