SAM (Synthetic Autonomic Mind) is a native macOS AI assistant built with Swift and SwiftUI. This guide provides essential information for all AI coding agents (CLIO, Cursor, Aider, GitHub Copilot, etc.).
- Language: Swift 6.0+ (strict concurrency mode enabled)
- Platform: macOS 14.0+, Apple Silicon (arm64) preferred, Intel support secondary
- Build System: Swift Package Manager (SPM) + Makefile
- Architecture: Modular, actor-based concurrency, privacy-first design
- Testing: XCTest framework, CI/CD via GitHub Actions
- Repository: https://github.com/SyntheticAutonomicMind/SAM
- License: GPL-3.0-only
- macOS 14.0+ (development on macOS 15.0+)
- Xcode 14.0+ with Swift 6.0+
- Command line tools:
xcode-select --install - ccache (optional, speeds up builds):
brew install ccache
# Clone with submodules (required for llama.cpp)
git clone --recursive https://github.com/SyntheticAutonomicMind/SAM.git
cd SAM
# First build automatically builds llama.cpp framework
make build-debug# Debug build (faster, includes debug symbols)
make build-debug
# Release build (optimized for production)
make build-release
# Development release (auto-increments -dev.N)
make build-dev
# Clean all build artifacts
make clean
# Build only llama.cpp framework (macOS only)
make llamacpp# Run debug build
.build/Build/Products/Debug/SAM.app/Contents/MacOS/SAM
# Run release build
.build/Build/Products/Release/SAM.app/Contents/MacOS/SAM
# Open in Finder
open .build/Build/Products/Debug/SAM.app# Run all unit tests
swift test
# Run specific test suite
swift test --filter MyTestSuite
# Run all tests (unit + e2e)
./Tests/run_all_tests.sh
# Run MCP API tests
./Tests/mcp_api_tests.sh
# Run tests like CI/CD pipeline
./scripts/test_like_pipeline.shALL Swift files must begin with:
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: Copyright (c) 2025 Andrew Wyatt (Fewtarius)- Classes/Structs/Enums:
PascalCase(e.g.,ConversationManager,ModelState) - Functions/Variables:
camelCase(e.g.,loadModel(),conversationHistory) - Constants:
camelCase, NOTSCREAMING_SNAKE_CASE(e.g.,let maxTokens = 2048) - Protocols: Descriptive names ending in
Protocolwhen needed (e.g.,ToolRegistryProtocol) - Actors:
PascalCasewithActorsuffix when ambiguous (e.g.,ModelLoadingActor)
- Documentation comments:
///(shown in Xcode Quick Help) - Implementation comments:
//(explain WHY, not WHAT) - Section markers:
// MARK: -to organize code sections - Document intent: Code should be self-documenting; comments explain decisions
Use swift-log Logger (NEVER print() or NSLog()):
private let logger = Logger(label: "com.sam.modulename")
logger.debug("Debug message")
logger.info("Info message")
logger.warning("Warning message")
logger.error("Error message")Logger label format: com.sam.<module> (e.g., com.sam.orchestrator, com.sam.conversation)
SAM uses Swift 6 strict concurrency checking. All code MUST follow these rules:
-
Sendable Conformance Required
- All types crossing actor boundaries must conform to
Sendable - Use
@unchecked SendableONLY when safe, with documentation
- All types crossing actor boundaries must conform to
-
MainActor Isolation for UI
- All SwiftUI views must be
@MainActor - All ViewModels must be
@MainActor - NSAttributedString operations MUST run on MainActor
- All SwiftUI views must be
-
Capture Before Crossing Actor Boundaries
// ❌ BAD await withTaskGroup { group in group.addTask { await self.property.doSomething() } } // ✅ GOOD let property = self.property await withTaskGroup { group in group.addTask { await property.doSomething() } }
-
Expected Build Results
- 0 errors (always)
- ~211 warnings (Sendable-related, non-blocking, acceptable)
- Run
make build-debugto verify locally - Run
./scripts/test_like_pipeline.shto simulate CI/CD
See project-docs/SWIFT6_CONCURRENCY_MIGRATION.md for:
- Wrapper types for non-Sendable dictionaries
nonisolated(unsafe)safe usage- Capture patterns in loops
- Custom type erasure for Sendable compliance
Sources/
├── SAM/
│ ├── main.swift - Entry point, AppDelegate
│ └── App.swift - SwiftUI App definition
├── ConversationEngine/ - Core conversation system, memory, database
│ ├── ConversationManager.swift
│ ├── MemorySystem/
│ ├── Database/ (SQLite schema, queries)
│ └── Streaming/ (Response streaming)
├── MLXIntegration/ - Local model inference (Apple Silicon)
│ ├── ModelManager.swift
│ ├── MLXSession.swift
│ └── Tokenization/
├── UserInterface/ - SwiftUI components and views
│ ├── MainWindow.swift
│ ├── ConversationView.swift
│ ├── SettingsView.swift
│ └── Components/
├── ConfigurationSystem/ - App preferences, settings
│ ├── PreferencesManager.swift
│ ├── AppConfig.swift
│ └── PropertiesStorage/
├── APIFramework/ - Multi-provider support, orchestration
│ ├── APIProvider.swift (protocol)
│ ├── Providers/ (OpenAI, Anthropic, DeepSeek, GitHub Copilot)
│ ├── AgentOrchestrator.swift (multi-step workflows)
│ └── ToolCallExtractor.swift
├── MCPFramework/ - Model Context Protocol tools
│ ├── MCPTool.swift (protocol)
│ ├── Tools/ (10 tools, 60+ operations)
│ ├── ToolRegistry.swift
│ └── ToolResult.swift
├── SharedData/ - Shared types, thread-safe storage
│ ├── SharedTopics.swift
│ ├── Storage.swift
│ └── Locking/
└── VoiceFramework/ - Speech recognition, TTS, wake word
├── SpeechRecognizer.swift
├── TextToSpeech.swift
└── WakeWordDetector.swift
- Package.swift - SPM manifest (dependencies, targets)
- Info.plist - App metadata (version, bundle ID, entitlements)
- Makefile - Build automation, llama.cpp, signing
- SAM.entitlements - Sandbox permissions
- BUILDING.md - Comprehensive build instructions
- CONTRIBUTING.md - Contribution guidelines
- VERSIONING.md - Version scheme and release process
- external/llama.cpp/ - Git submodule for local model support (macOS only)
- external/ml-stable-diffusion/ - Apple's image generation
- .github/workflows/ - CI/CD automation (build, test, release)
Tests/
├── APIFrameworkTests/ - Provider integration, orchestration
├── ConfigurationSystemTests/ - Settings, preferences
├── ConversationEngineTests/ - Conversation, memory, database
├── MCPFrameworkTests/ - Tool registry, tool execution
├── UserInterfaceTests/ - UI components
├── SecurityFrameworkTests/ - Security operations
├── e2e/ - End-to-end integration
├── test_workspace/ - Test data directory
├── run_all_tests.sh - Master test runner
├── mcp_api_tests.sh - MCP API integration tests
└── KNOWN_ISSUES.md - Known test issues
# All unit tests
swift test
# Specific suite
swift test --filter APIFrameworkTests
# With verbose output
swift test --verbose
# All tests (unit + e2e)
./Tests/run_all_tests.sh
# MCP API tests
./Tests/mcp_api_tests.sh
# Like CI/CD pipeline
./scripts/test_like_pipeline.sh- New features MUST have tests
- Tests must pass in CI/CD (GitHub Actions)
- Use
XCTestframework - Mock external dependencies (network, file system)
- Tests run on macOS 14.0+
- No external dependencies required
- Tests should be deterministic and fast
- Test data in
Tests/test_workspace/
- .github/workflows/ci.yml - Build and test on every push
- .github/workflows/release.yml - Build, sign, notarize, distribute
- .github/workflows/nightly-dev.yml - Nightly development builds
- .github/workflows/update-homebrew-cask.yml - Auto-update Homebrew cask
- Format:
YYYYMMDD.RELEASE[-dev.BUILD] - Example:
20260127.1(stable),20260127.1-dev.3(dev) - File:
Info.plist(CFBundleShortVersionString)
-
Stable Releases
- Version:
YYYYMMDD.RELEASE - Published as GitHub releases
- Distributed via Homebrew and automatic updates
- Version:
-
Development Releases
- Version:
YYYYMMDD.RELEASE-dev.BUILD - Published as GitHub pre-releases
- Opt-in via Preferences -> "Receive development updates"
- Version:
All commits MUST follow Conventional Commits format:
<type>(<scope>): <subject>
[optional body]
[optional footer]
feat- New featurefix- Bug fixrefactor- Code refactoring (no functional changes)docs- Documentation changestest- Test additions/updateschore- Maintenance (dependencies, build config)perf- Performance improvementsstyle- Code style changes (formatting)ci- CI/CD changes
ui- User interface changesapi- API provider implementationsmcp- MCP framework and toolsconversation- Conversation managementconfig- Configuration systembuild- Build system and dependenciesvoice- Voice frameworkimage- Image generationmemory- Memory and LTM
git commit -m "feat(mcp): Add web scraping tool with structured data extraction"
git commit -m "fix(ui): Prevent toolbar overflow on narrow windows"
git commit -m "refactor(api): Extract streaming logic into separate service"
git commit -m "docs(api): Add ALICE integration guide"- Create file in
Sources/MCPFramework/Tools/MyTool.swift - Implement
MCPToolprotocol - Register in
UniversalToolRegistry - Add tests in
Tests/MCPFrameworkTests/ - Document in tool's docstring
- Add entry to CHANGELOG
Important: If your tool needs to access SAM data (system prompts, mini-prompts, settings), access the manager directly (e.g., SystemPromptManager.shared) instead of calling the HTTP API. Tools run in the same process, so direct access is simpler and doesn't require authentication.
When adding UI state that should persist per conversation:
-
ConversationSettings struct (
ConversationModel.swift):- Add property to struct (e.g.,
public var showingMyPanel: Bool) - Add to init parameters with default value
- Add to
CodingKeysenum - Add to decoder with
decodeIfPresentand default
- Add property to struct (e.g.,
-
ChatWidget integration (
ChatWidget.swift):- Add
@State private var showingMyPanel = false - Add
.onChange(of: showingMyPanel)handler callingsavePanelState(panel: "mypanel", value: newValue) - Add
case "mypanel":tosavePanelStateswitch - Add
showingMyPanel = conversation.settings.showingMyPanelinsyncWithActiveConversation() - Add
conversation.settings.showingMyPanel = showingMyPanelinsaveUISettings()
- Add
-
Important: SwiftUI's
onChangedoesn't fire when value is already set before view loads. Always ALSO set up state inonAppearfor initial load.
- Create provider in
Sources/APIFramework/Providers/MyProvider.swift - Implement
APIProviderprotocol - Handle streaming responses
- Add tests with mock responses
- Register in
ProviderRegistry
# Update SPM dependencies
swift package update
# Check for updates
swift package describe
# Pin problematic versions in Package.swift as needed# Create signed, notarized DMG
make distribute
# Outputs:
# - dist/SAM-{VERSION}.dmg (installer)
# - dist/SAM-{VERSION}.zip (update archive)
# - Updated appcast.xml (for Sparkle updates)- NO telemetry, NO tracking by default
- All conversations stored locally in SQLite
- API credentials stored in UserDefaults (consider KeychainManager for sensitive keys)
- Local models run entirely offline (MLX, llama.cpp)
- Cloud providers (OpenAI, Anthropic, etc.) are opt-in only
See SAM.entitlements for:
- File system access requirements
- Network access for API providers
- Microphone access for voice
- Camera access (if image analysis needed)
# Ensure submodules are initialized
git submodule update --init --recursive
# Rebuild llama.cpp
make clean llamacpp
# Full rebuild
make build-debug- See
project-docs/SWIFT6_CONCURRENCY_MIGRATION.md - Check for Actor boundaries
- Verify Sendable conformance
- Capture values before crossing async boundaries
- Ensure
Tests/test_workspace/exists - Run tests from project root:
cd SAM && swift test - Check file permissions:
chmod -R 755 Tests/
# Ensure release build exists
make build-release
# Check permissions
ls -la .build/Build/Products/Release/SAM.app
# Try manually
make create-dmg- Website: https://www.syntheticautonomicmind.org
- GitHub: https://github.com/SyntheticAutonomicMind/SAM
- Issues: https://github.com/SyntheticAutonomicMind/SAM/issues
- Discussions: GitHub Discussions (for questions)
- Internal Docs:
project-docs/directory - Build Guide:
BUILDING.md - Contributing:
CONTRIBUTING.md - Versioning:
VERSIONING.md
If you're stuck:
- Check existing issue tracker
- Review
project-docs/for architecture notes - Look at similar code patterns
- Read the detailed comments in source files
- Check Tests for usage examples
This guide is maintained alongside the SAM codebase and updated with each major release.