mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 10:36:35 +00:00
18278a3357
Native SwiftUI app providing full visibility into the Hermes AI agent: - Dashboard with system health, token usage, and cost tracking - Sessions browser with conversation detail and FTS5 search - Activity feed with tool call inspector (read/edit/execute/fetch/browser) - Embedded terminal chat via SwiftTerm with full ANSI/Rich rendering - Memory viewer/editor with live file-watching refresh - Skills browser by category with file content viewer - Cron job viewer with output display - Real-time log tailing with level filtering - Settings display with raw config and Finder path links - Menu bar status icon with quick actions Architecture: MVVM-Feature, zero dependencies beyond SwiftTerm, read-only SQLite access, Swift 6 strict concurrency. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3.8 KiB
3.8 KiB
Scarf — Architecture
Pattern: MVVM-Feature
Per project standards, every feature is a self-contained module owning Models, ViewModels, and Views.
scarf/
Core/
Services/ Hermes data access (SQLite, file I/O, ACP)
Models/ Plain data structs for Hermes entities
Features/
Dashboard/
Views/ DashboardView
ViewModels/ DashboardViewModel
Sessions/
Views/ SessionsView, SessionDetailView
ViewModels/ SessionsViewModel
Activity/
Views/ ActivityView
ViewModels/ ActivityViewModel
Chat/
Views/ ChatView
ViewModels/ ChatViewModel
Memory/
Views/ MemoryView
ViewModels/ MemoryViewModel
Skills/
Views/ SkillsView
ViewModels/ SkillsViewModel
Cron/
Views/ CronView
ViewModels/ CronViewModel
Logs/
Views/ LogsView
ViewModels/ LogsViewModel
Settings/
Views/ SettingsView
ViewModels/ SettingsViewModel
Navigation/
AppCoordinator.swift
SidebarView.swift
Navigation
AppCoordinator is @Observable and injected via .environment() at the app root. It owns:
selectedSection: SidebarSection— which feature is activeselectedSessionID: String?— drill-down into a session
One NavigationSplitView at top level, driven by the coordinator. Leaf views read but never own navigation state.
Services
HermesDataService
- Opens
~/.hermes/state.dbread-only via SQLite3 C API - Queries
sessionsandmessagestables - Provides session list, message history, search (FTS5), and aggregate stats
- Polling-based refresh (watches WAL modification time)
HermesFileService
- Reads config.yaml (simple line parser for the YAML subset we need)
- Reads/writes memory markdown files
- Reads cron jobs.json, gateway_state.json, session JSON files
- Reads skill directory structure
HermesLogService
- Tails log files using file handle + periodic polling
- Parses log level from line format
ACPClient
- Spawns
hermes acpvia FoundationProcess - Writes JSON-RPC to stdin, reads from stdout
- Streams events: ToolCallStart, ToolCallProgress, AgentMessage, AgentThought
- Manages session lifecycle
HermesFileWatcher
- Uses
DispatchSource.makeFileSystemObjectSourceon key directories - Triggers refresh callbacks when Hermes writes new data
Dependencies
Zero external SPM packages:
- SQLite: System
sqlite3C library (available on macOS,import SQLite3not needed — uselibsqlite3) - JSON: Foundation
JSONDecoder/JSONSerialization - YAML: Custom lightweight parser for flat config structure
- Markdown:
AttributedString(markdown:)(built into Foundation) - File watching: GCD
DispatchSource - Subprocess: Foundation
Process+Pipe
Sandbox
Disabled. This app reads directly from ~/.hermes/ which is outside any app sandbox container. The ENABLE_APP_SANDBOX build setting is set to NO.
Concurrency
- Swift 6 strict concurrency with
@MainActordefault isolation - Services use
nonisolatedmethods with async/await for I/O @ObservableViewModels on MainActor, call into nonisolated services- ACP client runs its read loop on a background task
Data Flow
~/.hermes/state.db ──→ HermesDataService ──→ ViewModels ──→ Views
~/.hermes/config.yaml ──→ HermesFileService ──→ ViewModels ──→ Views
~/.hermes/memories/ ──→ HermesFileService ──→ ViewModels ──→ Views
~/.hermes/logs/ ──→ HermesLogService ──→ ViewModels ──→ Views
hermes acp (subprocess) ──→ ACPClient ──→ ChatViewModel ──→ ChatView
HermesFileWatcher ──→ triggers refresh on all services