In the previous chapter, Worker Daemon (Service Orchestrator), we built a "Ghost in the Machine." We created a background service that silently records everything Claude does into a database.
But a database full of raw text is hard to read. If you want to know "What did Claude do while I was getting coffee?", you don't want to run SQL queries. You want a visual dashboard.
Command-line interfaces (CLIs) are great for speed, but terrible for situational awareness.
The Pilot Console is a React-based web application that serves as your "Flight Deck." It connects to the Worker Daemon via HTTP to visualize the data in real-time.
It allows you to:
Imagine Claude makes a mistake and deletes a file it shouldn't have.
rm command in a wall of text.localhost:3000, click "Sessions," and see a clean timeline. You spot the red "observation" icon where the deletion happened and see exactly why Claude did it.
The Console is a Single Page Application (SPA). The entry point is App.tsx. It acts as a traffic cop, deciding which "View" to show you based on the URL.
It uses a Router to map URLs to components.
// Simplified from console/src/ui/viewer/App.tsx
export function App() {
const { path } = useRouter(); // Get current URL
// Decide what to render based on the path
return (
<DashboardLayout>
{/* If path is '/', show Dashboard */}
{path === '/' && <DashboardView />}
{/* If path is '/sessions', show Timeline */}
{path === '/sessions' && <SessionsView />}
{/* If path is '/vault', show Skills */}
{path === '/vault' && <VaultView />}
</DashboardLayout>
);
}
How it helps: This structure keeps the application organized. Each "page" (Dashboard, Sessions, Vault) is its own isolated component.
The most important view is the Session Timeline. This turns raw database rows into a readable conversation history.
It needs to distinguish between two things:
The React component asks the Worker Daemon for data using fetch.
// Simplified from SessionTimeline.tsx
useEffect(() => {
async function fetchTimeline() {
// 1. Call the Daemon's API
const response = await fetch(`/api/sessions/${sessionId}/timeline`);
// 2. Convert JSON to JavaScript Object
const result = await response.json();
// 3. Save to state so React can draw it
setData(result);
}
fetchTimeline();
}, [sessionId]);
Explanation:
fetch: The browser sends a GET request to the Daemon running on your machine.setData: When data arrives, React detects the change and updates the screen automatically.Once we have the data, we loop through it to create the visual cards.
// Simplified from SessionTimeline.tsx
{data.timeline.map((item) => (
<div key={item.id} className="timeline-item">
{/* Show a different icon based on type */}
<Icon name={item.type === 'prompt' ? 'message' : 'brain'} />
{/* Show the content */}
<div className="content">
<p>{item.data.title || "Untitled Action"}</p>
<span className="timestamp">{formatTime(item.timestamp)}</span>
</div>
</div>
))}
Explanation:
map: This loops over every event in the history.item.type. If it's a "prompt", we might show a chat bubble icon. If it's an "observation" (tool use), we show a brain icon.
The Vault is where claude-pilot manages shared knowledge (commands, rules, and skills). The Frontend provides a UI to see what is installed.
In Vault/index.tsx, we handle the "Sync" button. This tells the Daemon to pull the latest skills from the internet (e.g., from a Git repository).
// Simplified from Vault/index.tsx
function VaultSyncButton({ isInstalling, onInstall }) {
return (
<button
className="btn btn-primary"
disabled={isInstalling} // Disable if busy
onClick={onInstall} // Trigger the sync
>
{isInstalling ? "Syncing..." : "Sync All"}
</button>
);
}
When you click this, it triggers a function that calls the API endpoint /api/vault/sync. The Daemon does the heavy lifting (git pull, npm install), and the UI simply spins a loading wheel until it finishes.
Let's visualize exactly what happens when you open the "Sessions" page in your browser.
The Pilot Console Viewer is the "face" of the operation.
Now that we have a working brain (Daemon) and a face (Console), we need to give Claude a "Memory." Just recording logs isn't enough; Claude needs to be able to search through code to understand context.
In the next chapter, we will build the search engine for Claude's memory.
Next: Vector Memory Sync (Semantic Search)
Generated by Code IQ