Skip to content

Developer Guide ​

Quick reference for all APIs, hooks, components, and design tokens available in the Flightdeck web application.

Tech Stack ​

LayerTechnology
RuntimeNode.js with npm workspaces monorepo
FrontendReact 19.2, TypeScript 5.9, Vite 8, Tailwind CSS 4
State ManagementZustand 5 (stores in src/stores/), TanStack Query 5 (server state)
TestingVitest 4 + @testing-library/react 16, Playwright for E2E
BackendExpress 5, better-sqlite3, Drizzle ORM, WebSocket (ws)
Shared@flightdeck/shared — Zod schemas, shared types (workspace dependency)
Chartsvisx (d3-based)
BuildVite 8 with @vitejs/plugin-react, dev server proxies API/WS to :3001

API Endpoints ​

Agents ​

MethodEndpointDescription
GET/agentsList all agents
DELETE/agents/:idTerminate agent
POST/agents/:id/interruptInterrupt agent
POST/agents/:id/restartRestart agent
POST/agents/:id/messageSend message to agent
GET/agents/:id/messages?limit=200Get agent messages
POST/agents/:id/permissionResolve permission request

Sessions & Leads ​

MethodEndpointDescription
GET/leadList active leads
POST/lead/startStart new lead session
GET/lead/:id/progressGet lead progress
GET/lead/:id/dagGet task DAG
GET/lead/:id/decisionsGet pending decisions
POST/lead/:id/messageSend message to lead
GET/lead/:id/groupsList groups
POST/lead/:id/groupsCreate group
DELETE/lead/:idDelete lead

Decisions ​

MethodEndpointDescription
POST/decisions/:id/approveApprove decision
POST/decisions/:id/rejectReject decision
POST/decisions/:id/respondRespond with text
POST/decisions/:id/feedbackSend feedback

Natural Language ​

MethodEndpointDescription
GET/nl/commandsList all 30 NL commands
POST/nl/previewPreview command before executing
POST/nl/executeExecute NL command
POST/nl/undoUndo last command
GET/nl/suggestionsContext-aware suggestions

Onboarding ​

MethodEndpointDescription
GET/onboarding/statusGet mastery status
POST/onboarding/progressUpdate progress

Predictions (Removed) ​

Note: The Predictions API has been removed. The endpoints below are no longer active. See Removed Components for details.

Workflows (Removed) ​

Note: The Workflow Automation API has been removed. This feature was incomplete and is no longer available.

| GET | /commits | List commits | | GET | /commits/by-task/:taskId | Commits for task |

Conflicts ​

MethodEndpointDescription
GET/conflictsActive conflicts
POST/conflicts/:id/resolveResolve conflict
POST/conflicts/:id/dismissDismiss conflict
GET/conflicts/configGet config
PUT/conflicts/configUpdate config

Intent Rules (Removed) ​

Note: The Intent Rules API has been removed. Use the Oversight System instead.

Recovery (Removed) ​

Note: The Recovery/Handoff API has been removed. Agent crash recovery is now handled automatically by the orchestrator.

Oversight ​

MethodEndpointDescription
GET/config/yamlGet full config (includes oversight section)
PATCH/configUpdate oversight level and custom instructions

Playbooks (Removed) ​

Note: The Playbooks API has been removed.

Roles ​

MethodEndpointDescription
GET/rolesList roles
POST/rolesCreate role
PUT/roles/:idUpdate role
DELETE/roles/:idDelete role
POST/roles/testTest role config

Session Replay ​

MethodEndpointDescription
GET/api/replay/:leadId/keyframesGet keyframes
POST/replay/:leadId/shareCreate share link
GET/api/shared/:tokenGet shared replay

Notifications ​

MethodEndpointDescription
PUT/notifications/settingsUpdate notification preferences
GET/notifications/routingGet notification routing config

Analytics & Token Usage ​

MethodEndpointDescription
GET/api/costs/by-agentCosts by agent
GET/api/costs/by-taskCosts by task
GET/api/coordination/statusCoordination status
GET/api/coordination/timelineTimeline events

React Hooks ​

All hooks are in src/hooks/. Import example: import { useProjects } from '../hooks/useProjects';

Data Fetching Hooks ​

HookReturnsDescription
useApi(){ spawnAgent, terminateAgent, updateConfig, createRole, ... }Core API client
useFocusAgent(agentId){ data, loading, error, refresh }Polls agent activity, diffs, decisions
useDiffSummary(agentId){ summary, loading }Lightweight diff stats
useProjects(){ projects, loading }Fetch projects from REST API
useHistoricalAgents(projectId)Agent[]Derive agent data from keyframes for historical sessions
useConflicts(){ conflicts, activeConflicts, loading, resolve, dismiss }Conflict alerts
useConflictConfig(){ config, saveConfig }Detection config
useSessionReplay(leadId){ keyframes, worldState, playing, currentTime, seek, setSpeed, ... }Session replay

UI State Hooks ​

HookReturnsDescription
useCommandPalette(){ isOpen, open, close, toggle }⌘K palette state
useRecentCommands(){ recent, addRecent, clearRecent }Recent command history (localStorage)
useProgressiveRoutes(){ tier, visibleRoutes, hiddenRoutes }Progressive sidebar disclosure
useDashboardLayout(){ panels, allPanels, togglePanel, movePanel, reorderPanels }Dashboard panel config
useAutoScroll(ref, deps)voidAuto-scroll container to bottom
useIdleTimer(timeout){ isIdle }User inactivity detection
useAttachments(){ attachments, addAttachment, removeAttachment }File attachment state
useFileDrop(onDrop){ isDragOver, handleDragOver, handleDrop, ... }Drag-and-drop files
useGlassTooltips()voidGlobal [title] → glass tooltip conversion

