Welcome to the final chapter of our tutorial!
In the previous chapter, Interactive Picker UI, we built a nice menu that lets the user select exactly which piece of code or text they want.
Now we have the text in our hand. We are at the final mile. We need to get this text out of our program and into the user's computer so they can use it.
If you are running this tool on your own laptop, copying text is easy. But developers often use tools like this over SSH (connecting to a remote server).
Here is the nightmare scenario:
Why? Browsers and Terminals often block remote programs from touching your local clipboard for security reasons.
To solve this, we use a "Belt and Suspenders" strategy. We don't rely on just one method. We do two things at once to ensure the user always gets their data.
Our Output Management layer acts like a paranoid delivery service. It assumes the first delivery method might fail, so it always sends a backup.
We attempt to inject the text directly into the system clipboard using special terminal codes (OSC 52).
Ctrl+V immediately.
Simultaneously, we write the text to a temporary file on the disk (e.g., /tmp/claude/response.md).
By doing both, we guarantee success.
First, let's build the "reliable" backup method. We need a function that takes our text and saves it to a temporary folder.
We use Node.js built-in libraries fs (File System) and path.
// copy.tsx - Helper function
const COPY_DIR = join(tmpdir(), 'claude'); // e.g., /tmp/claude
async function writeToFile(text: string, filename: string): Promise<string> {
const filePath = join(COPY_DIR, filename);
// 1. Ensure the directory exists (create if missing)
await mkdir(COPY_DIR, { recursive: true });
// 2. Write the text to the file
await writeFile(filePath, text, 'utf-8');
// 3. Return the path so we can tell the user where it is
return filePath;
}
Explanation:
tmpdir(): Finds the system's temp folder automatically (works on Mac, Linux, and Windows).recursive: true: If the folder /tmp/claude doesn't exist, create it.
Now we create the main function that coordinates the dual strategy. This is called copyOrWriteToFile.
It attempts the clipboard copy, but regardless of whether that works or fails, it proceeds to write the file.
We use a helper setClipboard (which uses ANSI escape codes).
async function copyOrWriteToFile(text: string, filename: string) {
// Try to set the clipboard
const raw = await setClipboard(text);
// If the terminal supports it, write the code to stdout
if (raw) process.stdout.write(raw);
// ... continued below
Immediately after, we trigger the file write.
// ... continued
// Always write to a temp file as a fallback
try {
const filePath = await writeToFile(text, filename);
// Tell the user we did BOTH
return `Copied to clipboard...\nAlso written to ${filePath}`;
} catch {
// If file writing fails (rare), just mention the clipboard
return `Copied to clipboard`;
}
}
When the user selects an option in the Picker UI (from Chapter 4), this sequence occurs:
We have now seen every part of the copy command.
When the process finishes, the onDone callback is triggered with our status message. This exits the UI and leaves a helpful message in the user's terminal history.
> Copied to clipboard (450 characters, 12 lines)
> Also written to /tmp/claude/copy.tsx
Congratulations! You have walked through the complete architecture of a robust CLI command.
In this specific chapter, we learned about Output Management.
setClipboard with fs.writeFile to ensure the user never loses their data.
This concludes our tutorial series on building the copy command. You now understand how to build complex, interactive, and robust terminal tools using TypeScript and React!
Generated by Code IQ