RuleStack

Configs

Stacks

Compare

Diff

RuleStack

Configs

Stacks

Compare

Diff

Read API

RuleStack

Configs

Stacks

Compare

Diff

Read API

Diff/nimbalyst-nimbalyst-packages-ios-claude ↔ nimbalyst-nimbalyst-packages-electron-claude

Comparison

A · CLAUDE.md · nimbalyst/nimbalystB · CLAUDE.md · nimbalyst/nimbalyst
What each file covers, counted
DimensionSharedOnly in AOnly in BOverlap
Sections113183%
Commands0290%
Section tags25418%

What each file covers

Sections

1 shared · 13 only in A · 18 only in B
  • − iOS Package (Native iOS App)
  • − Package Structure
  • − Key Architecture Decisions
  • − Authentication Flow
  • − Data Flow
  • − iPad Support
  • − Development
  • − Prerequisites
  • − Commands
  • − From monorepo root:
  • − From packages/ios/:
  • − Transcript Bundle
  • − Key Files
  • + Electron Package
  • + Development Commands
  • + Architecture
  • + Main and Renderer Processes
  • + IPC Communication
  • + Preload API
  • + Document Service
  • + Common IPC Issues
  • + Data Persistence
  • + Renderer State Architecture
  • + Logging
  • + Window State Persistence
  • + Theme Support
  • + File Operations
  • + AI Providers
  • + macOS Code Signing & Notarization
  • + Git Worktree Integration
  • + Analytics
  •   Testing

Commands

0 shared · 2 only in A · 9 only in B
  • − npm run ios:test:swift
  • − npm run ios:build:transcript
  • + npm run dev
  • + npm run dev:loop
  • + npm run build:mac:local
  • + npm run build:mac:notarized
  • + npm run dev:user2
  • + npm run dev:url-handler
  • + npx playwright test e2e/monaco/file-watcher-updates.spec.ts
  • + npx playwright test e2e/monaco/
  • + npx playwright test

Section tags

2 shared · 5 only in A · 4 only in B
  • − setup
  • − architecture
  • − security
  • − dependencies
  • − monorepo
  • + git-pr
  • + api
  • + do-not
  • + agent-behaviour
  •   build
  •   test

Line diff

