- 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.
180 lines
8.4 KiB
Markdown
180 lines
8.4 KiB
Markdown
# 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:**
|
|
|
|
1. User selects project/session in sidebar (renderer)
|
|
2. Renderer calls `window.electronAPI.getSessionDetail(projectId, sessionId)`
|
|
3. IPC handler in `src/main/ipc/sessions.ts` receives request
|
|
4. Handler checks `DataCache` (LRU cache, 50 entries, 10min TTL)
|
|
5. On cache miss: `SessionParser` reads JSONL file from `~/.claude/projects/{encoded-path}/{sessionId}.jsonl`
|
|
6. `SessionParser` parses messages, extracts metadata, calculates metrics
|
|
7. `SubagentResolver` finds subagent files in `{sessionId}/subagents/`, parses them, links to Task calls
|
|
8. `ChunkBuilder` orchestrates chunk building: classifies messages (user/AI/system/noise), groups into chunks, attaches subagents
|
|
9. Result cached in `DataCache` and returned via IPC
|
|
10. Renderer receives data, updates Zustand store (`sessionDetailSlice`)
|
|
11. React components re-render with new data
|
|
|
|
**Real-time Update Flow:**
|
|
|
|
1. `FileWatcher` detects change in session JSONL file (100ms debounce)
|
|
2. FileWatcher emits 'file-change' event with `{ type, path, projectId, sessionId, isSubagent }`
|
|
3. Main process forwards event to renderer via `mainWindow.webContents.send('file-change', event)`
|
|
4. Renderer's `initializeNotificationListeners()` receives event
|
|
5. Store action `refreshSessionInPlace()` called (debounced 150ms)
|
|
6. Cache invalidated, session re-parsed
|
|
7. Store updated without changing `selectedSessionId` (no flicker)
|
|
8. 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 `tabUISlice` using 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 `type` field, each with timestamp, duration, metrics (tokens, cost, tools)
|
|
- Built by: `ChunkBuilder` orchestrating `ChunkFactory`, `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`, optional `team` metadata
|
|
- Built by: `SubagentResolver` parsing 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.ts` via `create<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 `error` state, 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.ts` via `validatePath()`, `validateMentions()`
|
|
- Config validation in `src/main/ipc/configValidation.ts` for user input sanitization
|
|
- Type guards in `src/main/ipc/guards.ts` for IPC argument validation
|
|
|
|
**Authentication:** Not applicable (local desktop app accessing user's file system)
|
|
|
|
**Caching:**
|
|
- `DataCache` service (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:**
|
|
- `ConfigManager` service manages `~/.claude-devtools/config.json`
|
|
- Accessed via `configManager.getConfig()`, `configManager.updateConfig()`
|
|
- IPC handlers in `src/main/ipc/config.ts` for renderer access
|
|
- Settings UI in `src/renderer/components/settings/`
|
|
|
|
---
|
|
|
|
*Architecture analysis: 2026-02-12*
|