- Introduced new planning files including PROJECT.md, REQUIREMENTS.md, ROADMAP.md, and STATE.md to outline the vision and requirements for SSH multi-context workspaces. - Added ARCHITECTURE.md and CONCERNS.md to detail the codebase structure and address technical debt, known bugs, and security considerations. - Created CONVENTIONS.md to establish coding standards and practices for the project. - Updated .gitignore to exclude demo files and added configuration for planning tools. This commit lays the groundwork for enhancing SSH functionality and user experience in managing multiple workspaces.
8.4 KiB
Architecture
Analysis Date: 2026-02-12
Pattern Overview
Overall: Three-process Electron architecture with domain-driven service layer and unidirectional data flow
Key Characteristics:
- Electron main process (Node.js) handles file system access, JSONL parsing, and business logic via domain-organized services
- Renderer process (React) manages UI state through Zustand slices with per-tab isolation
- Preload script provides secure IPC bridge via contextBridge
- Data flows: JSONL files → Services (parse/analyze) → IPC → Zustand store → React components
- Service layer organized into 5 domains: analysis, discovery, error, infrastructure, parsing
Layers
Main Process (Node.js):
- Purpose: File system access, session parsing, chunk building, business logic
- Location:
src/main/ - Contains: Entry point (
index.ts), services (domain-organized), IPC handlers, types, utilities - Depends on: Electron APIs, Node.js file system, shared types
- Used by: Renderer process via IPC
Preload Bridge:
- Purpose: Secure IPC communication layer between main and renderer
- Location:
src/preload/ - Contains: ElectronAPI implementation (
index.ts), IPC channel constants - Depends on: Electron contextBridge, IPC channel definitions
- Used by: Renderer process via
window.electronAPI
Renderer Process (React):
- Purpose: UI rendering, state management, user interaction
- Location:
src/renderer/ - Contains: React components, Zustand store, hooks, utilities, contexts
- Depends on: React, Zustand, ElectronAPI, shared types
- Used by: End user
Shared Code:
- Purpose: Cross-process types, pure utilities, constants
- Location:
src/shared/ - Contains: Type definitions, token formatting, model parsing, logger
- Depends on: Nothing (pure TypeScript)
- Used by: Main, renderer, preload processes
Service Domains (Main Process):
- Purpose: Business logic organized by responsibility
- Location:
src/main/services/{domain}/ - Contains: 5 domains with specialized services
- Depends on: Node.js APIs, shared types, utilities
- Used by: IPC handlers, main process lifecycle
Data Flow
Session Loading Flow:
- User selects project/session in sidebar (renderer)
- Renderer calls
window.electronAPI.getSessionDetail(projectId, sessionId) - IPC handler in
src/main/ipc/sessions.tsreceives request - Handler checks
DataCache(LRU cache, 50 entries, 10min TTL) - On cache miss:
SessionParserreads JSONL file from~/.claude/projects/{encoded-path}/{sessionId}.jsonl SessionParserparses messages, extracts metadata, calculates metricsSubagentResolverfinds subagent files in{sessionId}/subagents/, parses them, links to Task callsChunkBuilderorchestrates chunk building: classifies messages (user/AI/system/noise), groups into chunks, attaches subagents- Result cached in
DataCacheand returned via IPC - Renderer receives data, updates Zustand store (
sessionDetailSlice) - React components re-render with new data
Real-time Update Flow:
FileWatcherdetects change in session JSONL file (100ms debounce)- FileWatcher emits 'file-change' event with
{ type, path, projectId, sessionId, isSubagent } - Main process forwards event to renderer via
mainWindow.webContents.send('file-change', event) - Renderer's
initializeNotificationListeners()receives event - Store action
refreshSessionInPlace()called (debounced 150ms) - Cache invalidated, session re-parsed
- Store updated without changing
selectedSessionId(no flicker) - Components re-render with updated data
State Management:
- Zustand store with 14 slices (project, session, sessionDetail, subagent, conversation, tab, tabUI, pane, ui, notification, config, repository, connection, update)
- Each slice follows pattern:
{ data, selectedId, loading, error } - Per-tab UI state isolated in
tabUISliceusing tabId as key - IPC event listeners registered once in
App.tsx, update store directly
Key Abstractions
Chunk (Visualization Unit):
- Purpose: Independent timeline visualization unit for chat display
- Examples:
UserChunk,AIChunk,SystemChunk,CompactChunk - Pattern: Discriminated union on
typefield, each with timestamp, duration, metrics (tokens, cost, tools) - Built by:
ChunkBuilderorchestratingChunkFactory,MessageClassifier,ProcessLinker
Process (Subagent):
- Purpose: Represents spawned subagent execution with timing and metrics
- Examples: Task tool subagents, teammate messages in team coordination
- Pattern: Contains
id,name,startTime,endTime,metrics,isParallel, optionalteammetadata - Built by:
SubagentResolverparsing subagent JSONL files, linking to Task calls, detecting parallel execution
Service (Business Logic):
- Purpose: Domain-specific business logic with single responsibility
- Examples:
ChunkBuilder(analysis),ProjectScanner(discovery),SessionParser(parsing),NotificationManager(infrastructure),ErrorDetector(error) - Pattern: Class-based, injected dependencies via constructor, exported from domain barrel
- Location:
src/main/services/{domain}/
Zustand Slice (State Domain):
- Purpose: Domain-specific state management with actions
- Examples:
sessionSlice,tabSlice,notificationSlice - Pattern: Factory function returning slice with data, actions, selectors
- Combined in:
src/renderer/store/index.tsviacreate<AppState>()
IPC Handler (Communication):
- Purpose: Request/response handlers for renderer-to-main communication
- Examples:
get-projects,get-session-detail,notifications:get - Pattern: Domain-organized modules with initialize/register/remove functions
- Location:
src/main/ipc/{domain}.ts
Entry Points
Main Process Entry:
- Location:
src/main/index.ts(381 lines) - Triggers:
app.whenReady()Electron event - Responsibilities: Initialize services (ProjectScanner, SessionParser, SubagentResolver, ChunkBuilder, DataCache, FileWatcher, NotificationManager, UpdaterService, SshConnectionManager), register IPC handlers, create BrowserWindow, start file watcher, apply configuration
Renderer Entry:
- Location:
src/renderer/main.tsx(12 lines) - Triggers: Page load after Electron window created
- Responsibilities: Render React app (
<App />), mount to #root div
Preload Entry:
- Location:
src/preload/index.ts(369 lines) - Triggers: Before renderer loads (Electron lifecycle)
- Responsibilities: Expose ElectronAPI via contextBridge, wrap IPC calls with type-safe interface, provide event listener setup/cleanup
React App Component:
- Location:
src/renderer/App.tsx - Triggers: React render
- Responsibilities: Initialize theme, dismiss splash screen, register IPC event listeners (
initializeNotificationListeners()), render layout (<TabbedLayout />)
Error Handling
Strategy: Layer-specific error boundaries with graceful degradation
Patterns:
- Main process: Try/catch in services and IPC handlers, log errors with
createLogger(), return null or empty arrays on failure - Renderer: React
<ErrorBoundary>component catches render errors, shows fallback UI - IPC: Config handlers return
{ success: boolean, data?, error? }wrapper, other handlers return null on failure - Store actions: Catch async errors, set
errorstate, display in UI
Cross-Cutting Concerns
Logging: createLogger(namespace) from @shared/utils/logger, used throughout main and renderer
Validation:
- Path validation in
src/main/ipc/validation.tsviavalidatePath(),validateMentions() - Config validation in
src/main/ipc/configValidation.tsfor user input sanitization - Type guards in
src/main/ipc/guards.tsfor IPC argument validation
Authentication: Not applicable (local desktop app accessing user's file system)
Caching:
DataCacheservice (LRU, 50 entries, 10min TTL) for parsed session data- Cache invalidation on file changes detected by
FileWatcher - Automatic cleanup every 5 minutes
Performance:
- Virtual scrolling for session lists and message lists (
@tanstack/react-virtual) - Debounced file watching (100ms) to batch rapid changes
- Session refresh debouncing (150ms) to prevent redundant IPC calls
- LRU cache to avoid re-parsing large JSONL files
Configuration:
ConfigManagerservice manages~/.claude-devtools/config.json- Accessed via
configManager.getConfig(),configManager.updateConfig() - IPC handlers in
src/main/ipc/config.tsfor renderer access - Settings UI in
src/renderer/components/settings/
Architecture analysis: 2026-02-12