+104 added−97 removed27 unchanged20.6% identical
nimbalyst/nimbalyst · packages/ios/CLAUDE.md
@@ −1 @@
1# iOS Package (Native iOS App)
2 
3This package contains the native SwiftUI iOS/iPadOS app for Nimbalyst. It provides a mobile interface for viewing and interacting with AI sessions synced from the desktop Electron app via end-to-end encrypted WebSocket sync.
4 
5The app is **pure Swift/SwiftUI** with no Capacitor or web framework dependency. The only web view is `TranscriptWebView` (WKWebView) which renders the rich chat transcript using the same React components as the desktop app.
6 
7## Package Structure
 
 
 
 
8 
9```
10packages/ios/
11 NimbalystNative/ # Swift Package - all business logic and UI
12 Sources/
13 App/ # AppState (root observable), ContentView, navigation
14 Auth/ # AuthManager (Stytch OAuth via ASWebAuthenticationSession)
15 Crypto/ # CryptoManager (AES-256-GCM, PBKDF2), KeychainManager
16 Database/ # DatabaseManager (GRDB migrations, queries)
17 Models/ # GRDB record types: Project, Session, Message, QueuedPrompt, SyncState
18 Notifications/ # NotificationManager (push notification registration)
19 Sync/ # SyncManager, WebSocketClient, SyncProtocol types
20 Utils/ # RelativeTimestamp, NimbalystColors
21 Views/ # All SwiftUI views
22 Tests/ # Unit and integration tests (68 tests)
23 Package.swift # Swift Package Manager manifest (GRDB dependency)
24 
25 NimbalystApp/ # Xcode app target
26 Sources/ # App entry point (@main), DebugMenu
27 Resources/ # Assets.xcassets (AppIcon, Splash), transcript-dist bundle
28 project.yml # XcodeGen project definition
29 
30 CryptoCompatibility/ # CommonCrypto bridging header for PBKDF2 key derivation
31 
32 src/transcript/ # React transcript web bundle (loaded in WKWebView)
33 main.tsx # Entry point with Swift <-> JS bridge
34 styles.css # Styles with bundled Material Symbols font
35 fonts/ # Locally bundled Material Symbols TTF
36 
37 vite.config.transcript.ts # Vite config for transcript bundle (IIFE format for file://)
38 transcript.html # HTML entry point for Vite build
39 dist-transcript/ # Build output (not committed)
40```
41 
42## Key Architecture Decisions
 
 
43 
44### Authentication Flow
451. QR pairing stores encryption seed + server URL in Keychain
462. Stytch OAuth stores JWT + user ID in Keychain
473. When both paired AND authenticated, managers initialize
484. Encryption key derived from seed + user ID via PBKDF2
49 
50### Data Flow
51- **Sync**: WebSocket connection to CollabV3 Durable Object (same server as desktop)
52- **Encryption**: All session data encrypted with AES-256-GCM before transmission
53- **Storage**: GRDB (SQLite) with reactive `ValueObservation` for live UI updates
54- **Transcript**: WKWebView loads bundled React app, communicates via `webkit.messageHandlers.bridge`
55 
56### iPad Support
57- `NavigationSplitView` for regular size class (sidebar + detail)
58- `NavigationStack` for compact size class (iPhone)
 
 
59 
60## Development
 
 
 
 
61 
62### Prerequisites
63- Xcode 16+
64- Node.js 20+ (for transcript bundle)
65- XcodeGen (`brew install xcodegen`)
66 
67### Commands
68```bash
69# From monorepo root:
70npm run ios:test:swift # Run all 68 Swift tests
71npm run ios:build:transcript # Build transcript web bundle
72 
73# From packages/ios/:
74cd NimbalystNative && swift test # Run tests directly
75cd NimbalystApp && xcodegen generate # Regenerate .xcodeproj
76open NimbalystApp/NimbalystApp.xcodeproj # Open in Xcode
77```
78 
79### Transcript Bundle
80The Xcode pre-build script in `project.yml` automatically builds the transcript with Vite and copies it to `Resources/transcript-dist/`. You can also build manually:
 
 
81 
82```bash
83npm run ios:build:transcript
84```
85 
86Output: `dist-transcript/transcript.html` + `dist-transcript/assets/` (JS bundle + Material Symbols font).
87 
88After building, copy the output to Xcode resources:
89```bash
90rm -f NimbalystApp/Resources/transcript-dist/assets/transcript-*.js
91cp dist-transcript/transcript.html NimbalystApp/Resources/transcript-dist/transcript.html
92cp dist-transcript/assets/* NimbalystApp/Resources/transcript-dist/assets/
93```
94 
95**CRITICAL: React hooks rules in `src/transcript/main.tsx`**
 
 
 
 
 
 
96 
97The transcript React app runs inside WKWebView where errors are invisible (cross-origin `window.onerror` reports "Script error." with no details). This makes hooks violations especially dangerous -- the screen goes blank with no diagnostic information.
98 
99Rules for editing `TranscriptApp` in `main.tsx`:
100- **All hooks (`useState`, `useRef`, `useCallback`, `useMemo`, `useEffect`) must come BEFORE any early returns.** React requires the same hooks to run in the same order on every render. An early `return` before a hook means that hook runs on some renders but not others, crashing React with "Rendered more hooks than during the previous render."
101- **The `TranscriptErrorBoundary` wraps the app** to catch render errors and display them on screen + report to the native bridge. Do not remove it.
102- **The `postErrorToNative` helper** sends error details through `webkit.messageHandlers.bridge` so they appear in Xcode console logs with full stack traces. Use it in any new try-catch blocks.
103- **Test after any change**: Always rebuild the transcript (`npm run ios:build:transcript`), copy to Xcode resources, and rebuild in Xcode. Vite build success does NOT mean React will render correctly at runtime.
104 
105## Key Files
106 
107| File | Purpose |
108|------|---------|
109| `Sources/App/AppState.swift` | Root observable object; owns database, crypto, and sync managers |
110| `Sources/Sync/SyncManager.swift` | WebSocket sync with CollabV3; processes index responses and broadcasts |
111| `Sources/Sync/SyncProtocol.swift` | All wire protocol types (Codable structs with CodingKeys) |
112| `Sources/Database/DatabaseManager.swift` | GRDB schema migrations, queries, and project stats refresh |
113| `Sources/Crypto/CryptoManager.swift` | AES-256-GCM encrypt/decrypt, deterministic project ID encryption |
114| `Sources/Views/TranscriptWebView.swift` | WKWebView + Coordinator with JS bridge, TranscriptController |
115| `Sources/Views/SessionDetailView.swift` | Session detail with transcript, scroll-to-top, jump-to-prompt |
116| `Sources/Views/SessionListView.swift` | Time-grouped session list with search and swipe-to-delete |
117| `Sources/Views/ProjectListView.swift` | Project list sorted by last activity with desktop connection indicator |
118| `src/transcript/main.tsx` | React transcript app with `scrollToTop`, `scrollToMessage`, `getPromptList` JS bridge |
119 
120## Testing
121- 68 Swift tests covering database, crypto, sync integration, and web view
122- See [TESTING.md](./TESTING.md) for CI/CD pipeline details
123- Tests run on both macOS (via Swift Package Manager) and iOS simulator (via Xcode)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124 
nimbalyst/nimbalyst · packages/electron/CLAUDE.md
@@ +1 @@
1# Electron Package
2 
3The Nimbalyst desktop app, built with Electron.
4 
5## Development Commands
6 
7- **Dev server**: `npm run dev` (user runs this — don't do it yourself)
8- **Dev with restart loop**: `npm run dev:loop` (enables restart button / `/restart` command)
9- **Build for Mac**: `npm run build:mac:local` or `npm run build:mac:notarized`
10- **Auth callbacks in dev**: no URL-handler setup is required, including for `npm run dev:user2`. Every sign-in flow uses a nonce-protected one-shot listener on `127.0.0.1` owned by the instance that started it.
11- **Other deep links in dev (macOS)**: `npm run dev:url-handler` (from the repo root) points non-auth `nimbalyst://` links at this checkout. The applet in `scripts/install-dev-url-handler.sh` is not part of authentication. The dev app deliberately does *not* claim the scheme itself because all development copies share Electron's `com.github.Electron` bundle id. See `src/main/utils/protocolRegistration.ts`.
12 
13### Testing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14 
15From the repository root:
16- Run one spec: `npx playwright test e2e/monaco/file-watcher-updates.spec.ts`
17- Run a directory: `npx playwright test e2e/monaco/`
18- Run all: `npx playwright test`
19 
20**Always use `npx playwright test` directly.** Never use parallel execution — it corrupts PGLite. See [/docs/E2E_TESTING.md](/docs/E2E_TESTING.md).
21 
22## Architecture
 
 
 
23 
24### Main and Renderer Processes
 
 
 
25 
26Electron apps split into two contexts:
27- **Main** runs Node.js, manages lifecycle, windows, menus, system interactions.
28- **Renderer** runs in a Chromium context; UI only.
29 
30Renderers cannot access Node.js APIs directly — use IPC to request main-process services. For initialization rules (dynamic import in `bootstrap.ts`, lazy init for `app.getPath()` consumers, `safeHandle` / `safeOn`), and cross-platform code patterns, see [MAIN_PROCESS_INIT.md](./MAIN_PROCESS_INIT.md).
 
 
 
 
31 
32## IPC Communication
 
 
 
 
33 
34### Preload API
35- **Location**: `src/preload/index.ts`
36- **Exposed as**: `window.electronAPI` (NOT `window.api`)
37- **Generic methods**: `invoke`, `send`, `on` (returns an unsubscribe closure — there is no `off`, see [/docs/IPC_LISTENERS.md](/docs/IPC_LISTENERS.md))
38- Renderer services use these to talk to main-process services.
39 
40### Document Service
41- Main: `ElectronDocumentService` (file scanning, metadata extraction, caching)
42- Renderer: `RendererDocumentService` (facade over IPC)
43- **Metadata**: frontmatter extraction with bounded reads (4KB)
44- **Channels**: `document-service:*`
45 
46### Common IPC Issues
47- `window.api undefined` → use `window.electronAPI`
48- Empty responses → check the window has a valid workspace path
49- Service resolution is keyed off workspace path
50 
51For deep IPC patterns (`safeHandle`/`safeOn`, error handling, channel structure), see [/docs/IPC_GUIDE.md](/docs/IPC_GUIDE.md).
 
 
 
 
52 
53## Data Persistence
 
 
 
 
54 
55The app runs over **either PGLite (PostgreSQL in WebAssembly) or better-sqlite3** — both backends are active during the in-progress migration. Code must work on either; do not assume one. **Never use `localStorage` in the renderer.** Persist via IPC to main using:
56- **app-settings store** (`src/main/utils/store.ts`) for global app settings
57- **workspace-settings store** for per-project state
58- **AppDatabase** (PGLite or SQLite, selected at init) for complex data (AI sessions, document history, trackers)
59 
60The biggest divergence to remember: `data->'key'` returns a parsed object on PGLite but a JSON string on SQLite. For tables, locations, shutdown rules, timestamp handling, and the full list of backend-divergent behaviors, see [DATABASE.md](./DATABASE.md).
 
 
61 
62## Renderer State Architecture
63 
64The renderer uses Jotai for state that crosses component boundaries. Editors use **EditorHost** — a stable service object — for all host communication; content state lives in the editor, not parent components.
 
 
 
 
 
65 
66| Domain | Atoms | Owner |
67| --- | --- | --- |
68| Theme | `themeAtom` | Global, IPC-synced |
69| Editors | `editorDirtyAtom(key)`, `editorProcessingAtom(key)` | EditorHost writes, Tab reads |
70| Sessions | `sessionUnreadAtom(id)`, `sessionProcessingAtom(id)` | AgenticPanel writes, UI reads |
71| File Tree | `gitStatusAtom`, `expandedDirsAtom` | WorkspaceSidebar writes, FileTree reads |
72| Trackers | `trackerCountsAtom` | TrackerService writes, UI reads |
73 
74**Re-render isolation**: parents subscribe to lists of IDs; children subscribe to their own atoms. If you need `React.memo` to prevent re-renders, you have the wrong architecture.
75 
76For full patterns, see [/docs/EDITOR_STATE.md](/docs/EDITOR_STATE.md) and [/docs/JOTAI.md](/docs/JOTAI.md).
 
 
 
 
77 
78## Logging
79 
80Three log destinations:
 
 
 
 
 
 
 
 
 
 
 
81 
82- **Main process log**: `~/Library/Application Support/@nimbalyst/electron/logs/main.log` — main-process events, AI, sync, file ops; categories like `(MAIN)`, `(AI)`, `(API)`, `(SYNC)`.
83- **Renderer console log** (dev mode only): `~/Library/Application Support/@nimbalyst/electron/nimbalyst-debug.log` — captured via `webContents.on('console-message')` in `src/main/index.ts`.
84 
85Use the agent log access tools (`get_main_process_logs`, `get_renderer_debug_logs`) instead of asking users to paste logs. See [/docs/DEBUGGING_LOGS.md](/docs/DEBUGGING_LOGS.md).
86 
87## Window State Persistence
88 
89- **Global session state** restores all windows on restart (bounds, focus order, dev tools state).
90- **Per-project state** restores window configuration, open file, AI panel width and collapsed state, draft inputs.
91- **Session continuity** — chat sessions persist across restarts.
92 
93## Theme Support
94 
95Themes: Light, Dark (#2d2d2d / #1a1a1a / #3a3a3a), Crystal Dark (Tailwind gray scale), Auto.
96 
97**Critical rules:**
98- Never hardcode colors in CSS files — use CSS variables.
99- `src/renderer/index.css` is the only place theme colors are defined.
100- Apply themes by setting both the `data-theme` attribute and the CSS class on the root element.
101 
102Comprehensive: [THEMING.md](./THEMING.md).
103 
104## File Operations
105 
106- **Drag-and-drop**: move files/folders in the Project Sidebar; hold Option/Alt to copy.
107- **Context menus**: rename, delete, open in new window.
108- **File watching**: auto-update on disk changes.
109 
110## AI Providers
111 
112Provider implementations live in `packages/runtime` — see `/packages/runtime/CLAUDE.md`. Electron-only pieces:
113 
114- **Renderer panels**: `src/renderer/components/AIModels/panels/ClaudePanel.tsx`, `ClaudeCodePanel.tsx`
115- **Claude Code installer**: `src/renderer/components/AIModels/services/CLIInstaller.ts` (manages local installation of `@anthropic-ai/claude-agent-sdk`)
116 
117## macOS Code Signing & Notarization
118 
119- **Certificate**: Developer ID Application
120- **Builds**: `npm run build:mac:notarized` (notarized), `build:mac:local` (local testing)
121- **Bundled tools**: ripgrep is signed; JAR files are excluded automatically (can't be notarized)
122- **Entitlements**: hardened runtime with necessary exceptions
123 
124## Git Worktree Integration
125 
126Nimbalyst creates git worktrees for isolated AI coding sessions. See [/docs/WORKTREES.md](/docs/WORKTREES.md). The `worktrees` table stores metadata; `ai_sessions.worktree_id` links sessions to worktrees. IPC channels: `worktree:create`, `worktree:get-status`, `worktree:delete`, `worktree:list`, `worktree:get`.
127 
128## Analytics
129 
130See [/docs/ANALYTICS_GUIDE.md](/docs/ANALYTICS_GUIDE.md). **When adding, modifying, or removing PostHog events, update [/docs/POSTHOG_EVENTS.md](/docs/POSTHOG_EVENTS.md).**
131 
@@ −1 +1 @@
1−# iOS Package (Native iOS App)
1+# Electron Package
22  
3−This package contains the native SwiftUI iOS/iPadOS app for Nimbalyst. It provides a mobile interface for viewing and interacting with AI sessions synced from the desktop Electron app via end-to-end encrypted WebSocket sync.
3+The Nimbalyst desktop app, built with Electron.
44  
5−The app is **pure Swift/SwiftUI** with no Capacitor or web framework dependency. The only web view is `TranscriptWebView` (WKWebView) which renders the rich chat transcript using the same React components as the desktop app.
5+## Development Commands
66  
7−## Package Structure
7+- **Dev server**: `npm run dev` (user runs this — don't do it yourself)
8+- **Dev with restart loop**: `npm run dev:loop` (enables restart button / `/restart` command)
9+- **Build for Mac**: `npm run build:mac:local` or `npm run build:mac:notarized`
10+- **Auth callbacks in dev**: no URL-handler setup is required, including for `npm run dev:user2`. Every sign-in flow uses a nonce-protected one-shot listener on `127.0.0.1` owned by the instance that started it.
11+- **Other deep links in dev (macOS)**: `npm run dev:url-handler` (from the repo root) points non-auth `nimbalyst://` links at this checkout. The applet in `scripts/install-dev-url-handler.sh` is not part of authentication. The dev app deliberately does *not* claim the scheme itself because all development copies share Electron's `com.github.Electron` bundle id. See `src/main/utils/protocolRegistration.ts`.
812  
9−```
10−packages/ios/
11− NimbalystNative/ # Swift Package - all business logic and UI
12− Sources/
13− App/ # AppState (root observable), ContentView, navigation
14− Auth/ # AuthManager (Stytch OAuth via ASWebAuthenticationSession)
15− Crypto/ # CryptoManager (AES-256-GCM, PBKDF2), KeychainManager
16− Database/ # DatabaseManager (GRDB migrations, queries)
17− Models/ # GRDB record types: Project, Session, Message, QueuedPrompt, SyncState
18− Notifications/ # NotificationManager (push notification registration)
19− Sync/ # SyncManager, WebSocketClient, SyncProtocol types
20− Utils/ # RelativeTimestamp, NimbalystColors
21− Views/ # All SwiftUI views
22− Tests/ # Unit and integration tests (68 tests)
23− Package.swift # Swift Package Manager manifest (GRDB dependency)
13+### Testing
2414  
25− NimbalystApp/ # Xcode app target
26− Sources/ # App entry point (@main), DebugMenu
27− Resources/ # Assets.xcassets (AppIcon, Splash), transcript-dist bundle
28− project.yml # XcodeGen project definition
15+From the repository root:
16+- Run one spec: `npx playwright test e2e/monaco/file-watcher-updates.spec.ts`
17+- Run a directory: `npx playwright test e2e/monaco/`
18+- Run all: `npx playwright test`
2919  
30− CryptoCompatibility/ # CommonCrypto bridging header for PBKDF2 key derivation
20+**Always use `npx playwright test` directly.** Never use parallel execution — it corrupts PGLite. See [/docs/E2E_TESTING.md](/docs/E2E_TESTING.md).
3121  
32− src/transcript/ # React transcript web bundle (loaded in WKWebView)
33− main.tsx # Entry point with Swift <-> JS bridge
34− styles.css # Styles with bundled Material Symbols font
35− fonts/ # Locally bundled Material Symbols TTF
22+## Architecture
3623  
37− vite.config.transcript.ts # Vite config for transcript bundle (IIFE format for file://)
38− transcript.html # HTML entry point for Vite build
39− dist-transcript/ # Build output (not committed)
40−```
24+### Main and Renderer Processes
4125  
42−## Key Architecture Decisions
26+Electron apps split into two contexts:
27+- **Main** runs Node.js, manages lifecycle, windows, menus, system interactions.
28+- **Renderer** runs in a Chromium context; UI only.
4329  
44−### Authentication Flow
45−1. QR pairing stores encryption seed + server URL in Keychain
46−2. Stytch OAuth stores JWT + user ID in Keychain
47−3. When both paired AND authenticated, managers initialize
48−4. Encryption key derived from seed + user ID via PBKDF2
30+Renderers cannot access Node.js APIs directly — use IPC to request main-process services. For initialization rules (dynamic import in `bootstrap.ts`, lazy init for `app.getPath()` consumers, `safeHandle` / `safeOn`), and cross-platform code patterns, see [MAIN_PROCESS_INIT.md](./MAIN_PROCESS_INIT.md).
4931  
50−### Data Flow
51−- **Sync**: WebSocket connection to CollabV3 Durable Object (same server as desktop)
52−- **Encryption**: All session data encrypted with AES-256-GCM before transmission
53−- **Storage**: GRDB (SQLite) with reactive `ValueObservation` for live UI updates
54−- **Transcript**: WKWebView loads bundled React app, communicates via `webkit.messageHandlers.bridge`
32+## IPC Communication
5533  
56−### iPad Support
57−- `NavigationSplitView` for regular size class (sidebar + detail)
58−- `NavigationStack` for compact size class (iPhone)
34+### Preload API
35+- **Location**: `src/preload/index.ts`
36+- **Exposed as**: `window.electronAPI` (NOT `window.api`)
37+- **Generic methods**: `invoke`, `send`, `on` (returns an unsubscribe closure — there is no `off`, see [/docs/IPC_LISTENERS.md](/docs/IPC_LISTENERS.md))
38+- Renderer services use these to talk to main-process services.
5939  
60−## Development
40+### Document Service
41+- Main: `ElectronDocumentService` (file scanning, metadata extraction, caching)
42+- Renderer: `RendererDocumentService` (facade over IPC)
43+- **Metadata**: frontmatter extraction with bounded reads (4KB)
44+- **Channels**: `document-service:*`
6145  
62−### Prerequisites
63−- Xcode 16+
64−- Node.js 20+ (for transcript bundle)
65−- XcodeGen (`brew install xcodegen`)
46+### Common IPC Issues
47+- `window.api undefined` → use `window.electronAPI`
48+- Empty responses → check the window has a valid workspace path
49+- Service resolution is keyed off workspace path
6650  
67−### Commands
68−```bash
69−# From monorepo root:
70−npm run ios:test:swift # Run all 68 Swift tests
71−npm run ios:build:transcript # Build transcript web bundle
51+For deep IPC patterns (`safeHandle`/`safeOn`, error handling, channel structure), see [/docs/IPC_GUIDE.md](/docs/IPC_GUIDE.md).
7252  
73−# From packages/ios/:
74−cd NimbalystNative && swift test # Run tests directly
75−cd NimbalystApp && xcodegen generate # Regenerate .xcodeproj
76−open NimbalystApp/NimbalystApp.xcodeproj # Open in Xcode
77−```
53+## Data Persistence
7854  
79−### Transcript Bundle
80−The Xcode pre-build script in `project.yml` automatically builds the transcript with Vite and copies it to `Resources/transcript-dist/`. You can also build manually:
55+The app runs over **either PGLite (PostgreSQL in WebAssembly) or better-sqlite3** — both backends are active during the in-progress migration. Code must work on either; do not assume one. **Never use `localStorage` in the renderer.** Persist via IPC to main using:
56+- **app-settings store** (`src/main/utils/store.ts`) for global app settings
57+- **workspace-settings store** for per-project state
58+- **AppDatabase** (PGLite or SQLite, selected at init) for complex data (AI sessions, document history, trackers)
8159  
82−```bash
83−npm run ios:build:transcript
84−```
60+The biggest divergence to remember: `data->'key'` returns a parsed object on PGLite but a JSON string on SQLite. For tables, locations, shutdown rules, timestamp handling, and the full list of backend-divergent behaviors, see [DATABASE.md](./DATABASE.md).
8561  
86−Output: `dist-transcript/transcript.html` + `dist-transcript/assets/` (JS bundle + Material Symbols font).
62+## Renderer State Architecture
8763  
88−After building, copy the output to Xcode resources:
89−```bash
90−rm -f NimbalystApp/Resources/transcript-dist/assets/transcript-*.js
91−cp dist-transcript/transcript.html NimbalystApp/Resources/transcript-dist/transcript.html
92−cp dist-transcript/assets/* NimbalystApp/Resources/transcript-dist/assets/
93−```
64+The renderer uses Jotai for state that crosses component boundaries. Editors use **EditorHost** — a stable service object — for all host communication; content state lives in the editor, not parent components.
9465  
95−**CRITICAL: React hooks rules in `src/transcript/main.tsx`**
66+| Domain | Atoms | Owner |
67+| --- | --- | --- |
68+| Theme | `themeAtom` | Global, IPC-synced |
69+| Editors | `editorDirtyAtom(key)`, `editorProcessingAtom(key)` | EditorHost writes, Tab reads |
70+| Sessions | `sessionUnreadAtom(id)`, `sessionProcessingAtom(id)` | AgenticPanel writes, UI reads |
71+| File Tree | `gitStatusAtom`, `expandedDirsAtom` | WorkspaceSidebar writes, FileTree reads |
72+| Trackers | `trackerCountsAtom` | TrackerService writes, UI reads |
9673  
97−The transcript React app runs inside WKWebView where errors are invisible (cross-origin `window.onerror` reports "Script error." with no details). This makes hooks violations especially dangerous -- the screen goes blank with no diagnostic information.
74+**Re-render isolation**: parents subscribe to lists of IDs; children subscribe to their own atoms. If you need `React.memo` to prevent re-renders, you have the wrong architecture.
9875  
99−Rules for editing `TranscriptApp` in `main.tsx`:
100−- **All hooks (`useState`, `useRef`, `useCallback`, `useMemo`, `useEffect`) must come BEFORE any early returns.** React requires the same hooks to run in the same order on every render. An early `return` before a hook means that hook runs on some renders but not others, crashing React with "Rendered more hooks than during the previous render."
101−- **The `TranscriptErrorBoundary` wraps the app** to catch render errors and display them on screen + report to the native bridge. Do not remove it.
102−- **The `postErrorToNative` helper** sends error details through `webkit.messageHandlers.bridge` so they appear in Xcode console logs with full stack traces. Use it in any new try-catch blocks.
103−- **Test after any change**: Always rebuild the transcript (`npm run ios:build:transcript`), copy to Xcode resources, and rebuild in Xcode. Vite build success does NOT mean React will render correctly at runtime.
76+For full patterns, see [/docs/EDITOR_STATE.md](/docs/EDITOR_STATE.md) and [/docs/JOTAI.md](/docs/JOTAI.md).
10477  
105−## Key Files
78+## Logging
10679  
107−| File | Purpose |
108−|------|---------|
109−| `Sources/App/AppState.swift` | Root observable object; owns database, crypto, and sync managers |
110−| `Sources/Sync/SyncManager.swift` | WebSocket sync with CollabV3; processes index responses and broadcasts |
111−| `Sources/Sync/SyncProtocol.swift` | All wire protocol types (Codable structs with CodingKeys) |
112−| `Sources/Database/DatabaseManager.swift` | GRDB schema migrations, queries, and project stats refresh |
113−| `Sources/Crypto/CryptoManager.swift` | AES-256-GCM encrypt/decrypt, deterministic project ID encryption |
114−| `Sources/Views/TranscriptWebView.swift` | WKWebView + Coordinator with JS bridge, TranscriptController |
115−| `Sources/Views/SessionDetailView.swift` | Session detail with transcript, scroll-to-top, jump-to-prompt |
116−| `Sources/Views/SessionListView.swift` | Time-grouped session list with search and swipe-to-delete |
117−| `Sources/Views/ProjectListView.swift` | Project list sorted by last activity with desktop connection indicator |
118−| `src/transcript/main.tsx` | React transcript app with `scrollToTop`, `scrollToMessage`, `getPromptList` JS bridge |
80+Three log destinations:
11981  
120−## Testing
121−- 68 Swift tests covering database, crypto, sync integration, and web view
122−- See [TESTING.md](./TESTING.md) for CI/CD pipeline details
123−- Tests run on both macOS (via Swift Package Manager) and iOS simulator (via Xcode)
82+- **Main process log**: `~/Library/Application Support/@nimbalyst/electron/logs/main.log` — main-process events, AI, sync, file ops; categories like `(MAIN)`, `(AI)`, `(API)`, `(SYNC)`.
83+- **Renderer console log** (dev mode only): `~/Library/Application Support/@nimbalyst/electron/nimbalyst-debug.log` — captured via `webContents.on('console-message')` in `src/main/index.ts`.
84+ 
85+Use the agent log access tools (`get_main_process_logs`, `get_renderer_debug_logs`) instead of asking users to paste logs. See [/docs/DEBUGGING_LOGS.md](/docs/DEBUGGING_LOGS.md).
86+ 
87+## Window State Persistence
88+ 
89+- **Global session state** restores all windows on restart (bounds, focus order, dev tools state).
90+- **Per-project state** restores window configuration, open file, AI panel width and collapsed state, draft inputs.
91+- **Session continuity** — chat sessions persist across restarts.
92+ 
93+## Theme Support
94+ 
95+Themes: Light, Dark (#2d2d2d / #1a1a1a / #3a3a3a), Crystal Dark (Tailwind gray scale), Auto.
96+ 
97+**Critical rules:**
98+- Never hardcode colors in CSS files — use CSS variables.
99+- `src/renderer/index.css` is the only place theme colors are defined.
100+- Apply themes by setting both the `data-theme` attribute and the CSS class on the root element.
101+ 
102+Comprehensive: [THEMING.md](./THEMING.md).
103+ 
104+## File Operations
105+ 
106+- **Drag-and-drop**: move files/folders in the Project Sidebar; hold Option/Alt to copy.
107+- **Context menus**: rename, delete, open in new window.
108+- **File watching**: auto-update on disk changes.
109+ 
110+## AI Providers
111+ 
112+Provider implementations live in `packages/runtime` — see `/packages/runtime/CLAUDE.md`. Electron-only pieces:
113+ 
114+- **Renderer panels**: `src/renderer/components/AIModels/panels/ClaudePanel.tsx`, `ClaudeCodePanel.tsx`
115+- **Claude Code installer**: `src/renderer/components/AIModels/services/CLIInstaller.ts` (manages local installation of `@anthropic-ai/claude-agent-sdk`)
116+ 
117+## macOS Code Signing & Notarization
118+ 
119+- **Certificate**: Developer ID Application
120+- **Builds**: `npm run build:mac:notarized` (notarized), `build:mac:local` (local testing)
121+- **Bundled tools**: ripgrep is signed; JAR files are excluded automatically (can't be notarized)
122+- **Entitlements**: hardened runtime with necessary exceptions
123+ 
124+## Git Worktree Integration
125+ 
126+Nimbalyst creates git worktrees for isolated AI coding sessions. See [/docs/WORKTREES.md](/docs/WORKTREES.md). The `worktrees` table stores metadata; `ai_sessions.worktree_id` links sessions to worktrees. IPC channels: `worktree:create`, `worktree:get-status`, `worktree:delete`, `worktree:list`, `worktree:get`.
127+ 
128+## Analytics
129+ 
130+See [/docs/ANALYTICS_GUIDE.md](/docs/ANALYTICS_GUIDE.md). **When adding, modifying, or removing PostHog events, update [/docs/POSTHOG_EVENTS.md](/docs/POSTHOG_EVENTS.md).**
124131  
RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack

RuleStack

Built by

Kynth Studio

Directory

Configs
Stacks
Compare formats
Diff two configs
Best AGENTS.md examples

Formats

AGENTS.md
CLAUDE.md
Cursor rules
Copilot instructions

Reference

Read API
Corpus health
Privacy Policy
Terms

RuleStack