170 TypeScript/TSX source files across five clean architecture layers. Dependency injection. No DI container. Streaming CLI integration.
Every file belongs to exactly one layer. Import rules are strictly enforced — inner layers never depend on outer layers.
| Layer | Technology | Version |
|---|---|---|
| Shell | Electron | 33.4.0 |
| Bundler | Vite (via Electron Forge) | ^5.4.21 |
| Framework | React | ^18.3.0 |
| Language | TypeScript | ~5.5.0 |
| Styling | Tailwind CSS | ^4.0.0 |
| State | Zustand | ^5.0.0 |
| Database | better-sqlite3 | ^11.0.0 |
| AI Backend | Claude Code CLI | (system-installed) |
| Build | Pandoc | (bundled binary) |
| IDs | nanoid | 3 |
| Markdown | marked | ^15.0.0 |
| Diff | diff | ^8.0.4 |
| Archive | archiver | ^7.0.1 |
| Build Tool | Electron Forge | ^7.11.1 |
All services are composed in src/main/index.ts (the composition root) and injected via constructors. No DI container — plain constructor injection.
SettingsService(userDataPath) DatabaseService(dbPath) AgentService(agentsDir) FileSystemService(booksDir, userDataPath) ClaudeCodeClient(booksDir, db) ProviderRegistry(settingsService) UsageService → IDatabaseService ChapterValidator(booksDir) ChatService ├── ISettingsService ├── IAgentService ├── IDatabaseService ├── IProviderRegistry ├── IFileSystemService ├── UsageService └── ChapterValidator PipelineService → IFileSystemService BuildService → IFileSystemService, pandocPath, booksDir RevisionQueueService ├── IFileSystemService ├── IProviderRegistry ├── IAgentService ├── IDatabaseService └── ISettingsService MotifLedgerService → IFileSystemService, IProviderRegistry VersionService → IDatabaseService, IFileSystemService ManuscriptImportService → IFileSystemService, pandocPath SourceGenerationService ├── ISettingsService ├── IAgentService ├── IDatabaseService ├── IFileSystemService └── IProviderRegistry SeriesImportService → IManuscriptImportService, ISeriesService HelperService ├── ISettingsService ├── IAgentService ├── IDatabaseService ├── IFileSystemService ├── IProviderRegistry └── StreamManager NotificationManager → ISettingsService BookWatcher(booksDir, callback) BooksDirWatcher(booksDir, callback)
No DI container. Services depend on interfaces, not concrete classes. The composition root in src/main/index.ts is the only place concrete classes are instantiated.
Claude Code CLI handles its own authentication through the user's Anthropic subscription. The app stores no secrets. Additional OpenAI-compatible providers manage their own keys.
Central ProviderRegistry routes model requests to the correct provider. O(1) reverse model index. Protects built-in providers from deletion. Config persisted to settings.json.
Claude CLI spawned as child process with --output-format stream-json. NDJSON events parsed line-by-line and mapped to a 12-variant StreamEvent discriminated union.
Per-agent read guidance determines which files to include. Dynamic conversation compaction based on available context window: generous (all turns), moderate (8), tight (4), critical (2).
Every file write (agent or user) creates a snapshot in SQLite. SHA-256 dedup prevents duplicate storage. Structured diffs via the diff library. One-click revert with audit trail.
7 tables in SQLite with WAL mode and foreign keys enabled. Forward-only migration system via src/infrastructure/database/migrations.ts.
| Table | Purpose | Key Columns |
|---|---|---|
conversations | Chat conversations per book/agent | id, book_slug, agent_name, pipeline_phase, purpose, title |
messages | Message history with thinking | id, conversation_id (FK), role, content, thinking |
token_usage | Token consumption per call | conversation_id (FK), input/output/thinking tokens, model |
stream_events | Persisted stream events for replay | session_id, sequence_number, event_type, payload |
stream_sessions | CLI call lifecycle tracking | id, conversation_id, agent_name, model, ended_at, interrupted |
file_versions | File snapshots with dedup | book_slug, file_path, content, content_hash, source |
schema_version | Migration tracking | version, applied_at, description |
src/
├── domain/ # Pure types, zero imports
│ ├── types.ts # All shared type definitions
│ ├── interfaces.ts # Service contracts (ports)
│ ├── constants.ts # Agent registry, pipeline phases, defaults
│ ├── statusMessages.ts # Rotating status messages
│ └── index.ts # Barrel export
│
├── infrastructure/ # Implements domain interfaces
│ ├── settings/ # SettingsService — JSON preferences
│ ├── database/ # DatabaseService + schema + migrations
│ ├── agents/ # AgentService — .md prompt loader
│ ├── filesystem/ # FileSystemService + BookWatcher + BooksDirWatcher
│ ├── claude-cli/ # ClaudeCodeClient + StreamSessionTracker
│ ├── providers/ # ProviderRegistry + OpenAiCompatibleProvider
│ ├── series/ # SeriesService — file-based series CRUD + bible
│ └── pandoc/ # Pandoc binary path resolution
│
├── application/ # Business logic via injected interfaces
│ ├── ChatService.ts # Send → stream → save orchestration
│ ├── ContextBuilder.ts # Budget-aware context assembly
│ ├── PipelineService.ts # Phase detection + user confirmation gates
│ ├── BuildService.ts # Pandoc execution for DOCX/EPUB
│ ├── UsageService.ts # Token tracking
│ ├── RevisionQueueService.ts # Revision plan parsing + session execution
│ ├── ChapterValidator.ts # Auto-corrects misplaced chapter files
│ ├── AuditService.ts # Verity audit/fix pipeline
│ ├── PitchRoomService.ts # Pitch Room message handling
│ ├── HotTakeService.ts # Ghostlight hot-take orchestration
│ ├── AdhocRevisionService.ts # Direct feedback → Forge revision plan
│ ├── StreamManager.ts # Stream lifecycle + session tracking
│ ├── MotifLedgerService.ts # Motif ledger CRUD + CLI normalization
│ ├── VersionService.ts # File versioning (snapshot/diff/revert)
│ ├── ManuscriptImportService.ts # DOCX/MD import + chapter detection
│ ├── SourceGenerationService.ts # Multi-agent source doc generation
│ ├── SeriesImportService.ts # Batch series import orchestration
│ ├── HelperService.ts # In-app help assistant
│ ├── DashboardService.ts # Book overview dashboard aggregation
│ ├── StatisticsService.ts # Writing statistics + cost estimates
│ ├── FindReplaceService.ts # Bulk find & replace across chapters
│ ├── thinkingBudget.ts # Thinking budget resolution
│ └── context/TokenEstimator.ts # ~4 chars/token estimation
│
├── main/ # Electron main process
│ ├── index.ts # Composition root
│ ├── bootstrap.ts # First-run directory/file creation
│ ├── notifications.ts # OS notification manager
│ └── ipc/handlers.ts # Thin IPC adapter (80+ channels)
│
├── preload/index.ts # contextBridge: typed API for renderer
│
└── renderer/ # React UI
├── App.tsx # Root component, onboarding gate
├── main.tsx # React 18 createRoot entry
├── stores/ (23 stores) # Zustand state management
├── components/ (20 groups) # Layout, Chat, Files, Build, PitchRoom,
│ # RevisionQueue, MotifLedger, Import,
│ # Helper, Series, Dashboard, Statistics,
│ # Reading, RightPanel, common, etc.
├── hooks/ (6 hooks) # Resize, theme, events, status, tooltip
├── tours/ # Guided tour step definitions
└── styles/globals.css # Tailwind CSS v4 base
Node.js 20+, npm 9+, Claude Code CLI installed and authenticated.
git clone https://github.com/john-paul-ruf/novel-engine.git cd novel-engine npm install npm run download-pandoc npm start
If you contribute, respect these non-negotiable rules:
window.novelEngine (the preload bridge).index.ts) in every infrastructure subdirectory.any types. No API keys stored. contextIsolation: true always.Detailed architecture docs are maintained in the repo at docs/architecture/ — ARCHITECTURE.md, DOMAIN.md, INFRASTRUCTURE.md, APPLICATION.md, IPC.md, RENDERER.md.