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

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

// 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), 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) 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). 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)
// 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. 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), 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—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 inserts run_file_change_hooks calls without LLM involvement
  • Command execution: The handler in run-file-change-hooks.ts runs user-configured commands against the changed files
  • Context injection: Hook results flow through tool-executor.ts into the prompt stream via 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, 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). This shared location ensures consistency between the runtime's hook invocation and the client's command execution expectations.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →