Claude
Skills
Sign in
Back

hermes-desktop-macos

Included with Lifetime
$97 forever

```markdown

Writing & Docs

What this skill does

```markdown
---
name: hermes-desktop-macos
description: Native macOS SSH workspace for Hermes Agent — sessions, terminal, files, usage, and skills over direct SSH
triggers:
  - set up Hermes Desktop on my Mac
  - connect Hermes Desktop to my SSH host
  - configure Hermes Desktop SSH connection
  - browse Hermes sessions in the desktop app
  - edit Hermes memory files from Mac
  - build Hermes Desktop from source
  - troubleshoot Hermes Desktop SSH connection
  - add a new connection profile in Hermes Desktop
---

# Hermes Desktop macOS

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

Hermes Desktop is a native macOS app (Swift/SwiftUI, macOS 14+) that gives Hermes Agent users a first-class Mac workspace over direct SSH. It reads sessions from `~/.hermes/state.db`, edits canonical memory files (`USER.md`, `MEMORY.md`, `SOUL.md`), browses skills, shows token usage dashboards, and embeds a real SSH terminal — all without a gateway API, local file mirrors, or a remote helper service.

---

## Installation

### Option A: Download the release binary

```bash
# 1. Download from GitHub Releases
# 2. Unzip
unzip HermesDesktop.app.zip

# 3. Move to Applications
mv HermesDesktop.app /Applications/

# 4. Verify the bundle signature
codesign --verify --deep --strict /Applications/HermesDesktop.app

# 5. Check SHA-256 matches the release asset
shasum -a 256 HermesDesktop.app.zip
```

> **First-launch gatekeeper bypass** (app is not yet notarized):
> Right-click → Open, or go to **System Settings → Privacy & Security → Open Anyway**.

### Option B: Build from source

```bash
git clone https://github.com/dodo-reach/hermes-desktop
cd hermes-desktop

# Requires Xcode 15+ and macOS 14 SDK
./scripts/build-macos-app.sh

# The built app lands in the repo root or DerivedData — check script output
open HermesDesktop.app
```

---

## Requirements

| Requirement | Detail |
|---|---|
| macOS | 14 Sonoma or newer |
| SSH auth | Key-based, no interactive prompts from this Mac |
| Host key | Already accepted once in Terminal |
| Remote | `python3` available in the SSH environment |
| Hermes data | `~/.hermes/` present on the remote host |

Quick sanity check — if this works without prompts, the app will work:

```bash
ssh your-host echo ok
# → ok
```

---

## Connecting to a Hermes Host

### Using an SSH alias (recommended)

Add an entry to `~/.ssh/config` on your Mac:

```sshconfig
Host hermes-pi
  HostName 192.168.1.42
  User pi
  IdentityFile ~/.ssh/id_ed25519

Host hermes-vps
  HostName vps.example.com
  User deploy
  Port 2222
  IdentityFile ~/.ssh/id_ed25519

Host hermes-local
  HostName localhost
  User alex
```

In the app: **Connections → New Profile → SSH alias → `hermes-pi`**. Leave Host/User/Port blank.

### Using explicit host details

In the app:
- **Host or IP**: `vps.example.com`
- **User**: `deploy`
- **Port**: `2222`

### Testing the connection

Click **Test** before **Use Host**. The preflight check verifies:
1. SSH target is reachable
2. Authentication completes without prompts
3. `python3` exists in the remote SSH environment

---

## App Sections

### Overview

Displays on first connect:
- Remote `$HOME` path
- Hermes root (`~/.hermes`)
- Which canonical files are present
- Session source (`state.db` or `.jsonl` fallback)

### Files — editing canonical Hermes files

The app provides conflict-aware editing for three files:

| File | Purpose |
|---|---|
| `~/.hermes/memories/USER.md` | User context fed to the agent |
| `~/.hermes/memories/MEMORY.md` | Agent working memory |
| `~/.hermes/SOUL.md` | Agent persona/soul definition |

**Conflict protection flow:**
1. App records the remote file hash at load time
2. Before saving, it re-fetches the remote hash
3. If the file changed on the host since load → save is blocked, edits preserved
4. Prompt: **Reload from Remote** → review diff → re-apply edits → save

### Sessions

- Source: `~/.hermes/state.db` (SQLite, canonical)
- Fallback: `~/.hermes/sessions/*.jsonl` if SQLite unavailable
- Features: search, metadata display, remote deletion, refresh-on-entry

