CLAUDE.md · Foundry376/Mailspring
CLAUDE.md from Foundry376/Mailspring: 234 lines covering Build and Development Commands, Architecture Overview, Core Data Flow: Sync Engine, Tasks, and…
Template by Foundry376
Install
Works with Claude Code. Read the repository before you give any tool access to your data.
curl -fsSL https://raw.githubusercontent.com/Foundry376/Mailspring/master/CLAUDE.md -o CLAUDE.md
Read it and adapt it to your project before use.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: code-generation
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
What's in it
A CLAUDE.md file: project memory Claude Code loads at the start of every session.
- For
- Claude Code
- Length
- 234 lines
- File
CLAUDE.md
Sections
- Build and Development Commands
- Architecture Overview
- Core Data Flow: Sync Engine, Tasks, and Observable Database
- Comment Style
- Development Notes
- Claude Hooks
First lines (shown under the repo's GPL-3.0 license)
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Build and Development Commands
```bash
# Install dependencies
npm install
# Run the app in development mode (uses --dev flag, data stored in Mailspring-dev folder)
npm start
# Run linting (prettier runs as an eslint rule); lint:check reports without fixing
npm run lint
npm run lint:check
# TypeScript type checking
npm run typecheck
# Run all tests, or only spec files whose path matches a regex
npm test
npm test -- --spec-file-pattern='calendar'
# Run window-specific tests
npm run test-window
# Run Playwright end-to-end tests
npm run test:e2e
# TypeScript type checking in watch mode
npm run tsc-watch
# Build for production
npm run build
```
## Architecture Overview
Mailspring is an Electron-based email client written in TypeScript with React. It uses a plugin architecture where features are implemented as internal packages.
### Key Directories
- **`app/src/`** - Core application source code
- `browser/` - Main process code (application lifecycle, window management, auto-updates)
- `flux/` - Flux-based state management (actions, stores, models, tasks)
- `components/` - Reusable React UI components
- `services/` - Application services (search, sanitization, etc.)
- `registries/` - Extension registries (components, extensions, database objects)
- `global/` - Global exports (`mailspring-exports`, `mailspring-component-kit`)
- **`app/internal_packages/`** - Built-in plugins implementing features (composer, message-list, thread-list, preferences, themes, etc.)
> **IMPORTANT:** Application source code lives in **both** `app/src/` and `app/internal_packages/`. When searching for usages of a module, symbol, or pattern, always search both directories. Searching only `app/src/` will miss a large portion of the codebase and lead to incomplete changes.
- **`app/spec/`** - Jasmine test specs
### Core Modules
**Global exports for plugins:**
- `mailspring-exports` - Core APIs: Actions, Stores, Models, Tasks, Utils, database access
- `mailspring-component-kit` - Reusable UI components
**Flux Architecture:**
- **Models** (`flux/models/`) - Data models: Message, Thread, Contact, Account, Folder, Label, etc.
- **Stores** (`flux/stores/`) - Application state: DatabaseStore, DraftStore, AccountStore, etc.
- **Tasks** (`flux/tasks/`) - Async operations: SendDraftTask, ChangeFolderTask, etc.
- **Actions** (`flux/actions.ts`) - Application-wide action dispatcher
### Plugin Structure
Each plugin in `internal_packages/` has:
- `package.json` - Metadata with `windowTypes` specifying where plugin loads
- `lib/main.ts` - Entry point with `activate()` and `deactivate()` lifecycle hooks
- `lib/` - Plugin source code
- `styles/` - LESS stylesheets
- `keymaps/` - Keyboard shortcut definitions
## Core Data Flow: Sync Engine, Tasks, and Observable Database
**Important:** The UI is read-only with respect to the database. All database modifications happen in the C++ sync engine (Mailspring-Sync). The Electron app requests changes via Tasks, and the sync engine streams entity changes back to create a real-time UI.
### Sync Engine Communication (`mailsync-process.ts`, `mailsync-bridge.ts`)
The sync engine is a separate C++ process spawned per account:
1. **Electron → Sync Engine**: JSON messages sent via stdin (task requests, commands)
2. **Sync Engine → Electron**: Newline-delimited JSON streamed via stdout (database change deltas)
```
┌─────────────────┐ stdin (JSON) ┌──────────────────┐
│ Electron UI │ ──────────────────────────────▶│ Mailspring-Sync │
│ (TypeScript) │ │ (C++) │
│ │ ◀────────────────────────────── │ │
└─────────────────┘ stdout (JSON deltas) └──────────────────┘
```
The `MailsyncBridge` (in main window only) manages sync process lifecycle, listens to `Actions.queueTask`, and forwards tasks to the appropriate account's sync process.
### Task System (`flux/tasks/`)
Tasks represent operations the user wants to perform (send email, star thread, move to folder). They are **persisted models** stored in the database.
**Task Lifecycle:**
1. UI calls `Actions.queueTask(new SomeTask({...}))`
2. `MailsyncBridge._onQueueTask()` validates and sends to sync engine via stdin
3. Sync engine executes the task (local changes + remote API calls)
4. Sync engine persists task status updates and emits deltas
5. Task completion triggers `onSuccess()` or `onError()` callbacks
**Task States** (`flux/tasks/task.ts`):
- `local` - Not yet executed
- `remote` - Local phase complete, waiting for remote
- `complete` - Finished successfully
- `cancelled` - Cancelled before completion
**Key Task Classes:**
- `SendDraftTask`, `DestroyDraftTask` - Email composition
- `ChangeLabelsTask`, `ChangeFolderTask` - Organization
- `ChangeStarredTask`, `ChangeUnreadTask` - Status flags
- `SyncbackMetadataTask` - Plugin metadata sync
- `SyncbackEventTask` - Calendar event sync
**Undoable Tasks:**
Tasks can support undo/redo by implementing `canBeUndone` and `createUndoTask()`. The `UndoRedoStore` automatically registers tasks with `canBeUndone = true` for undo.
Two patterns exist:
1. **Toggle pattern** (`ChangeStarredTask`): Undo simply flips a boolean flag
2. **Snapshot pattern** (`SyncbackMetadataTask`, `SyncbackEventTask`): Store original state in `undoData`, swap on undo
```typescript
// Snapshot pattern example
const undoData = { ics: event.ics, recurrenceStart: event.recurrenceStart };
event.ics = newIcs; // Modify after capturing
Actions.queueTask(SyncbackEventTask.forUpdating({ event, undoData, description: 'Edit event' }));
```
See `docs/undo-redo-task-pattern.md` for detailed implementation guide.
### Task Queue (`flux/stores/task-queue.ts`)
The TaskQueue store observes Task model changes from the database and provides:
- `queue()` - Active tasks
- `completed()` - Finished tasks
- `waitForPerformLocal(task)` - Promise that resolves when task runs locally
- `waitForPerformRemote(task)` - Promise that resolves when task fully completes
### Observable Database Pattern
**Database is read-only in Electron** (`flux/stores/database-store.ts`):
- `DatabaseStore.inTransaction()` throws - writes are not allowed
- Uses SQLite in WAL mode via better-sqlite3 for concurrent reads
- The sync engine exclusively handles writes
**Change Records** (`flux/stores/database-change-record.ts`):
When the sync engine modifies data, it emits JSON deltas that become `DatabaseChangeRecord` objects:
```typescript
{Signals
- Stars
- 17,938
- Forks
- 1,064
- Open issues
- 5
- Last commit (this folder)
- Oct 7, 2026 (22 days ago)
- Last release
- Oct 22, 2026 (7 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- GPL-3.0
- Repository
- github.com/Foundry376/Mailspring
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
Cite this page
CLAUDE.md · Foundry376/Mailspring. Agent Mart. https://agentmart.dev/templates/foundry376-mailspring-claude-md (last updated 2026-10-24)