Architecture Overview
August 21, 2026 · View on GitHub
This document describes the high-level architecture of Skales, including the technology stack, data flow, and major subsystems.
1. Technology Stack
Desktop Shell: Electron
- Cross-platform desktop application (macOS, Windows, Linux)
- Provides file system access, window management, and native OS integrations
- Handles auto-updates and system notifications
Web Application: Next.js 14
- React-based frontend framework
- Server-side rendering and static generation
- API routes for backend services
Language: TypeScript
- Type-safe development across frontend and backend
- Better IDE support and catching errors at compile time
Architecture:
The Electron app launches a Next.js server on localhost and renders it in a native window. The port is not fixed: Skales walks 3000 through 3009 and binds the first free one, so a second instance moves the whole app, API included. SKALES_URL is how external callers follow it.
┌─────────────────────────────────┐
│ Electron Main Process │
│ (Window, File I/O, OS API) │
└──────────────┬──────────────────┘
│ localhost:3000
┌──────────────▼──────────────────┐
│ Next.js Web App │
│ (React UI, API Routes) │
└─────────────────────────────────┘
2. Data Directory Structure
All user data is stored locally in ~/.skales-data/ with no cloud dependency. This directory is created automatically on first launch.
~/.skales-data/
├── settings.json # User preferences and configuration
├── sessions/ # Conversation histories
│ ├── session-001.json
│ ├── session-002.json
│ └── ...
├── memories/ # One JSON file per memory, plus its state files
│ ├── mem_a1b2c3d4e5f6a7b8.json
│ └── ...
├── agent-skills/ # SKILL.md skills, one folder each
│ ├── agent-skills-state.json # which skills are on; absent means all on
│ ├── research-skill/
│ │ └── SKILL.md
│ └── ...
├── skills/ # Executable custom skills, plus manifest.json
├── agents/ # Agent definitions and execution records
├── cron/ # Scheduled tasks, one JSON file each
├── mcp-servers.json # MCP server configuration
├── workflows.json # Saved workflow templates
├── local-models/ # Downloaded on-device models
├── workspace/ # Project folders created by Flow and Skales Code
├── devkit/ # devkit.json and an optional DEVKIT.md
└── logs/ # Application logs
Key Principles:
- Local-First: Everything stored locally, never synced to cloud
- User Control: Users own their data; can browse and edit directly
- No Dependencies: Zero reliance on external services for basic functionality
- Portable: Entire
~/.skales-data/directory can be backed up or migrated
3. Orchestrator Flow
The orchestrator is the core decision engine that processes user messages and coordinates LLM responses.
1. User enters message
↓
2. Load conversation context
↓
3. Load memories (injected into system prompt)
↓
4. Load available tools (from skills, integrations, MCP)
↓
5. The orchestrator decides: a tool call, or an answer
↓
6. LLM selects provider and generates response
↓
7. LLM returns tool calls or final message
├─ If tool calls:
│ ↓
│ Execute each tool call
│ ↓
│ Gather results
│ ↓
│ Loop: return results to LLM for next iteration
│
└─ If no tool calls:
↓
Display message to user
↓
Save to conversation history
Key Responsibilities:
- Provider Selection: Routes requests to configured AI provider (OpenAI, Claude, etc.)
- Context Management: Loads conversation history, memories, and available tools
- Tool Execution: Safely executes tool calls with proper error handling
- Loop Control: Manages multi-turn reasoning until task completion or user intervention
- Safety Gating: Applies safety rules (auto vs. confirm tools)
4. Provider Abstraction Layer
Skales supports 26 providers through a unified abstraction layer. See providers.md for the full list.
Provider Interface
interface Provider {
name: string
sendMessage(
messages: Message[],
tools: Tool[],
options?: ProviderOptions
): Promise<Response>
listModels(): Promise<Model[]>
getMaxTokens(): number
}
Supported Provider Formats
-
OpenAI Compatible — the large majority:
- OpenAI, OpenRouter, Groq, Mistral, DeepSeek, Together AI, Minimax, Moonshot, GLM, Qwen, Hunyuan, GigaChat, Atlas Cloud, Cloudflare Workers AI, Nvidia NIM, Hugging Face, and every local runtime (Ollama, LM Studio, Skales Local, Unsloth, and the Custom slot)
- API Format: OpenAI
messagesformat - Tool Format: OpenAI function calling
-
Anthropic Format:
- Anthropic Claude models
- API Format: Anthropic
messagesformat - Tool Format: Anthropic tool use
-
Google Format:
- Google Gemini models
- API Format: Google
generativeAIformat - Tool Format: Google function calling
Provider Selection Flow
- User configures default provider in Settings → Providers
- Per-conversation override: select different provider
- Orchestrator calls
providers.getActiveProvider() - Provider adapter translates tool format to provider's API
- Response is translated back to unified format
Benefits:
- Switch providers without changing conversation logic
- Compare models from different providers
- Use cheapest provider for simple tasks, most capable for complex ones
5. Tool System
Skales includes 257 built-in tools, plus whatever MCP servers and custom skills add.
Tool Organization
Tools carry a category. As of 12.8.4:
| Category | Tools | Examples |
|---|---|---|
| core | 119 | analyze_image, search_web, read_skill, create_task |
| media | 36 | image, audio and video generation and editing |
| communication | 29 | email, messaging channels, calendar |
| system | 22 | processes, system info, notifications |
| file | 15 | read_file, write_file, edit_file, append_file |
| browser | 12 | real browser control |
| productivity | 9 | tasks, notes, documents |
| web | 6 | fetch_web_page, extract_web_text |
| social | 6 | posting and reading on connected accounts |
| automation | 3 | scheduling and workflow triggers |
GET /api/cli/tools lists what a given install actually offers, which is the honest answer for any specific machine — add-ons and integrations change it.
Safety Levels
Each tool has a safety level:
Each tool is auto, confirm or manual.
- auto — runs without asking:
read_file,search_web,fetch_web_page,extract_web_text,search_sessions,read_skill,list_tasks,git_status,git_diff,ask_user - confirm — the user approves first:
write_file,append_file,edit_file,replace_in_file,delete_file,download_file,git_commit,git_push,test_run,deploy_project - manual — reserved for actions the agent never starts on its own
Tool Gating via Add-Ons
Tools are gated by add-ons, not by skills. An add-on is a surface or an integration you chose to switch on; a capability the assistant simply has — reading files, summarizing, seeing a screenshot — is core and cannot be switched off.
Switching an add-on off does not merely hide its tools: they are never offered to the model in the first place, so it cannot suggest what it cannot see. The matching sidebar entry and settings section disappear at the same time.
Three further states exist. Retired means switched off for good and impossible to re-enable — GitHub, DLNA casting and the playground are retired in 12.8.4. Parked means no longer offered to new users: anyone who has it on keeps every tool, setting and route, and only the sidebar entry is gone.
MCP-Provided Tools
Tools from MCP servers are auto-discovered and namespaced:
- MCP server name:
acme-api - Tool name:
get_customers - In Skales:
mcp_acme_api_get_customers
This prevents naming collisions when multiple MCP servers provide similar functionality.
6. Skills
A skill is instruction text, not a tool bundle. It teaches the agent how to do something with the tools it already has; it does not grant new ones. The surface is the Custom Skills page — there is no "Agent Skills" settings section, and /skills is the Add-Ons screen, which is a different thing.
Skill File Structure
~/.skales-data/agent-skills/
├── agent-skills-state.json # which skills are on; no file means all on
└── research-skill/
├── SKILL.md # frontmatter + instructions
├── scripts/ # optional, read on demand
└── references/ # optional, read on demand
28 skills ship built in, read-only inside the app and gated by the same state file.
SKILL.md Format
---
name: research-assistant
description: Search the web, fetch articles, and summarize findings
---
# Research Assistant
When asked to research a topic, start with...
Only name and description are required, and only a small set of keys is read at all — see agent-skills.md for the exact list. There is no tools, no system_prompt and no enabled key in the frontmatter.
How Skills Work
- Load: every SKILL.md under
~/.skales-data/agent-skills/, plus the built-ins - Progressive disclosure: while the combined body length of model-invocable skills stays under about 12,000 characters, all bodies go into the prompt. Above that, the prompt carries a manifest of names and shortened descriptions, plus the full body of any skill the message names by name.
- On demand: the
read_skilltool loads one body when the agent decides it needs it - Enable/Disable: on the Custom Skills page; the state is opt-out, so a fresh install has everything on
- User-invoked skills never appear in the manifest — they enter only when a human names them
Creating Custom Skills
- Create
~/.skales-data/agent-skills/my-skill/and write aSKILL.md - Or import one on the Custom Skills page: a GitHub URL, a local folder, or pasted text
- Or have a model write one, which is validated and test-loaded before it installs
Portability
Skills are portable across tools that support the SKILL.md format:
- Export from Skales → use in another tool
- Import from other tools → use in Skales
- No vendor lock-in
7. MCP (Model Context Protocol) Integration
Skales supports MCP servers using JSON-RPC 2.0 protocol.
Supported Transports
Stdio (Local processes):
Skales ↔ Process (stdio) ↔ Tool
Example: Local Python script, Node.js server
SSE over HTTP (Remote servers):
Skales ↔ HTTP Client ↔ SSE Server ↔ Tool
Example: Remote API, cloud-hosted MCP server
MCP Configuration
MCP servers are configured in ~/.skales-data/settings.json:
{
"mcp_servers": [
{
"name": "acme-api",
"type": "stdio",
"command": "node",
"args": ["~/.local/mcp/acme-api/server.js"]
},
{
"name": "remote-tools",
"type": "sse",
"url": "https://mcp.example.com/sse"
}
]
}
Tool Discovery and Naming
- MCP server registers tools:
get_customers,create_order - Skales namespaces them:
mcp_acme_api_get_customers,mcp_acme_api_create_order - Tools appear in available tools list
- LLM can call them in conversations
Protocol Flow
1. Skales requests: { "method": "resources/list" }
2. MCP Server responds: { "resources": [...] }
3. Skales requests: { "method": "tools/call", "params": {...} }
4. MCP Server executes and responds: { "result": ... }
5. Skales returns result to LLM for processing
Stability: MCP servers are monitored for crashes and automatically restarted.
8. Memory System
Skales extracts and learns from conversations to build a personalized knowledge base.
Memory Extraction Flow
1. User conversation stored in session history
↓
2. Memory scanner runs periodically (or on-demand)
↓
3. Regex NLP patterns identify facts, preferences, contacts
↓
4. Extract structured data (JSON) and unstructured (Markdown)
↓
5. Store in ~/.skales-data/memories/
↓
6. Inject memories into system prompt on next message
Memory Types
There is no personality.md, no preferences.json and no contacts.json. Every memory is one JSON file in ~/.skales-data/memories/, in a single shape:
{
"id": "mem_a1b2c3d4e5f6a7b8",
"category": "preference",
"content": "Prefers concise, direct answers",
"source_conversation_id": "session_1755800000000",
"extracted_at": 1755800000000,
"relevance_keywords": ["style", "tone"]
}
category is a plain string. The ones the app colours are preference, fact, action_item, contact, url, location and topic, but any value is stored. extracted_at is epoch milliseconds.
Two files in that folder are bookkeeping rather than memories: _state.json and _deleted_blocklist.json.
Memory Injection
On every conversation turn:
- Load relevant memories from
~/.skales-data/memories/ - Inject into system prompt context window
- Memories provide personalization without retaining history
Example injected prompt:
System: Based on your memories, you should:
- Use concise, direct language
- Provide technical depth (user is an AI researcher)
- Include code examples when relevant
Memory Updates
Memories are continuously updated:
- New conversations analyzed for facts
- New contacts extracted from mentions
- Preferences updated based on feedback
- User can manually edit memory files anytime
9. Discover Feed
The Discover Feed is a social network for AI agents to share findings and learn from each other.
Local Activity → Shared Feed
1. User has conversation in Skales
↓
2. Interesting findings extracted (opt-in)
↓
3. Anonymized summary created
↓
4. Posted to Discover Feed with anonymous ID
↓
5. Other agents see post and can interact
Anonymization
- User identity replaced with anonymous ID (generated from opt-in telemetry)
- Sensitive information filtered (API keys, passwords, PII)
- Topic and findings summarized, not copied verbatim
- Users can see what will be shared before posting
Discover Feed Features
- Posts: Share research findings, interesting discoveries
- Reactions: Like, find useful, or save interesting posts
- Comments: Agents can reply to discussions (anonymously)
- Topics: Browse by research area, skill, or tool
Opt-In Model
Users must explicitly enable:
- Settings → Privacy → Discover Feed
- Choose what types of activities to share
- Review summaries before they're posted
- Opt out anytime—deletes past posts
10. Auto-Updater
Skales automatically checks for and downloads updates.
Update Check Flow
1. Electron app starts
↓
2. AutoUpdater checks for new version
(configured in electron-builder)
↓
3. If update available: download in background
↓
4. On next restart: install update automatically
↓
5. Or: show update prompt to user
Update settings live in Settings → Updates.
Download Formats
- macOS: DMG (disk image)
- Windows: NSIS installer
- Linux: AppImage (portable). A
.debis published alongside it for Debian, Ubuntu and Mint, where the AppImage falls foul of newer AppArmor policy.
Update Page
When an update is available:
- User sees notification
- Clicking opens Update Page showing:
- Version number (e.g., "v2.1.0")
- Changelog and new features
- Download link
- User can:
- Download manually
- Auto-install on next launch
- Skip this update
- View release notes
Rollback
There is no automatic rollback. To go back, reinstall the previous version from the releases page — your data is untouched, because it lives in ~/.skales-data/ and not inside the app.
Data Flow Diagram
Here's a complete flow from user input to tool execution:
User Input
↓
┌─────────────────────────────────┐
│ Load Context │
│ - Conversation history │
│ - User memories │
│ - Available tools │
└────────────┬────────────────────┘
↓
┌─────────────────────────────────┐
│ Build Message with Context │
│ - System prompt │
│ - Previous messages │
│ - Tool definitions │
│ - Memory context │
└────────────┬────────────────────┘
↓
┌─────────────────────────────────┐
│ Call AI Provider │
│ - Selected provider adapter │
│ - Format: OpenAI/Anthropic/etc │
│ - Include available tools │
└────────────┬────────────────────┘
↓
AI Response
/ | \
/ | \
Text TC None
\ | /
\ | /
↓ ↓ ↓
┌─────────────────┐
│ Has Tool Calls? │
└────┬─────┬──────┘
│ │
Yes No
│ │
↓ ↓
┌──────────────────┐ ┌─────────────┐
│ Execute Tools │ │ Display │
│ - Check safety │ │ Response │
│ - Ask if needed │ │ │
│ - Gather results │ └────┬────────┘
└────┬─────────────┘ │
│ │
┌────┴──────────┐ │
│ Loop to LLM │ │
│ with results │ │
└────┬──────────┘ │
│ │
└──────────┬──────────┘
↓
┌──────────────────┐
│ Save to History │
│ Extract Memories │
│ Update Context │
└──────────────────┘
Deployment and Packaging
Build Process
# Install dependencies
npm install
# Build Next.js app
npm run build
# Package with Electron
npm run electron-build
Output Artifacts
- macOS:
skales-x.x.x.dmg(disk image) - Windows:
skales-x.x.x.exe(installer) - Linux:
skales-x.x.x.AppImage(portable)
Auto-Update Metadata
- GitHub releases tagged with version
- Update server serves download URLs and checksums
- Electron autoUpdater downloads from releases
Performance Considerations
Optimization Areas
- Conversation Loading: Lazy-load older messages to keep UI responsive
- Memory Injection: Only inject relevant memories (semantic search)
- Tool Discovery: Cache MCP tool definitions; refresh periodically
- Database Indexes: Conversations indexed by date for fast lookup
- Memory Usage: Offload large files to disk; keep working set small
Scalability Limits
- Conversations: 10,000+ conversations work fine (stored as individual files)
- Memory Size: Keep memories under 100KB for fast injection
- Tools: 200+ tools manageable; use filtering if more
Security Architecture
Data Protection
- Local Storage: All data encrypted at rest using system keychain (macOS/Windows)
- In Transit: API calls over HTTPS only
- Credentials: API keys stored in encrypted local storage, never sent to Skales servers
- Isolation: Each user has isolated data directory
Tool Safety
- Auto vs. Confirm: Tools gated by safety level
- Confirmation UI: User must approve sensitive operations
- Sandboxing: Tool execution limited to specific permissions
- Audit Log: All tool executions logged for review
Provider Trust
- No Data Logging: Skales sends only necessary context to providers
- Provider Selection: Users choose which provider to use
- Fallback: If preferred provider down, user can switch providers
- Local-First: Most functionality works without any provider
Extensibility
Developers can extend Skales in several ways:
- MCP Servers: Add custom tools via MCP protocol
- Custom Skills: Write SKILL.md to bundle tools and prompts
- Integration Plugins: Connect to external services
- DevKit REST API: Drive chat, memory, sessions, models, scheduling and MCP from outside the app
Guides: MCP Servers, Custom Skills, Integrations, API Reference. Capabilities maps everything the app does that the DevKit does not yet reach.