### Usage

Aggregate token dashboard sourced from `~/.hermes/state.db`:
- Total input / output tokens
- Top sessions by token count
- Per-model breakdowns
- Recent session trend

### Skills

Recursive browser for `~/.hermes/skills/**/SKILL.md`:
- Discovers all nested skill files
- Quick filter/search in the list
- Full markdown preview

### Terminal

Embedded SSH terminal with tabs. Uses `/usr/bin/ssh` — the same binary as Terminal.app. Connects to the same host as the active profile.

---

## SSH Configuration Patterns

### Key-based auth setup (if not already done)

```bash
# Generate a key on your Mac if needed
ssh-keygen -t ed25519 -C "hermes-desktop" -f ~/.ssh/id_hermes

# Copy public key to the remote host
ssh-copy-id -i ~/.ssh/id_hermes.pub user@host

# Or manually:
cat ~/.ssh/id_hermes.pub | ssh user@host "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys"

# Test — must return without any prompt
ssh -i ~/.ssh/id_hermes user@host echo ok
```

### SSH config for common topologies

```sshconfig
# Tailscale (MagicDNS hostname)
Host hermes-tailscale
  HostName my-machine.tail1234.ts.net
  User alex
  IdentityFile ~/.ssh/id_ed25519

# Jump host / bastion
Host hermes-behind-bastion
  HostName 10.0.0.5
  User ubuntu
  ProxyJump bastion.example.com
  IdentityFile ~/.ssh/id_ed25519

# Same Mac via localhost
Host hermes-local
  HostName localhost
  User alex
  IdentityFile ~/.ssh/id_ed25519
  StrictHostKeyChecking no
```

> **Note:** Hermes Desktop uses standard `/usr/bin/ssh`. If your setup requires the separate `tailscale ssh` command, use a ProxyCommand instead:

```sshconfig
Host hermes-tailscale-cmd
  HostName my-machine
  User alex
  ProxyCommand tailscale ssh --nc %h %p
```

---

## Building from Source — Key Files

```
hermes-desktop/
├── scripts/
│   └── build-macos-app.sh          # Universal (arm64 + x86_64) build script
├── HermesDesktop/
│   ├── App/
│   │   └── HermesDesktopApp.swift  # SwiftUI @main entry point
│   ├── Views/
│   │   ├── SessionsView.swift
│   │   ├── FilesView.swift
│   │   ├── UsageView.swift
│   │   ├── SkillsView.swift
│   │   └── TerminalView.swift
│   ├── Models/
│   │   └── ConnectionProfile.swift
│   └── SSH/
│       └── SSHClient.swift         # Wraps /usr/bin/ssh process calls
└── HermesDesktop.xcodeproj
```

### Build script usage

```bash
# Full universal build
./scripts/build-macos-app.sh

# Verify the result is a universal binary
lipo -info HermesDesktop.app/Contents/MacOS/HermesDesktop
# → Architectures in the fat file: arm64 x86_64
```

### Xcode build (development)

```bash
# Debug build for current arch
xcodebuild -scheme HermesDesktop -configuration Debug build

# Run tests
xcodebuild -scheme HermesDesktop test
```

---

## Swift Patterns Used in the Codebase

### Running SSH commands (non-interactive)

```swift
import Foundation

func runSSHCommand(
    alias: String,
    command: String,
    completion: @escaping (Result<String, Error>) -> Void
) {
    let process = Process()
    process.executableURL = URL(fileURLWithPath: "/usr/bin/ssh")
    process.arguments = [
        "-o", "BatchMode=yes",          // never prompt interactively
        "-o", "ConnectTimeout=10",
        alias,
        command
    ]

    let pipe = Pipe()
    process.standardOutput = pipe
    process.standardError = pipe

    process.terminationHandler = { proc in
        let data = pipe.fileHandleForReading.readDataToEndOfFile()
        let output = String(data: data, encoding: .utf8) ?? ""

        if proc.terminationStatus == 0 {
            completion(.success(output))
        } else {
            completion(.failure(SSHError.nonZeroExit(output)))
        }
    }

    do {
        try process.run()
    } catch {
        completion(.failure(error))
    }
}

// Usage
runSSHCommand(alias: "hermes-pi", command: "python3 --version") { result in
    switch result {
    case .success(let version): print("python3 ok:", ver

Related in Writing & Docs