-
Notifications
You must be signed in to change notification settings - Fork 1
Architecture
SnCode is an Electron application with a clear separation between the main process (Node.js) and the renderer process (React).
sncode/
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # CI: lint, typecheck, test, build
│ │ └── release.yml # Release: package + GitHub Release
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.yml
│ │ ├── feature_request.yml
│ │ └── config.yml
│ └── pull_request_template.md
├── build/ # App icons for electron-builder
├── scripts/
│ └── generate-icons.mjs # Icon generation from SVG
├── src/
│ ├── main/ # Electron main process
│ │ ├── main.ts # Window, IPC handlers, agent orchestration
│ │ ├── agent.ts # AI agent loop, streaming, tool calling
│ │ ├── store.ts # JSON file persistence
│ │ ├── project-tools.ts # Sandboxed file/command tools
│ │ ├── mcp.ts # MCP (Model Context Protocol) client
│ │ ├── skills.ts # Skills discovery and management
│ │ ├── oauth.ts # OAuth flows (Anthropic PKCE, OpenAI device code)
│ │ ├── credentials.ts # OS keychain via keytar
│ │ ├── preload.ts # Context bridge (IPC API)
│ │ └── project-tools.test.ts # Tool tests
│ ├── renderer/ # React frontend
│ │ ├── main.tsx # React entry point
│ │ ├── App.tsx # Main UI (~2470 lines)
│ │ ├── SettingsModal.tsx # Settings panel (~800 lines)
│ │ ├── ErrorBoundary.tsx # React error boundary
│ │ ├── styles.css # CSS variables & theme system
│ │ └── vite-env.d.ts # Vite type declarations
│ └── shared/ # Shared types & utilities
│ ├── types.ts # All TypeScript interfaces
│ ├── schema.ts # Zod validation schemas
│ ├── models.ts # Model catalog & helpers
│ ├── schema.test.ts # Schema tests
│ └── models.test.ts # Model tests
├── package.json # Dependencies & electron-builder config
├── vite.config.ts # Vite bundler config
├── vitest.config.ts # Test runner config
├── tsconfig.json # Renderer TypeScript config
├── tsconfig.node.json # Main process TypeScript config
├── eslint.config.js # ESLint config
└── index.html # Electron entry HTML (CSP)
┌─────────────────────────────────────────────────────┐
│ Main Process (Node.js) │
│ │
│ ┌──────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ main.ts │──│ agent.ts │──│ project-tools.ts │ │
│ │ (window, │ │ (AI loop,│ │ (file ops, shell, │ │
│ │ IPC) │ │ streaming│ │ glob, grep) │ │
│ └──────────┘ └──────────┘ └────────────────────┘ │
│ │ │ │
│ ┌──────────┐ ┌──────────┐ ┌────────────────────┐ │
│ │ store.ts │ │ oauth.ts │ │ credentials.ts │ │
│ │ (JSON │ │ (OAuth │ │ (keytar/OS │ │
│ │ state) │ │ flows) │ │ keychain) │ │
│ └──────────┘ └──────────┘ └────────────────────┘ │
│ │ │
│ ┌──────────┐ ┌──────────┐ │
│ │skills.ts │ │ mcp.ts │ │
│ │(skill │ │(MCP JSON │ │
│ │ loader) │ │ RPC) │ │
│ └──────────┘ └──────────┘ │
└─────────────────┬───────────────────────────────────┘
│ IPC (contextBridge via preload.ts)
┌─────────────────┴───────────────────────────────────┐
│ Renderer Process (React) │
│ │
│ ┌──────────────────────────────────────────────┐ │
│ │ App.tsx │ │
│ │ ┌─────────┐ ┌──────────┐ ┌───────────────┐ │ │
│ │ │Sidebar │ │Chat area │ │Right sidebar │ │ │
│ │ │(projects│ │(messages,│ │(file preview, │ │ │
│ │ │ threads)│ │ input) │ │ diff, tasks) │ │ │
│ │ └─────────┘ └──────────┘ └───────────────┘ │ │
│ │ ┌─────────┐ ┌──────────┐ ┌───────────────┐ │ │
│ │ │FileTree │ │SearchBar │ │OnboardingModal│ │ │
│ │ └─────────┘ └──────────┘ └───────────────┘ │ │
│ └──────────────────────────────────────────────┘ │
│ ┌──────────────────────────────────────────────┐ │
│ │ SettingsModal.tsx │ │
│ └──────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────┘
All communication between main and renderer processes goes through Electron's contextBridge (defined in preload.ts). The renderer accesses the API via window.sncode.*.
The renderer calls methods like window.sncode.sendMessage(...), which trigger ipcMain.handle(...) handlers in main.ts.
The agent emits events during execution via webContents.send(...):
| Channel | Purpose |
|---|---|
agent:status |
Agent status changes (running, idle, error, cancelled) |
agent:chunk |
Streaming text chunks |
agent:tool |
Tool invocation notifications |
agent:message |
Complete messages (tool results, final assistant text) |
The renderer listens with window.sncode.on(channel, callback).
The agent runs in the main process (agent.ts):
- Build messages — Convert thread history to provider-specific format
- API call — Stream response from Anthropic or OpenAI
- Parse response — Extract text and tool calls
-
Execute tools — Run tool calls sequentially (except
spawn_taskwhich runs in parallel) -
Loop — If tool calls were made, feed results back and repeat (up to
maxToolSteps) - Complete — Save final assistant message
Both providers use native streaming:
-
Anthropic:
client.messages.stream()withcontent_block_deltaevents -
OpenAI:
client.responses.create({ stream: true })with delta events
Text chunks are emitted in real-time to the renderer for immediate display.
Application state is persisted as a JSON file at:
-
Windows:
%APPDATA%/sncode/sncode-state.json -
macOS:
~/Library/Application Support/sncode/sncode-state.json -
Linux:
~/.config/sncode/sncode-state.json
The store uses structuredClone for immutable state snapshots and supports legacy migration.
Stored in the OS keychain under the service name sncode.providers. Each provider has its credential stored as either a plain API key string or an oauth: prefixed JSON string.
-
Sandboxed renderer:
contextIsolation: true,nodeIntegration: false,sandbox: true -
Content Security Policy: Strict CSP in
index.html - Path traversal protection: All file operations validate paths stay within project root
-
Navigation prevention:
will-navigateevent is blocked -
External links: Only
https://URLs via system browser - Application menu: Disabled entirely
- Command safety: Auto-injects exclusion flags for recursive search commands
| Component | Technology |
|---|---|
| Desktop framework | Electron 37 |
| Frontend | React 19, TypeScript 5.9 |
| Styling | Tailwind CSS 4 |
| Bundler | Vite 7 |
| Markdown | react-markdown + remark-gfm |
| Syntax highlighting | highlight.js |
| Validation | Zod 4 |
| Credential storage | keytar (OS keychain) |
| AI SDKs | @anthropic-ai/sdk, openai |
| Testing | Vitest |
| Linting | ESLint 9 |
| Packaging | electron-builder 26 |
SnCode Wiki
Getting Started
User Guide
Agent System
Technical
Support