Welcome to the final chapter of the FileWriteTool tutorial!
In the previous chapter, Ecosystem Integration (Side Effects), we ensured that when a file is written, the rest of your system (like VS Code and Linters) gets notified.
However, there is one final entity that needs to know what happened: You.
If the AI writes a file and the terminal just says Task Completed, you are left guessing. Did it write the whole file? Did it fix the bug or delete my code?
This chapter is about User Interface & Feedback. We will explore how we turn raw data into a beautiful, readable dashboard that shows you exactly what the AI did.
The Central Use Case:
You ask the AI: "Refactor utils.js to improve performance."
The Problem: The AI changes 5 lines of code in a 200-line file.
The Solution: We use a library called Ink (which lets us use React for Command Line Interfaces) to render different "Views" based on the action performed.
renderToolResultMessage)The core of our UI logic is a function that acts like a traffic cop. It looks at the result coming from the tool and decides which visual component to display.
The tool output (Output) generally has a type: either 'create' (new file) or 'update' (modified file).
// src/tools/FileWriteTool/UI.tsx
export function renderToolResultMessage(output: Output, ...args) {
// Check the type of operation
switch (output.type) {
case 'create':
// Render the "New File" view
return <FileWriteToolCreatedMessage {...output} />
case 'update':
// Render the "Diff" view
return <FileEditToolUpdatedMessage {...output} />
}
}
Explanation:
When the AI creates a brand new file, we don't need a "Diff" (because there was nothing there before). Instead, we want to see the content.
However, if the AI creates a 5,000-line log file, we don't want to flood your terminal.
The Strategy:
// Inside FileWriteToolCreatedMessage component
function FileWriteToolCreatedMessage({ filePath, content, verbose }) {
const numLines = countLines(content)
// Only show the first 10 lines to save space
const preview = content.split("\n").slice(0, 10).join("\n")
return (
<Box flexDirection="column">
<Text>Wrote {numLines} lines to {filePath}</Text>
<HighlightedCode code={preview} filePath={filePath} />
</Box>
)
}
Explanation:
HighlightedCode: This is a custom component that colors the text (like blue for keywords, green for strings) based on the file extension.slice(0, 10): This acts as a "preview," ensuring the UI stays clean.When an existing file is changed, seeing the whole file is useless. You only care about the change.
We use a "Structured Patch" (a fancy word for a Diff) to render this.
Visualizing the Output: Instead of code, think of it as a receipt of changes:
// Inside renderToolResultMessage 'update' case
case 'update': {
return (
<FileEditToolUpdatedMessage
filePath={filePath}
// Pass the diff data calculated in Chapter 1
structuredPatch={structuredPatch}
fileContent={originalFile}
/>
)
}
Why this is beginner-friendly: The user instantly sees impact. If they see a huge block of red text, they know the AI deleted a lot of code and can react immediately (perhaps checking if it was a mistake).
Let's look at how the data flows from the engine to your screen.
To give accurate feedback (e.g., "Wrote 15 lines"), we need a helper function. Computers see "lines" just as special characters (\n).
// src/tools/FileWriteTool/UI.tsx
const EOL = '\n'; // End Of Line character
export function countLines(content: string): number {
// Split the huge string into an array of lines
const parts = content.split(EOL);
// Adjust count if the file ends with a newline
return content.endsWith(EOL) ? parts.length - 1 : parts.length;
}
Explanation:
Sometimes, the tool fails (maybe due to the Safety checks in Chapter 3). The UI handles this gracefully too.
// src/tools/FileWriteTool/UI.tsx
export function renderToolUseRejectedMessage({ file_path, content }) {
// Show the user what the AI *tried* to do
return (
<WriteRejectionDiff
filePath={file_path}
content={content}
/>
)
}
Explanation:
Congratulations! You have completed the FileWriteTool tutorial.
We have built a complete, production-grade AI tool from scratch:
You now understand that an AI Tool is not just a function callβit is a full application stack that bridges the gap between an AI's "thought" and a computer's "action."
End of Tutorial.
Generated by Code IQ