WebSocket ​

typescript
import { useWebSocket } from '../hooks/useWebSocket';
import { sendWsMessage } from '../hooks/useWebSocket';

const ws = useWebSocket(); // Full WS client
sendWsMessage({ type: 'queue_open' }); // Send from anywhere

Shared Components ​

Import: import { EmptyState, SkeletonCard, SkeletonList, ErrorPage } from '../components/Shared';

EmptyState ​

tsx
<EmptyState
  icon="📊"
  title="No active sessions"
  description="Sessions appear when you create a project and agents start working."
  action={{ label: "Create Project →", onClick: () => navigate('/') }}
/>
// compact variant for inline use:
<EmptyState icon="📋" title="No items" compact />

Props: icon?: string, title: string, description?: string, action?: { label, onClick }, compact?: boolean, children?: ReactNode

SkeletonCard / SkeletonList ​

tsx
<SkeletonCard lines={3} showHeader showAvatar />
<SkeletonList count={5} />

SkeletonCard props: lines?: number, showHeader?: boolean, showAvatar?: boolean, className?: string SkeletonList props: count?: number, cardProps?: SkeletonCardProps, className?: string

ErrorPage ​

tsx
<ErrorPage
  title="Failed to load agents"
  message="The server is unavailable."
  detail="ECONNREFUSED localhost:3001"
  statusCode={503}
  onRetry={() => refetch()}
  onGoHome={() => navigate('/')}
/>

Props: title?: string, message?: string, detail?: string, statusCode?: number, onRetry?: () => void, onGoHome?: () => void


Motion System ​

Import: styles are global via src/styles/motion.css. Just add class names.

Animation Classes ​

ClassDurationUse for
motion-fade-in250msUniversal entry
motion-slide-in250msContent entering from left
motion-slide-in-right250msPanels, slide-overs
motion-slide-up250msToasts, bottom sheets
motion-slide-down250msDropdowns, notifications
motion-scale-in250msModals, popovers
motion-scale-in-spring450msPlayful/dramatic entrances
motion-pulse2s ∞Status indicators
motion-pulse-border2s ∞Attention-drawing borders
motion-glow2s ∞Active/selected state
motion-fade-out120msExit animation

Transition Presets ​

ClassDurationUse for
transition-micro120msHover states, toggles
transition-standard250msPanel transitions
transition-dramatic450msPage transitions

Stagger Pattern ​

tsx
{items.map((item, i) => (
  <div
    key={item.id}
    className="motion-stagger"
    style={{ '--stagger-index': i } as React.CSSProperties}
  >
    {/* content */}
  </div>
))}

Reduced Motion ​

All animations respect prefers-reduced-motion: reduce automatically (0ms duration).


Chart Theme ​

Import: styles are global via src/styles/chart-theme.css. Use CSS custom properties.

Series Colors ​

--chart-1 through --chart-8 — indigo, cyan, amber, purple, green, rose, blue, orange

Semantic Colors ​

VariableUse
--chart-successPositive/completed
--chart-warningCaution/amber
--chart-dangerError/red
--chart-infoInformational/blue
--chart-neutralInactive/muted

Agent Status Colors ​

--chart-running, --chart-idle, --chart-completed, --chart-failed, --chart-creating, --chart-terminated

Communication Edge Colors ​

--chart-edge-delegation, --chart-edge-message, --chart-edge-group, --chart-edge-broadcast, --chart-edge-report

Structural ​

--chart-grid, --chart-axis, --chart-tooltip-bg, --chart-tooltip-text, --chart-tooltip-border

Usage with visx ​

tsx
<LinePath
  stroke={`rgb(var(--chart-1))`}
  // ...
/>
<AxisBottom
  stroke={`rgb(var(--chart-axis))`}
  tickStroke={`rgb(var(--chart-axis))`}
  tickLabelProps={{ fill: `rgb(var(--chart-neutral))` }}
/>

All variables use RGB triplets for opacity support: rgba(var(--chart-1), 0.5).


Key Patterns ​

TanStack Query (Server State) ​

Use useQuery from @tanstack/react-query for all server data fetching:

typescript
import { useQuery } from '@tanstack/react-query';
import { apiFetch } from '../hooks/useApi';

const { data, isLoading, error } = useQuery({
  queryKey: ['agents', projectId],
  queryFn: ({ signal }) => apiFetch(`/api/agents?project=${projectId}`, { signal }),
  refetchInterval: 5000,
});

API Fetching (Low-Level) ​

typescript
import { apiFetch } from '../hooks/useApi';

// GET
const data = await apiFetch<MyType[]>('/projects');

// POST
await apiFetch('/nl/execute', {
  method: 'POST',
  body: JSON.stringify({ commandId: 'nl-pause-all' }),
});

Always handle both response shapes:

typescript
const data = await apiFetch<unknown>('/endpoint');
const items = Array.isArray(data) ? data : data?.items ?? [];

Zustand Store (Client State) ​

Stores live in src/stores/. Define stores with Zustand 5's create:

typescript
import { create } from 'zustand';

const useAppStore = create<AppState>((set, get) => ({
  agents: [],
  setAgents: (agents) => set({ agents }),
}));

// Testing: useAppStore.getState().setAgents([...])

Use selectors to avoid unnecessary re-renders:

typescript
// ✅ Good — re-renders only when agents changes
const agents = useAppStore(s => s.agents);

// ❌ Bad — re-renders on ANY store change
const { agents, config } = useAppStore();

Theme Classes ​

Always use th- prefixed classes, never raw colors:

  • Background: bg-th-bg, bg-th-bg-alt
  • Text: text-th-text, text-th-text-muted
  • Border: border-th-border, border-th-border-muted
  • Accent: bg-accent/20 text-accent
Documentation generated by AI

Documentation generated by AI