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) }