Welcome to Chapter 4! ๐
In the previous chapter, Model Governance & Validation, we acted as "Security Guards," checking IDs and permissions before allowing a model change.
Now, we face a new challenge: Memory.
Imagine you tell the CLI, "I want to use the Opus model." The CLI says "Okay!" and runs your command. But five minutes later, when you run a new command, the CLI has forgotten everything and goes back to the default settings. That is frustrating, right?
We need a "Brain" that remembers your preferences even after you close the terminal. This is Application State Management.
By default, computer programs are like goldfish: they have very short memories. Variables in code only exist while the program is running.
// โ This variable dies when the command finishes
let currentModel = 'claude-3-opus';
To build a professional tool, we need:
In our project, we use a centralized store accessed via Hooks. If you have used React before, this will feel very familiar. If not, think of it as a magical library where you can check out books (data) and return them updated.
We have two main tools:
useAppState: To READ data.useSetAppState: To WRITE data.Let's look at how to use them to solve our use case: persisting the AI Model selection.
To see what the current model is, we don't look at a local variable. We ask the global store.
import { useAppState } from '../../state/AppState.js';
// Inside your component
const currentModel = useAppState(state => state.mainLoopModel);
console.log(currentModel); // Outputs: "claude-3-5-sonnet"
state: Represents the entire configuration of the app.s => s.model): We specifically ask for just the mainLoopModel. This is efficient because our component only re-renders if this specific value changes.To change the setting, we need the "Setter."
import { useSetAppState } from '../../state/AppState.js';
// 1. Get the tool
const setAppState = useSetAppState();
// 2. Use the tool to update
setAppState(prevState => ({
...prevState, // Keep all other settings (Fast Mode, etc.)
mainLoopModel: 'opus' // Only change the model
}));
prevState: We always look at the current state before making changes to avoid overwriting other data....prevState): This is crucial! It copies all existing settings so we don't accidentally delete them while changing the model.
Let's revisit the ModelPickerWrapper from React-based Command Implementation. Now we can see exactly how it connects to the brain.
We need the UI to show which model is currently active (highlighted).
function ModelPickerWrapper({ onDone }) {
// Read the current state so the UI knows where to start
const activeModel = useAppState(s => s.mainLoopModel);
const setAppState = useSetAppState();
// ... render logic
}
When the user presses "Enter" on a new model, we save it.
const handleSelect = (newModel) => {
// Write to the global brain
setAppState(prev => ({
...prev,
mainLoopModel: newModel
}));
onDone(`Saved! You are now using ${newModel}`);
};
State management isn't just about saving one string. Sometimes, changing one setting affects another.
Example: "Fast Mode" is a setting that makes the AI faster but less smart.
// Inside handleSelect...
if (isFastModeOn && !supportsFastMode(newModel)) {
// Update TWO things at once
setAppState(prev => ({
...prev,
mainLoopModel: newModel,
fastMode: false // Force turn off
}));
}
This ensures our application state is always consistent and valid.
You might be wondering: "Where does this data actually go?"
When you call setAppState, a few things happen in the background to ensure the data persists across sessions.
~/.claude/config.json).
While we consume useAppState, the backend of this system is likely built on a library designed for persistent state.
In our file state/AppState.js, the code looks something like this (simplified):
import { create } from 'zustand'; // A popular state library
import { persist } from 'zustand/middleware';
export const useAppState = create(
persist(
(set) => ({
mainLoopModel: 'claude-3-5-sonnet', // Default
fastMode: false,
// ... other settings
}),
{
name: 'claude-cli-storage', // Key for local storage/file system
}
)
);
Because of this structure:
In this chapter, we gave our application a Memory.
useAppState allows us to read global settings from anywhere.useSetAppState allows us to update settings safely.We now have a command that exists (Chapter 1), has a UI (Chapter 2), is secure (Chapter 3), and remembers its settings (Chapter 4).
But how do we know if anyone is actually using it? Is the "Opus" model popular, or does everyone stick to "Sonnet"? To answer this, we need to track usage data.
Next Chapter: Analytics & Telemetry
Generated by Code IQ