Welcome to the ExitWorktreeTool tutorial! This is the first chapter in our journey to build a robust tool that helps AI agents manage temporary coding environments.
Imagine you check into a hotel room (a temporary worktree) to get some work done. You unpack your bags, rearrange the furniture, and write some code. When you are finished, you can't just teleport out; you need a formal process to check out.
If you just "leave" (change directories):
ExitWorktree is the "Check Out" button. It handles the logistics of leaving a temporary workspace safely and restoring the original environment.
User: "I've fixed the bug. Please delete this temporary environment and go back to the main project."
AI's Goal:
Before the AI can use a tool, it needs to know what the tool does and when to use it. We define this in a prompt string.
Think of this as the API documentation written specifically for the AI.
// prompt.ts
export function getExitWorktreeToolPrompt(): string {
return `Exit a worktree session created by EnterWorktree...
## When to Use
- The user explicitly asks to "exit", "go back", or end the session.
- Do NOT call this proactively โ only when the user asks.
## Behavior
- Restores the session's working directory to the original one.
- Clears caches so the session state reflects the original directory.`
}
Explanation: This text tells the AI: "Don't press this button unless the user tells you to. When you do press it, I will take you back to where you started."
To use the tool, the AI must fill out a specific "form" (the parameters). We define this using zod.
There are two main knobs the AI can turn:
keep the files or remove them?remove, are we allowed to delete unsaved work?// ExitWorktreeTool.ts (Simplified)
const inputSchema = lazySchema(() =>
z.strictObject({
// The main switch: Keep the folder or delete it?
action: z.enum(['keep', 'remove']),
// The safety override key
discard_changes: z.boolean().optional()
})
)
Example Inputs:
{ "action": "remove" }{ "action": "remove", "discard_changes": true }{ "action": "keep" }After the tool runs, it hands a "receipt" back to the AI. This helps the AI confirm to the user that the job is done.
// ExitWorktreeTool.ts (Simplified)
const outputSchema = lazySchema(() =>
z.object({
action: z.enum(['keep', 'remove']),
originalCwd: z.string(), // Where we ended up
worktreePath: z.string(), // What we just left
message: z.string(), // A human-readable summary
})
)
Here is a high-level view of what happens when the AI presses the button.
We wrap all this logic in a Tool definition. This bundles the name, description, schemas, and the executable code into one package.
We use a builder function to create the tool object.
// ExitWorktreeTool.ts
export const ExitWorktreeTool = buildTool({
name: 'ExitWorktree',
userFacingName: () => 'Exiting worktree',
// Hooking up the parts we defined earlier
inputSchema: inputSchema,
outputSchema: outputSchema,
async prompt() { return getExitWorktreeToolPrompt() },
// ... continued below
Explanation: This registers the tool with the system, attaching the "Manual" (prompt) and the "Form" (schemas).
Before doing any real work, we check if the request is valid. This prevents the tool from crashing or deleting the wrong things.
// Inside buildTool ...
async validateInput(input) {
const session = getCurrentWorktreeSession()
// Guard: Are we actually in a worktree session?
if (!session) {
return { result: false, message: 'No active worktree session.' }
}
// We will cover safety checks in Chapter 2!
return { result: true }
},
Explanation: If getCurrentWorktreeSession() returns null, it means we aren't in a temporary mode. The tool effectively says, "I can't exit a room I haven't entered."
This is where the magic happens. The call function is triggered only if validation passes.
// Inside buildTool ...
async call(input) {
const session = getCurrentWorktreeSession()
// Case 1: Keep the files, just leave the room
if (input.action === 'keep') {
await keepWorktree() // Helper to update state
restoreSessionToOriginalCwd(session.originalCwd, ...)
return { data: { /* ... success receipt ... */ } }
}
// Case 2: Remove everything (Action: remove)
await cleanupWorktree() // Helper to delete files
restoreSessionToOriginalCwd(session.originalCwd, ...)
return { data: { /* ... success receipt ... */ } }
}
});
Explanation:
action input.keepWorktree or cleanupWorktree) to handle the file system.restoreSessionToOriginalCwd to reset the AI's internal state (like its current working directory).We have created the Interface for our tool.
keep or remove.However, there is a dangerous gap in our logic above. What if the user asks to "remove", but they forgot to save their code? Our current simple logic might delete it all!
In the next chapter, we will build the protection mechanism to prevent this data loss.
Next Chapter: Safety Gates (Change Detection)
Generated by Code IQ