Adds Sparkle 2 auto-updates and a local release script that produces
signed, notarized, stapled builds for GitHub distribution. App Store
submission was rejected because Scarf spawns the user-installed hermes
binary and reads ~/.hermes/ directly — both forbidden by App Sandbox —
so we commit to the GitHub-release path properly.
- Sparkle SPM dep wired into the app target (link-only; hardened-runtime
entitlement disable-library-validation lets Sparkle load at runtime).
- Tracked Info.plist with SUFeedURL, SUPublicEDKey, and daily check
interval; replaces the auto-generated plist so Sparkle keys live in
version control rather than pbxproj INFOPLIST_KEY_* noise.
- UpdaterService wraps SPUStandardUpdaterController and is injected via
.environment(). Menu bar, standard app menu (CommandGroup after
.appInfo), and a new Updates section in Settings → General each call
updater.checkForUpdates().
- scripts/release.sh runs the full pipeline: version bump → universal
archive → Developer ID export → notarytool submit (keychain profile
scarf-notary) → staple → appcast EdDSA sign → gh-pages push → gh
release → tag. scripts/ExportOptions.plist pins manual Developer ID
signing for team 3Q6X2L86C4.
- README: removes the right-click-Open workaround (notarized builds
don't need it), notes Sparkle, adds a Releases section describing
the pipeline and signing prerequisites.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Reorganize the Features section to match the app's sidebar (Monitor, Interact,
Configure, Manage, Project Dashboards, System) so readers find features the
same way they find them in the app. Add a "What's New in 1.6" callout with
links to the release notes.
Binaries: ARM64 (15 MB) and Universal (19 MB). Both signed with the Apple
Development identity (Team 3Q6X2L86C4). Universal contains both arm64 and
x86_64 slices verified with lipo.
Major expansion of Scarf's Hermes platform coverage. Settings is now a 10-tab
layout exposing ~60 previously hidden config fields. A new "Configure" sidebar
section groups per-platform setup, personality management, quick commands,
credential pools, plugins, webhooks, and profile switching.
## Highlights
- **Platforms feature** — Native GUI setup for all 13 messaging platforms
(Telegram, Discord, Slack, WhatsApp, Signal, Email, Matrix, Mattermost,
Feishu, iMessage, Home Assistant, Webhook, CLI). Per-platform forms write
credentials to ~/.hermes/.env and behavior toggles to ~/.hermes/config.yaml.
WhatsApp and Signal use an inline SwiftTerm terminal for QR/link pairing.
- **Credential Pools** — Provider-aware add/remove with proper type handling.
OAuth flow uses Process + pipes to extract the authorization URL, open the
browser explicitly, and accept the code via a form field. Fixes the Anthropic
OAuth failure where the code had nowhere to be entered.
- **Model Picker** — Hierarchical provider -> model picker backed by
~/.hermes/models_dev_cache.json (111 providers, every major model). Used in
Settings -> General and Delegation. "Custom..." escape hatch for unlisted IDs.
- **Settings as tabs** — 10 tabs (General, Display, Agent, Terminal, Browser,
Voice, Memory, Aux Models, Security, Advanced). HermesConfig grew from 32 to
~90 fields via grouped sub-structs. All new fields round-trip through
`hermes config set`.
- **Extended existing features** — Cron (create/edit/pause/resume/run-now/
delete), Skills (Browse Hub + Updates tabs), Health (run `hermes dump` and
`hermes debug share` with confirmation dialog), Sessions (rename/delete/
export/export-all).
## Bug fixes
- Tools platform picker showed only CLI (was reading a nonexistent
`platform_toolsets:` YAML section). Now enumerates KnownPlatforms.all with
live connectivity dots from gateway_state.json.
- Credentials add with --api-key was triggering OAuth for providers like
Anthropic because --type was missing. Now always passes --type api-key.
- Remove-by-index used 0-based indexing; hermes CLI expects 1-based. Fixed.
- Various CLI parser fragility issues (plugins, profiles, skills hub, webhooks)
replaced with structured file reads or proper box-drawn table parsers.
## New core services
- HermesEnvService — reads/writes ~/.hermes/.env atomically, preserves
comments, commented-out keys get enabled in-place on save, values with
spaces/specials get quoted, unset commented out (non-destructive).
- ModelCatalogService — decodes the models.dev cache into typed providers and
models with context/cost/release-date metadata.
- OAuthFlowController — manages the OAuth Process subprocess: extracts the
auth URL via regex, opens the browser, pipes the code back via stdin,
detects success/failure markers in output.
## New sidebar structure
Monitor / Projects / Interact / **Configure (new)** / Manage
The Configure section gathers the setup-style features that used to require
the CLI: Platforms, Personalities, Quick Commands, Credential Pools, Plugins,
Webhooks, Profiles.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two related bugs surfaced when testing MCP servers that spawn npx, node,
python, etc. from Homebrew/nvm/asdf/mise installs.
1. MCP test reported success even when the connection failed.
`hermes mcp test <server>` exits 0 even when the inner connection
fails — it prints the error to stdout instead. Scarf trusted the
exit code and rendered a green checkmark while the output said
"✗ Connection failed: [Errno 2] No such file or directory: 'npx'".
Fix: also scan output for ✗, "Connection failed", "No such file or
directory", and "Error:" markers.
2. .app launches start with a minimal PATH that excludes Homebrew.
When Scarf is launched from Finder/Dock, ProcessInfo's PATH is
`/usr/bin:/bin:/usr/sbin:/sbin` — no /opt/homebrew/bin, no
/usr/local/bin, no nvm/asdf/mise shims. Hermes inherits this and
can't find npx/node/python when spawning MCP server subprocesses.
Fix: query the user's login shell PATH once via `/bin/zsh -lc 'echo
$PATH'`, cache it on HermesFileService, and inject it into both
`runHermesCLI` and the ACP subprocess. Falls back to a sane default
covering both Apple Silicon and Intel Homebrew if zsh query fails.
Bumps version to 1.5.8 (build 10). Includes signed Universal + ARM64
binaries.
Fixes a bug where adding a second MCP server caused the first to disappear
from the list view, and any args containing YAML reserved characters (e.g.
"@modelcontextprotocol/server-fetch") corrupted the config file.
Three root causes in HermesFileService MCP YAML patching:
1. extractMCPBlock extended through trailing comments to EOF when
mcp_servers was the last top-level key in config.yaml. Trailing
comments became part of the "block", so subsequent inserts landed
at end-of-file rather than inside the entry.
2. patchMCPServerField's entry boundary similarly absorbed trailing
blanks/comments, making the entry "own" everything until the next
sibling — or until EOF for the last entry.
3. yamlScalar did not quote values starting with YAML reserved
indicators (@ * & ? | > ! % , [ ] { } < ` ' "). Args like
"@modelcontextprotocol/server-fetch" were written bare, producing
invalid YAML that broke subsequent reads/writes.
Fix: trim trailing blanks/comments off both the block and the entry
in the locator/extractor; quote any scalar starting with a reserved
first character.
Bumps version to 1.5.7 (build 9). Includes signed Universal + ARM64
binaries.
Note: users with an already-corrupted ~/.hermes/config.yaml from the
1.5.6 bug should manually clean up their mcp_servers block (delete the
orphan args at end of file) before upgrading. New writes will be clean.
The first 1.5.6 zips contained `linker-signed` bundles with no Sealed
Resources, plus a stray nested scarf.app from a case-insensitive cp.
macOS Gatekeeper rejected the ARM64 download as "damaged"; the
Universal one ran only because the user had already trusted it.
Now both bundles are properly ad-hoc-signed (`Sealed Resources
version=2`) with the hardened runtime preserved. Sizes dropped
significantly (Universal 33MB→16MB, ARM64 27MB→13MB) because the
nested junk is gone.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Includes the MCP Servers management UI shipped in 219bca2:
- Add via curated presets (GitHub, Linear, Notion, Sentry, Stripe, …)
or fully custom (stdio command + args, or HTTP URL with bearer auth)
- Per-server detail view: enable/disable, env + headers editor,
tool include/exclude filters, resources/prompts toggles, request
and connect timeouts, OAuth token detection + clearing
- One-click "Test Connection" runs `hermes mcp test` and surfaces
the discovered tool list
- Gateway-restart banner after config changes that need a reload
README updated with the MCP Servers section, the new MCPServers/
feature module entry, and the `hermes mcp` + `mcp-tokens/` entries
in the Data Sources table.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ship Hermes v0.9.0 compatibility plus new features (log component
filter, session pill, Fast Mode, Backup/Restore, iMessage, /compress,
Discord threads). README lists both universal and ARM64 downloads.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Log parser: session-ID tag in v0.9.0 log format is now an optional
capture group; session pill renders inline and tap-filters the view.
- Logs: component filter (Gateway/Agent/Tools/CLI/Cron) and bounded
logger column with middle truncation.
- Gateway stop: uses `hermes gateway stop` CLI (v0.9.0's launchctl
bootout fix) with SIGTERM as fallback.
- HermesConfig: new keys for Fast Mode (service_tier), gateway notify
interval, force IPv4, context engine, interim assistant messages,
and Honcho eager init (camelCase per PR #6995).
- Settings: new Performance, Network, Advanced, and Backup & Restore
sections that call `hermes backup` / `hermes import` off the main
actor; robust zip-path extraction via regex.
- Platforms: iMessage (BlueBubbles) added to KnownPlatforms and
icon map.
- Cron: Discord thread delivery (`discord:chat:thread`) renders as
"Discord thread X in Y".
- Chat: `/compress <focus>` button appears when ACP advertises the
command; optional focus sheet sends through existing prompt path.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add cumulative token tracking from ACP prompt results with fallback
display when DB has no data yet. Improve scroll-to-bottom reliability
with an external trigger for "Return to Active Session" and onAppear
auto-scroll. Show per-session cost in the dashboard session list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Synchronous Process.run()/waitUntilExit() calls on the main thread blocked
SwiftUI's render loop, causing toggle controls to appear as solid blue
rectangles instead of proper switches. All hermes subprocess and file I/O
calls are now async via Task.detached, toggle uses optimistic state update
for immediate visual feedback, and pipe file handles are properly closed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sessions were silently dying and losing chat history because:
- Pipe write errors (EPIPE) were completely undetected — broken pipe
writes via Task.detached { handle.write() } failed silently, leaving
the app unaware the subprocess had crashed
- Reconnection fell back to newSession() when loadSession() failed,
creating a blank session and permanently losing all conversation context
- No message reconciliation after reconnect — DB-persisted messages
were never re-fetched, so the UI stayed stale/incomplete
- Keepalive sent bare "\n" which caused json.loads("") parse errors
in the ACP library every 30 seconds, destabilizing the connection
- TERM=xterm-256color was set on a pipe-based subprocess, risking
terminal escape sequence pollution in the JSON-RPC stream
Fixes:
- Replace FileHandle.write() with POSIX Darwin.write() + SIGPIPE
suppression for immediate broken-pipe detection at all write sites
- Send valid JSON-RPC notification {"jsonrpc":"2.0","method":"$/ping"}
as keepalive instead of bare newlines
- Never fall back to newSession() during reconnection — try
resumeSession then loadSession, fail visibly if both fail
- Add reconcileWithDB() to merge DB-persisted messages with local
state after successful reconnection
- Finalize streaming messages immediately on disconnect so partial
content is preserved before reconnection begins
- Use SIGINT instead of SIGTERM for graceful Python subprocess shutdown
- Remove TERM env var from ACP subprocess environment
- Consolidate disconnect cleanup into single idempotent method
- Add isHandlingDisconnect guard against double-handling
- Increase reconnect attempts from 3 to 5 with capped backoff
- Add "Reconnect" button to toolbar error state
Also: bump version to 1.5.1, set deployment target to macOS 14.6
(Sonoma), and update README with rich chat/ACP features, process
controls, skill editing, and corrected system requirements.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement a rich chat interface powered by the Hermes ACP (Agent
Communication Protocol) over JSON-RPC stdio pipes, with comprehensive
connection stability:
- ACPClient actor: manages hermes acp subprocess lifecycle, JSON-RPC
transport, event streaming via AsyncStream, and session management
- ACPMessages: full event parsing for message chunks, thought chunks,
tool calls, permission requests, and prompt completion
- RichChatViewModel: streaming message display with live updates,
tool result rendering, and message grouping
- ChatViewModel: ACP session orchestration, auto-start on first
message, and terminal mode fallback
Connection stability fixes:
- Non-blocking pipe writes via Task.detached to prevent actor deadlock
- Read loop cleanup (handleReadLoopEnded) finishes event stream and
fails pending requests on EOF instead of hanging silently
- 30s request timeouts on control messages via watchdog Task pattern
- Keepalive: writes \n to stdin every 30s to detect dead processes
via EPIPE before the next user action
- Health monitor: polls process.isRunning every 5s as belt-and-suspenders
- Auto-reconnect: retries up to 3 times with exponential backoff
(1s/2s/4s), restores session, only shows error after all retries fail
- connectionLost event displays system message in chat on failure
- Proper stderr pipe management: stored task reference, closed in stop()
- Idempotent cleanup across handleReadLoopEnded, handleTermination,
and handleConnectionDied via actor serialization and nil guards
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline AttributedString(markdown:) in RichMessageBubble with
the shared MarkdownContentView for consistent styled rendering of
headers, lists, blockquotes, and inline formatting in chat messages.
Code blocks continue to use CodeBlockView with its copy button.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add rich chat interface with iMessage-style message bubbles, terminal
toggle, session info bar, code block rendering with copy button, and
tool call cards. Supports both terminal and rich chat display modes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add tool result display to the Activity detail pane. When selecting a
tool call, the inspector now shows Arguments → Output → Assistant
Message, giving full visibility into what was requested, what came back,
and how the assistant interpreted it.
- Add fetchToolResult(callId:) query to HermesDataService
- Fetch tool result on entry selection in ActivityViewModel
- Display output in styled monospaced box in detail pane
- Render assistant message with MarkdownContentView
Closes#12
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The config.yaml uses YAML empty string literal (provider: '') which the
parser reads as the literal string '' rather than an empty string. Strip
surrounding quotes before checking so '' and "" are treated as empty.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a custom MarkdownContentView that renders markdown with visual
styling — large headers, styled code blocks with language labels,
bullet and numbered lists, blockquotes with colored borders, and
horizontal rules. YAML frontmatter in skill files is hidden.
Markdown rendering added to:
- Memory view (MEMORY.md, USER.md) with live preview in editor
- Skills view (.md files) with new edit/save capability
- Session messages (assistant responses)
- Dashboard text widgets
Other changes:
- Shared MarkdownRenderer utility for inline formatting
- Split-pane editors (raw markdown left, live preview right)
- saveSkillContent() in HermesFileService with path validation
- Line breaks preserved in non-markdown content (Key: Value format)
Closes#11
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add hermesPID() and stopHermes() to HermesFileService for process
signal management via SIGTERM
- Add process control bar to Health view with running status, PID
display, and Start/Stop/Restart buttons
- Add Start/Stop/Restart Hermes quick actions to menu bar
- Start launches gateway, stop sends SIGTERM, restart combines both
Closes#10
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce a new structured chat view as an alternative to the SwiftTerm
terminal. Users can switch between raw terminal and rich chat modes via a
segmented picker in the toolbar. The rich view polls state.db for messages
and renders them as conversation bubbles with markdown, code blocks,
expandable tool call cards, reasoning sections, and a live session info bar
showing tokens, cost, and model. The terminal process stays alive in both
modes — in rich mode it runs hidden while user input from the text field is
piped to its stdin.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Auto-detect v0.7.0 database schema with backward compat for older DBs
- Surface reasoning tokens, actual cost, and billing provider from sessions
- Display model reasoning/thinking content in session message bubbles
- Add cost tracking to Dashboard, Insights, and session detail views
- Fix FTS5 search crash on dotted terms (e.g., "config.yaml", "v0.7.0")
- Add missing platforms: Home Assistant, Webhook, Matrix
- Consolidate platform icon mapping into shared KnownPlatforms.icon(for:)
- Map execute_code tool to ToolKind.execute
- Add Settings UI for reasoning effort, approval mode, show cost
- Show memory provider warning when external provider (Honcho) is active
- Replace fragile manual HermesSession init with withTitle() helper
- De-duplicate formatTokens utility function
- Bump version to 1.4.0
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
All builds were reporting version 1.0 because the Xcode project version
was never updated from its default. Fixes#5, fixes#7.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dashboards with a webview widget now show a tab bar: Dashboard tab
renders all normal widgets, Site tab displays the web content
full-canvas with even margins. Cleaner UX than the split layout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New widget type that renders any URL (local dev servers, HTML reports)
directly in the dashboard via WKWebView. Sections with webviews
automatically split layout: grid widgets left, webview right.
Configurable height, non-persistent data store, navigation error logging.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Replace try? with do/catch and [Scarf] error logging in all service-layer
JSON decoding, file writes, and directory creation
- Extract sqliteTransient constant replacing raw unsafeBitCast(-1, ...) pattern
- Add QueryDefaults and FileSizeUnit enums for all magic numbers
- Guard HOME env var with NSHomeDirectory() fallback instead of force-unwrap
- Add path traversal validation to loadSkillContent()
- Add SessionStats.empty and use it across all initialization sites
- Replace KnownPlatforms array indexing with named .cli constant
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces a new Projects section that renders custom dashboards from
JSON files in project directories. Supports 7 widget types (stat,
progress, text, table, chart, list) with live file-watching refresh.
Includes project registry, SwiftUI Charts integration, schema docs,
and comprehensive README documentation.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Universal binary (arm64 + x86_64) available on Releases page.
Updated Building section to Install with download + build options.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Settings view now has editable form controls organized by section:
Model: editable model name field, provider dropdown picker
Display: personality picker (parsed from config), streaming/reasoning/verbose toggles
Terminal: backend picker (local/docker/singularity/modal/daytona/ssh), max turns stepper
Voice: auto TTS toggle, silence threshold stepper
Memory: enabled toggle, char limit steppers, nudge interval stepper
All changes write via `hermes config set key value` CLI with save
confirmation feedback. Open in Editor button launches the raw YAML
in the default text editor. Paths and raw config sections retained.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hermes auto-enables TTS when voice mode turns on (auto_tts config).
Our ttsEnabled started as false, so the UI showed off when TTS was
actually on. Now reads auto_tts from config.yaml when voice enables
and sets the initial state to match.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hermes voice mode needs mic access when running as a Scarf subprocess.
- Added NSMicrophoneUsageDescription to Info.plist keys
- Created entitlements file with com.apple.security.device.audio-input
- Applied to both Debug and Release configurations
macOS will prompt for mic permission on first push-to-talk use.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Voice toolbar now shows three controls when voice is enabled:
- Mic toggle (voice on/off)
- TTS toggle (speaker icon, sends /voice tts)
- Push to Talk (waveform, sends Ctrl+B)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents tested versions and the interfaces Scarf depends on
(SQLite schema v6, CLI output parsing).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replaced the long flat list with a cleaner layout:
- Compact header bar: version, update banner, pass/warn/error counts
- Status/Diagnostics tab switcher (segmented control)
- 2-column card grid: each section is a uniform card showing icon,
title, and colored status dot counts (green/orange/red)
- Cards have a colored border accent based on worst status
- Click to expand: reveals individual check rows inline
- Only one section expanded at a time for clean scanning
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New Health section in the Manage group combining hermes status and
hermes doctor output:
- Version header with update available banner (e.g. "47 commits behind")
- Summary badges: passing/warning/issue counts
- Status sections: environment, API keys, auth providers, terminal
backend, messaging platforms, gateway service, scheduled jobs
- Diagnostics sections: Python environment, required/optional packages,
config files, directory structure, external tools, API connectivity,
submodules, tool availability, Skills Hub, Honcho memory
- Each check shows green/orange/red icon with label and detail
- Refresh button to re-run both commands
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New Gateway section in the Manage group:
- Service controls: Start/Stop/Restart buttons calling hermes gateway CLI
- Status display: state (running/stopped), PID, loaded indicator, stale
service warning, exit reason, last update timestamp
- Platform cards: each connected messaging platform with connection state
(reads from gateway_state.json)
- Pairing management: approved users list with revoke button, pending
pairing codes with approve button
- Auto-refreshes via HermesFileWatcher when gateway state changes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Added Tools Manager to features list
- Updated Sessions Browser with rename/delete/export
- Updated Skills Browser with file switcher
- Updated Dashboard with live refresh
- Updated Log Viewer with text search
- Added hermes tools and hermes sessions to data sources table
- Revised How It Works section to cover management actions
- Updated architecture tree with Tools feature
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After rename:
- Update selectedSession so detail header refreshes immediately
- Update sessionPreviews so previewFor() returns the new title
- Dashboard now observes HermesFileWatcher and reloads on DB changes
- Chat session menu reloads via file watcher (persists across nav)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>