# How File Change Hooks Trigger and Propagate in the Freebuff Agent Runtime

> Learn how Freebuff agent runtime triggers file change hooks after tool execution. Discover how changed file paths propagate and hook results enhance LLM decision-making.

- Repository: [Codebuff/freebuff](https://github.com/CodebuffAI/freebuff)
- Tags: internals
- Published: 2026-08-20

---

**Freebuff's agent runtime automatically triggers `run_file_change_hooks` after any file-modifying tool executes, propagating changed file paths through the step processor and injecting hook results back into the LLM context for agent decision-making.**

File change hooks are a core mechanism in the [CodebuffAI/freebuff](https://github.com/CodebuffAI/freebuff) agent runtime that enables automated quality checks, testing, and validation following every code modification. This article explains the complete flow—from file modification detection to hook result consumption—based on the actual source implementation.

## How File Changes Are Detected and Tracked

Every file-modifying tool in Freebuff's runtime produces a structured result that includes the paths of affected files.

When an agent invokes tools like `write_file` or `str_replace`, the runtime records which paths were written or replaced. The tool result payload contains a **`"changedFiles"`** array, defined in the tool schema at [[`common/src/tools/params/tool/run-file-change-hooks.ts`](https://github.com/CodebuffAI/freebuff/blob/main/common/src/tools/params/tool/run-file-change-hooks.ts)](https://github.com/CodebuffAI/freebuff/blob/main/common/src/tools/params/tool/run-file-change-hooks.ts):

```typescript
// Tool result structure includes changedFiles array
{
  "type": "tool_result",
  "tool_use_id": "...",
  "content": {...},
  "changedFiles": ["src/utils.ts", "tests/utils.test.ts"]
}

```

This array accumulates across multiple tool calls within a single step, ensuring no modification goes unnoticed.

## Automatic Hook Insertion in the Step Processor

The runtime does not rely on the LLM to remember triggering hooks. Instead, the **step processor** handles this automatically.

In [[`packages/agent-runtime/src/run-agent-step.ts`](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/run-agent-step.ts)](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/run-agent-step.ts), the runtime performs the following after a step finishes:

1. Inspects the accumulated `changedFiles` from all executed tools
2. If any files were modified, automatically enqueues a `run_file_change_hooks` tool call
3. Passes the complete list of changed files as parameters

This insertion happens **before** the next LLM prompt is generated. The agent cannot bypass the hook because it is injected as a mandatory tool call, not presented as an optional choice.

## Hook Execution Through the Tool Dispatcher

Once enqueued, the `run_file_change_hooks` call flows through Freebuff's standard tool execution pipeline.

The **tool executor** at [[`packages/agent-runtime/src/tools/tool-executor.ts`](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/tool-executor.ts)](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/tool-executor.ts) dispatches the call to its dedicated handler at [[`packages/agent-runtime/src/tools/handlers/tool/run-file-change-hooks.ts`](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/run-file-change-hooks.ts)](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/tools/handlers/tool/run-file-change-hooks.ts). The handler:

- Reads the user's **CodebuffConfig** to determine which commands to run
- Translates the hook request into client-side command execution
- Collects output from each configured command (e.g., `npm test`, `eslint .`, `tsc --noEmit`)

```typescript
// Conceptual handler flow
const config = await loadCodebuffConfig();
const commands = config.fileChangeHooks || [];
const results = await Promise.all(
  commands.map(cmd => execCommand(cmd, { files: changedFiles }))
);
return { success: results.every(r => r.exitCode === 0), details: results };

```

The handler returns a structured result that captures exit codes, stdout, stderr, and execution duration for each command.

## Result Propagation Back to the Agent

Hook results do not disappear into a log file—they become part of the agent's working context.

The handler's response is packaged as a **tool result** and appended to the agent's message history by [`tool-executor.ts`](https://github.com/CodebuffAI/freebuff/blob/main/tool-executor.ts). Then, in [[`packages/agent-runtime/src/prompt-agent-stream.ts`](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/prompt-agent-stream.ts)](https://github.com/CodebuffAI/freebuff/blob/main/packages/agent-runtime/src/prompt-agent-stream.ts), this history is included when generating the next LLM prompt.

This design enables the agent to make context-aware decisions:

- **Success scenario**: "All tests passed—continuing with the next refactoring."
- **Failure scenario**: "Lint errors detected in [`src/utils.ts`](https://github.com/CodebuffAI/freebuff/blob/main/src/utils.ts)—will fix before proceeding."
- **Partial success**: "Type check passed but tests failed—investigating test regression."

## Agent Reaction Patterns

Because hook results are now embedded in the conversation, the agent can respond through several patterns:

- **Continue execution**: Proceed with additional file modifications based on clean hook results
- **Call `end_turn`**: Terminate if the task is complete and all checks pass
- **Spawn sub-agents**: Delegate failure recovery to specialized agents for complex fix scenarios

The runtime places no artificial restriction on agent behavior—the same tool set remains available, but the agent now operates with full awareness of the hook outcomes.

## Summary

- **File modification detection**: Every write tool populates a `changedFiles` array in its result
- **Automatic hook scheduling**: The step processor in [`run-agent-step.ts`](https://github.com/CodebuffAI/freebuff/blob/main/run-agent-step.ts) inserts `run_file_change_hooks` calls without LLM involvement
- **Command execution**: The handler in [`run-file-change-hooks.ts`](https://github.com/CodebuffAI/freebuff/blob/main/run-file-change-hooks.ts) runs user-configured commands against the changed files
- **Context injection**: Hook results flow through [`tool-executor.ts`](https://github.com/CodebuffAI/freebuff/blob/main/tool-executor.ts) into the prompt stream via [`prompt-agent-stream.ts`](https://github.com/CodebuffAI/freebuff/blob/main/prompt-agent-stream.ts)
- **Agent autonomy**: Results become part of conversation history, enabling conditional logic and failure recovery

## Frequently Asked Questions

### What triggers a file change hook in Freebuff?

Any tool that modifies the filesystem—including `write_file`, `str_replace`, and similar operations—triggers the mechanism. The runtime automatically detects these modifications through the `changedFiles` array in tool results and schedules the hook without requiring explicit agent invocation.

### Can agents skip or disable file change hooks?

No. Hook insertion occurs at the runtime level in [`run-agent-step.ts`](https://github.com/CodebuffAI/freebuff/blob/main/run-agent-step.ts), before the LLM receives its next prompt. This architecture ensures consistent quality checks across all agent operations. Hooks can only be disabled by reconfiguring the user's **CodebuffConfig**, not through agent prompting.

### How do hook failures affect agent execution?

Hook failures propagate as structured tool results that the agent can interpret. The agent receives exit codes, error output, and affected file information, then decides whether to fix issues, abort, or escalate. The runtime does not automatically terminate on failure—it delegates the decision to the agent's reasoning loop.

### Where is the file change hook tool schema defined?

The parameter schema and TypeScript types reside in [[`common/src/tools/params/tool/run-file-change-hooks.ts`](https://github.com/CodebuffAI/freebuff/blob/main/common/src/tools/params/tool/run-file-change-hooks.ts)](https://github.com/CodebuffAI/freebuff/blob/main/common/src/tools/params/tool/run-file-change-hooks.ts). This shared location ensures consistency between the runtime's hook invocation and the client's command execution expectations.