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:
- Inspects the accumulated
changedFilesfrom all executed tools - If any files were modified, automatically enqueues a
run_file_change_hookstool call - 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
changedFilesarray in its result - Automatic hook scheduling: The step processor in
run-agent-step.tsinsertsrun_file_change_hookscalls without LLM involvement - Command execution: The handler in
run-file-change-hooks.tsruns user-configured commands against the changed files - Context injection: Hook results flow through
tool-executor.tsinto the prompt stream viaprompt-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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →