diff --git a/src/services/autoFix/autoFixIntegration.test.ts b/src/services/autoFix/autoFixIntegration.test.ts
new file mode 100644
index 00000000..a8d8fe44
--- /dev/null
+++ b/src/services/autoFix/autoFixIntegration.test.ts
@@ -0,0 +1,46 @@
+import { describe, expect, test } from 'bun:test'
+import { getAutoFixConfig } from './autoFixConfig.js'
+import { shouldRunAutoFix, buildAutoFixContext } from './autoFixHook.js'
+import { runAutoFixCheck } from './autoFixRunner.js'
+
+describe('autoFix end-to-end flow', () => {
+ test('full flow: config → shouldRun → check → context', async () => {
+ const config = getAutoFixConfig({
+ enabled: true,
+ lint: 'echo "error: unused" && exit 1',
+ maxRetries: 2,
+ timeout: 5000,
+ })
+ expect(config).not.toBeNull()
+ expect(shouldRunAutoFix('file_edit', config)).toBe(true)
+
+ const result = await runAutoFixCheck({
+ lint: config!.lint,
+ test: config!.test,
+ timeout: config!.timeout,
+ cwd: '/tmp',
+ })
+ expect(result.hasErrors).toBe(true)
+
+ const context = buildAutoFixContext(result)
+ expect(context).not.toBeNull()
+ expect(context).toContain('AUTO-FIX')
+ expect(context).toContain('unused')
+ })
+
+ test('full flow: no errors = no context', async () => {
+ const config = getAutoFixConfig({
+ enabled: true,
+ lint: 'echo "all clean"',
+ timeout: 5000,
+ })
+ const result = await runAutoFixCheck({
+ lint: config!.lint,
+ timeout: config!.timeout,
+ cwd: '/tmp',
+ })
+ expect(result.hasErrors).toBe(false)
+ const context = buildAutoFixContext(result)
+ expect(context).toBeNull()
+ })
+})
diff --git a/src/services/tools/toolHooks.ts b/src/services/tools/toolHooks.ts
index cb2ef8fa..98f4e0e2 100644
--- a/src/services/tools/toolHooks.ts
+++ b/src/services/tools/toolHooks.ts
@@ -29,6 +29,9 @@ import {
} from '../../utils/permissions/PermissionResult.js'
import { checkRuleBasedPermissions } from '../../utils/permissions/permissions.js'
import { formatError } from '../../utils/toolErrors.js'
+import { getAutoFixConfig } from '../autoFix/autoFixConfig.js'
+import { shouldRunAutoFix, buildAutoFixContext } from '../autoFix/autoFixHook.js'
+import { runAutoFixCheck } from '../autoFix/autoFixRunner.js'
import { isMcpTool } from '../mcp/utils.js'
import type { McpServerType, MessageUpdateLazy } from './toolExecution.js'
@@ -185,6 +188,40 @@ export async function* runPostToolUseHooks(
}
}
}
+
+ // Auto-fix: run lint/test if configured for this tool
+ const autoFixSettings = toolUseContext.getAppState().settings
+ const autoFixConfig = getAutoFixConfig(
+ autoFixSettings && typeof autoFixSettings === 'object' && 'autoFix' in autoFixSettings
+ ? (autoFixSettings as Record).autoFix
+ : undefined,
+ )
+ if (shouldRunAutoFix(tool.name, autoFixConfig) && autoFixConfig) {
+ try {
+ const cwd = toolUseContext.options?.cwd ?? process.cwd()
+ const autoFixResult = await runAutoFixCheck({
+ lint: autoFixConfig.lint,
+ test: autoFixConfig.test,
+ timeout: autoFixConfig.timeout,
+ cwd,
+ signal: toolUseContext.abortController.signal,
+ })
+ const autoFixContext = buildAutoFixContext(autoFixResult)
+ if (autoFixContext) {
+ yield {
+ message: createAttachmentMessage({
+ type: 'hook_additional_context',
+ content: [autoFixContext],
+ hookName: `AutoFix:${tool.name}`,
+ toolUseID,
+ hookEvent: 'PostToolUse',
+ }),
+ }
+ }
+ } catch (autoFixError) {
+ logError(autoFixError)
+ }
+ }
} catch (error) {
logError(error)
}