In the previous chapter, Session Persistence & Recovery, we gave our AI a long-term memory, allowing it to pick up conversations exactly where it left off.
However, even the smartest AI with the best memory will fail if the computer it is running on is broken. What if git isn't installed? What if the configuration files have syntax errors? What if the AI doesn't have permission to write files?
In this final chapter, we will build the Diagnostic System (often called "The Doctor"). This is the application's mechanic, performing health checks to ensure everything is running smoothly.
Complex applications rely on many moving parts:
git, ripgrep, node.
If one of these is missing, the application might crash silently or throw a confusing error like ENOENT.
The Solution: Instead of waiting for a crash, we proactively run a battery of tests. We present the results in a clean dashboard so the user can fix issues immediately.
doctor Command
Imagine a user types screens doctor. They see a screen that checks three critical areas:
ripgrep) working?The Doctor screens runs these checks in the background and updates the UI live as results come in.
A function that queries the Operating System. It asks: "Hey, what version of Node is running?" or "Can I execute rg --version?"
This is a specific shape of data that holds the health report. It usually includes:
OK | Warning | ErrorSince checking the disk and running commands takes time, the Doctor must handle loading states gracefully. We don't want the UI to freeze while we check the file system.
We will explore Doctor.tsx. This component acts as the dashboard for all our health checks.
When the component mounts, we immediately fire off our asynchronous checks. We use useEffect for this.
// Inside Doctor.tsx
const [diagnostic, setDiagnostic] = useState(null);
useEffect(() => {
// 1. Run the system check
getDoctorDiagnostic().then(setDiagnostic);
// 2. Run other async checks (Config, Locks, etc.)
checkContextWarnings(...).then(setContextWarnings);
}, []);
Explanation:
getDoctorDiagnostic(): This is a utility function (imported helper) that runs shell commands to check the OS.setDiagnostic: We store the result in the local component state.[] dependency array ensures this runs only once when the screen appears.While the computer is thinking, we need to show feedback.
// Inside Doctor.tsx
if (!diagnostic) {
return (
<Pane>
<Text dimColor>Checking installation statusβ¦</Text>
</Pane>
);
}
Explanation:
diagnostic has data, we show a dim loading text.Once the data arrives, we display it using our layout components.
// Inside Doctor.tsx
return (
<Box flexDirection="column">
<Text bold>Diagnostics</Text>
<Text>β Version: {diagnostic.version}</Text>
<Text>β Path: {diagnostic.installationPath}</Text>
{/* Conditional Rendering for Warnings */}
{diagnostic.warnings.map(w => (
<Text color="warning">Warning: {w.issue}</Text>
))}
</Box>
);
Explanation:
.map) to render dynamic arrays of warnings.How does the Doctor gather all this information? It acts as a bridge between the System and the Global State.
Doctor component initializes.getDoctorDiagnostic to check binaries (Git, Ripgrep).The Doctor isn't just checking the binary; it's also checking the Application Logic.
We rely on the data loaded in Agent & Tool Configuration. If the user made a typo in their agent config file, we display it here.
// Inside Doctor.tsx
const agentInfo = useAppState(s => s.agentDefinitions);
{agentInfo?.failedFiles?.length > 0 && (
<Box flexDirection="column">
<Text bold color="error">Agent Parse Errors</Text>
{agentInfo.failedFiles.map(file => (
<Text key={file.path}>β {file.path}: {file.error}</Text>
))}
</Box>
)}
Explanation:
agentDefinitions from the global store.failedFiles.color="error" (usually red) to draw attention to the broken files.Sometimes users set limits (like "Max Output Tokens") that are invalid. The Doctor validates these against allowed ranges.
// Inside Doctor.tsx (simplified)
const envVars = [
{ name: "BASH_MAX_OUTPUT", default: 4000 },
// ... other vars
];
const envValidationErrors = envVars
.map(v => validateBoundedIntEnvVar(v.name, v.default))
.filter(v => v.status !== "valid");
Explanation:
envValidationErrors has items, we render a warning section in the UI.In this chapter, we built the Diagnostic System. We learned:
Congratulations! You have navigated through the architecture of a complex React-based CLI application.
We have covered:
You now possess the knowledge to build, debug, and extend the Screens project. Happy coding!
Generated by Code IQ