docs(wiki): v2.5 — split ScarfGo into user guide + roadmap, add Platform-Differences

Alan Wizemann
2026-04-25 08:02:27 +02:00
parent 042317f1a2
commit d4eb83de73
7 changed files with 324 additions and 110 deletions
+6 -4
@@ -2,18 +2,20 @@
A native macOS companion app for the [Hermes AI agent](https://github.com/hermes-ai/hermes-agent). Full visibility into what Hermes is doing, when, and what it creates — across one local install or many remote ones.
**Latest release:** [v2.2.1](https://github.com/awizemann/scarf/releases/tag/v2.2.1) (v2.3.0 queued — bump this line after `scripts/release.sh` completes)
**Latest release:** [v2.5.0](https://github.com/awizemann/scarf/releases/tag/v2.5.0) — ScarfGo iOS companion + Mac Sessions parity. Bump this line after `scripts/release.sh` completes the v2.5 publish in Phase G.
**Latest mobile:** ScarfGo public TestFlight — see [ScarfGo](ScarfGo) for the invite link.
**Targets Hermes:** v0.10.0 (v2026.4.16) — Tool Gateway features require this
**Available in:** English, Simplified Chinese (zh-Hans), German (de), French (fr), Spanish (es), Japanese (ja), Brazilian Portuguese (pt-BR). See [Localization](Localization).
## Quick links
- [Installation](Installation) — download, first launch, system requirements
- [Installation](Installation) — download, first launch, system requirements (Mac)
- **[ScarfGo](ScarfGo)** — the iPhone companion (public TestFlight from v2.5)
- [Platform Differences](Platform-Differences) — Mac vs iOS feature matrix
- [First Run](First-Run) — what Scarf expects in `~/.hermes/`
- [Project Templates](Project-Templates) — `.scarftemplate` bundles, install / export / author
- [Architecture Overview](Architecture-Overview) — MVVM-F, services, transport
- [Servers & Remote](Servers-and-Remote) — adding remote Hermes hosts over SSH
- [ScarfGo](ScarfGo) — the iPhone companion (on-the-go agent management)
- [Localization](Localization) — supported languages + how to contribute a new one
- [Release Notes Index](Release-Notes-Index) — every version's notes
- [Wiki Maintenance](Wiki-Maintenance) — how this wiki is edited and kept in sync
@@ -34,4 +36,4 @@ Scarf 2.0 is a multi-window app — one window per Hermes server, local or remot
Open-source (MIT), 160+ stars, actively maintained. See [Roadmap](Roadmap) for what's coming.
---
_Last updated: 2026-04-24 — Scarf v2.3.0 queued_
_Last updated: 2026-04-25 — Scarf v2.5.0 queued + ScarfGo public TestFlight_
+81
@@ -0,0 +1,81 @@
# Platform differences — Scarf (Mac) vs ScarfGo (iOS)
Both clients talk to the same Hermes host with the same paths and the same data. They differ where iOS's constraints make a Mac feature impractical or unnecessary. This page is the canonical "what's missing on iOS, and why" reference.
## Feature matrix
| Feature | Mac (Scarf) | iOS (ScarfGo) | Why the gap |
|---|:---:|:---:|---|
| **Dashboard** | Full (stats + recent sessions + Surfaces + Connected info) | Stripped (stats + recent + Sessions sub-tab with project filter) | iOS density. Surfaces (gateway state / pgrep / config.yaml health) read Mac-only filesystems. |
| **Chat (ACP)** | Yes | Yes | — |
| **Project-scoped chat** | Yes | Yes | Shared `ProjectContextBlock` / `SessionAttributionService` in ScarfCore. |
| **Session resume** | Yes | Yes | — |
| **Sessions list — global** | Yes, with project filter + badges (v2.5) | Yes, with project filter + badges | — |
| **Sessions list — per-project** | Yes (Projects sidebar) | No (global Sessions tab covers it) | Different navigation: Mac has a Projects sidebar, iOS has tabs + per-tab filter. Same data. |
| **Memory editor** | Yes | Yes | — |
| **Cron list (read)** | Yes | Yes | — |
| **Cron editor (write)** | Yes | No | Mac builds a richer editor sheet; iOS read-only in v1. Coming. |
| **Skills tree** | Yes | Yes (read-only) | iOS won't grow a skill editor — too cramped. |
| **Settings (read)** | Yes (full YAML) | Yes (full YAML) | — |
| **Settings (write)** | Yes (full YAML editor) | **No** | iOS deferred. The plan was a curated `hermes config set <key> <value>` shell-out for 7 keys; that landed in the codebase but isn't surfaced in v1 because changing settings remotely without local validation is too easy to break. Mac stays the canonical editor. |
| **Templates — install** | Yes | No | Templates are a content-creation surface; iOS won't get a UI for it in v1. Use Mac. |
| **Templates — uninstall** | Yes | No | Same. |
| **Templates — author / export** | Yes | No | Same. |
| **Messaging Gateway (Discord, etc.)** | Yes (status + config) | No | Mac-only filesystem checks (`gateway_state.json`, pgrep). Could be lifted into ScarfCore with effort. |
| **Health view** | Yes (full) | No | Mac reads `~/.hermes/logs/`, gateway state, config sanity, the `hermes dashboard` web link. Could be ported in chunks. |
| **Logs viewer** | Yes (errors + gateway tails) | No | Could ride on `HermesLogService.streamLines` (already remote-capable). Pending UX design for iOS. |
| **Tool Gateway (Nous Portal)** | Yes (provider picker + Auxiliary tab + Health surface) | Provider exists in catalog | iOS Settings is read-only, so tool-gateway routing toggles aren't editable. Use Mac. |
| **Insights / activity charts** | Yes | No | Charts are richer on bigger screens; deferred. |
| **Terminal (SwiftTerm)** | Mac doesn't have it either | No | Hermes shell mode is CLI-only; neither client wraps it. |
| **Localization** | 7 languages (de, es, fr, it, ja, pt-BR, zh-Hans) | English only | iOS strings are extracted but no translations contributed in v1. |
| **Multi-server** | Yes (one window per server) | Yes (sidebar-adaptable Tab root) | — |
| **Push notifications** | n/a (Mac uses local notifications + the menu bar) | Skeleton present, gated `apnsEnabled = false` | Push sender doesn't exist on Hermes side yet. Capability disabled in target. Flips on simultaneously when both lights are green. |
| **Sparkle auto-update** | Yes | n/a | iOS uses TestFlight / App Store. |
| **iPad support** | n/a | Wired (`.tabViewStyle(.sidebarAdaptable)`) but not smoke-tested | iPad target flag is off; layout probably free, but verify before flipping. |
## Why the iOS surface is intentionally smaller
ScarfGo is a **monitor + steer** client. It's optimised for:
- Running a chat with the agent.
- Looking at sessions, memory, scheduled jobs.
- Resuming work you started somewhere else.
- Approving or denying pending permissions (when push lands).
It's intentionally NOT optimised for:
- Authoring templates or skills.
- Editing settings to deeply tune behaviour.
- Operations work — log triage, gateway debugging, deployment.
Those belong on Mac, where you have a keyboard, real screen, and the full Hermes filesystem layout. Trying to cram them into an iPhone produces a worse Mac and a worse iPhone.
## Things that won't ever come to iOS
- **`hermes` daemon** — Hermes is Python; iOS doesn't sandbox Python well.
- **Local Hermes filesystem** — `~/.hermes/` is on a server, always.
- **Process management** — `pgrep`, `kill`, `launchctl` flows stay Mac-only.
## Things that should come to iOS but haven't yet
These are gaps without a fundamental reason; they just need engineering time:
- **Cron editor.** Add / remove jobs from the phone. The data model is shared; only the editor sheet is missing.
- **Scoped Settings editor.** A whitelisted "Quick edits" sheet for the 7 keys most users actually change (`model.default`, `model.provider`, `agent.approval_mode`, `agent.max_turns`, `display.show_cost`, `display.show_reasoning`, `display.streaming`).
- **Health summary card.** A reduced version of the Mac Health view — just enough to answer "is the gateway running, is the DB reachable, is the agent crashy?"
- **Localization.** Translate the strings the Mac app already has; reuse the `.strings` files.
- **iPad layout pass.** Probably one afternoon's verification.
## Things that are intentionally **not** mirrored
| Mac feature | Why not on iOS |
|---|---|
| Project sidebar | Replaced by Sessions filter — same outcome, different navigation idiom. |
| Window-per-server | Tabs replace it. iPhone has one focus at a time anyway. |
| Sparkle | TestFlight / App Store does the same job. |
| Menu bar / Dock | iOS has neither. |
| AppleScript / URL scheme `scarf://` | iOS apps don't get the same routing surface; can be added if needed. |
---
_Last updated: 2026-04-25 — v2.5._
+7 -1
@@ -4,6 +4,8 @@ Every Scarf release in chronological order. The notes themselves live in `releas
| Version | Date | GitHub release | Notes file |
|---|---|---|---|
| **v2.5.0** | 2026-04-25 | [v2.5.0](https://github.com/awizemann/scarf/releases/tag/v2.5.0) | [`releases/v2.5.0/RELEASE_NOTES.md`](https://github.com/awizemann/scarf/blob/main/releases/v2.5.0/RELEASE_NOTES.md) |
| **v2.3.0** | 2026-04-24 | [v2.3.0](https://github.com/awizemann/scarf/releases/tag/v2.3.0) | [`releases/v2.3.0/RELEASE_NOTES.md`](https://github.com/awizemann/scarf/blob/main/releases/v2.3.0/RELEASE_NOTES.md) |
| **v2.2.1** | 2026-04-23 | [v2.2.1](https://github.com/awizemann/scarf/releases/tag/v2.2.1) | [`releases/v2.2.1/RELEASE_NOTES.md`](https://github.com/awizemann/scarf/blob/main/releases/v2.2.1/RELEASE_NOTES.md) |
| **v2.2.0** | 2026-04-23 | [v2.2.0](https://github.com/awizemann/scarf/releases/tag/v2.2.0) | [`releases/v2.2.0/RELEASE_NOTES.md`](https://github.com/awizemann/scarf/blob/main/releases/v2.2.0/RELEASE_NOTES.md) |
| **v2.1.0** | 2026-04-21 | [v2.1.0](https://github.com/awizemann/scarf/releases/tag/v2.1.0) | [`releases/v2.1.0/RELEASE_NOTES.md`](https://github.com/awizemann/scarf/blob/main/releases/v2.1.0/RELEASE_NOTES.md) |
@@ -17,6 +19,10 @@ Every Scarf release in chronological order. The notes themselves live in `releas
## Highlights by major
**2.5****[ScarfGo](ScarfGo) iOS companion** ships in public TestFlight: native iPhone app speaking SSH (Citadel) to your Hermes hosts, with multi-server, project-scoped chat, session resume, memory editor, cron list, skills tree, and read-only settings. Mac side gets project filter + badges on the global Sessions list (parity with ScarfGo's Sessions tab) and human-readable cron schedules everywhere. Under the hood, `SessionAttributionService` / `ProjectContextBlock` / `CronScheduleFormatter` consolidate into ScarfCore so both apps consume one source of truth. See [Platform Differences](Platform-Differences) for what is and isn't shared.
**2.3** — [Projects sidebar grows up](https://github.com/awizemann/scarf/blob/main/releases/v2.3.0/RELEASE_NOTES.md): folders, rename, archive, search, ⌘1-9 keyboard jumps, per-project Sessions tab, and Scarf-managed `AGENTS.md` context block injected at chat-start so the agent always knows what project it's in. Catches up to Hermes v0.10.0's [Tool Gateway](Hermes-Version-Compatibility) — Nous Portal subscription routing, in-app sign-in, Health-tab visibility for tool routing.
**2.2** — [Project Templates](Project-Templates): shareable `.scarftemplate` bundles with typed configuration schemas, Keychain-backed secrets, a public template catalog at [awizemann.github.io/scarf/templates/](https://awizemann.github.io/scarf/templates/), `scarf://install?url=…` deep links, CI-enforced PR validator, and a Site tab for webview-bearing dashboards. Cross-agent by default via the `AGENTS.md` standard — works in Claude Code, Cursor, Codex, Aider, and every other agent that reads it. v2.2.1 adds the `awizemann/template-author` scaffolding-skill template and fixes the Configuration sheet's segmented-picker overflow on schemas with long enum labels.
**2.1** — Full UI translations for six locales on top of English (Simplified Chinese, German, French, Spanish, Japanese, Brazilian Portuguese); locale-aware currency / byte-size / compact-number formatting; chat slash-command menu with filtering and argument hints; auto-scroll polish and loading state for chat session reconnects.
@@ -38,4 +44,4 @@ When `scripts/release.sh <version>` completes a full (non-draft) release, this p
This is one of the [wiki update triggers](Wiki-Maintenance) that future Claude Code sessions will follow automatically.
---
_Last updated: 2026-04-23 — Scarf v2.2.1_
_Last updated: 2026-04-25 — Scarf v2.5.0 + ScarfGo public TestFlight_
+15 -12
@@ -2,24 +2,27 @@
What's next for Scarf. Public, opinionated, subject to change. The internal version of this lives in [`scarf/docs/ROADMAP.md`](https://github.com/awizemann/scarf/blob/main/scarf/docs/ROADMAP.md) — the public wiki version is a distillation.
## Now (2.0.x)
## Now (2.5)
- **Stability.** 2.0 was a big multi-server release. Expect 2.0.x patches focused on edge cases in remote diagnostics, snapshot recovery, ACP reconnection, and correctness.
- **Wiki bootstrap.** This wiki is new — Phase 2 is filling it out, Phase 3 is keeping it in sync (ongoing).
- **[ScarfGo](ScarfGo) public TestFlight.** First public iPhone companion build. Pulse the beta tester pool; iterate on feedback over 2.5.x patches.
- **Mac Sessions parity.** Project filter + badges shipped in 2.5 alongside the iOS work. Watch for follow-up on per-project Insights views.
- **Documentation pass.** Wiki reorganized to surface ScarfGo as a first-class section. [Platform Differences](Platform-Differences) is the new canonical reference for "what's different on iOS".
## Near-term
## Near-term (2.6 candidates)
These are the unblocked candidates for 2.1 / 2.2:
- **Test coverage.** A `MockTransport` unlocks unit-testing the service layer; protocol-oriented testing for `HermesEnvService`, `ACPClient` framing, `HermesPathSet`, `HermesConfig` decoding. See [Testing](Testing).
- **Mermaid diagrams in the wiki.** Architecture pages get a lot of value from one good diagram; deferred to keep Phase 1 focused on text.
- **Per-project FSEvents on remote.** Remote currently has one global mtime-poll loop ([HermesFileWatcher](Core-Services) line 84 has a TODO); per-project paths would reduce remote chattiness.
- **iOS cron editor.** Add / remove / toggle cron jobs from the phone. Data model is already shared; just needs the editor sheet.
- **iOS scoped Settings editor.** The codebase already has the `hermes config set` shell-out wired for 7 keys; needs UX work + the curated whitelist before exposing.
- **iOS push notifications, lit up.** Three things need to happen together: enable the Push Notifications capability in the Xcode target, ship a Hermes-side push sender, flip `apnsEnabled = true`. Skeleton + lock-screen action category are already in place.
- **iPad layout pass.** `.tabViewStyle(.sidebarAdaptable)` is wired; needs verification + a target-flag flip.
- **More MCP presets.** The curated list grows as MCP ecosystem matures.
- **Mermaid diagrams in the wiki.** Architecture pages get a lot of value from one good diagram.
- **Per-project FSEvents on remote.** Remote currently has one global mtime-poll loop ([HermesFileWatcher](Core-Services) line 84 has a TODO); per-project paths would reduce remote chattiness.
## Medium-term
- **[ScarfGo](ScarfGo) — iPhone companion.** On-the-go remote agent management. Pre-TestFlight dogfood as of 2026-04-24 — see the dedicated page for full roadmap (M7 stabilization → M8 UX density → M9 multi-server + push + project-chat → M10 TestFlight) and [Known Issues](ScarfGo#known-issues).
- **Custom commands palette.** A ⌘K-style palette for quick actions across all sidebar sections.
- **iOS localization.** Translate the strings the Mac app already has; reuse the `.strings` files. 7 languages on Mac; iOS is English-only in v1.
- **iOS Health summary card.** Reduced version of the Mac Health view — gateway / DB / agent crash status. Read-mostly, doesn't need a full editor.
- **Custom commands palette.** A ⌘K-style palette for quick actions across all sidebar sections (Mac).
- **Better Insights.** Rolling heatmaps, drill-downs from any chart, exportable summaries.
- **Voice mode polish.** Speaker selection, partial-results display, better handling of long-form dictation.
- **In-app log filtering by structured fields.** Currently text-search; a typed query (level=error AND component=gateway AND session=...) would help.
@@ -42,4 +45,4 @@ These are the unblocked candidates for 2.1 / 2.2:
Open an issue at <https://github.com/awizemann/scarf/issues> with what you want and why. Star the repo if you'd use it (signal helps prioritization).
---
_Last updated: 2026-04-20 — Scarf v2.0.1_
_Last updated: 2026-04-25 — Scarf v2.5.0 + ScarfGo public TestFlight_
+129
@@ -0,0 +1,129 @@
# ScarfGo — Roadmap & development reference
> **Looking for the user guide?** See **[ScarfGo](ScarfGo)**. This page tracks engineering milestones (M6 / M7 / M8 / M9) and the pass-1 / pass-2 smoke-test punch list — useful for contributors and history, less useful for users.
ScarfGo is the on-the-go iPhone companion to [Scarf](Home). Its scope is deliberately narrower than the Mac app: **remotely manage and interact with a running Hermes agent** from your phone. It is **not** a feature-parity port — Mac handles the full operator surface; ScarfGo handles what's valuable away from a desk.
**Current state:** **public TestFlight** as of v2.5. M6 → M7 → M8 → M9 all shipped. See [Known Issues](#known-issues) below for residual items + this page for the historical milestone narrative.
**Tech stack:**
- **Transport:** pure-Swift SSH via [Citadel](https://github.com/orlandos-nl/Citadel) 0.12.x — no OpenSSH client subprocess (iOS sandbox).
- **Shared core:** `ScarfCore` SPM package — Models / Transport / Services / ViewModels portable across macOS and iOS, unit-tested on Linux in CI.
- **iOS-only code:** `ScarfIOS` package (Citadel glue, Keychain key storage) + `Scarf iOS/` SwiftUI views.
- **Target:** iPhone, iOS 18+. iPad / macCatalyst deferred.
## What's shipped today (M6)
| Feature | Behavior |
|---|---|
| **Onboarding** | 8-step: host form → generate or import Ed25519 → show public key → you add to `~/.ssh/authorized_keys` → test probe → done. Single server v1 — multi-server in M9. |
| **Dashboard** | Session count, message count, tool-call count, token totals, last 5 sessions. Pulled from remote `~/.hermes/state.db` via `sqlite3 .backup` + SFTP download (WAL-safe snapshot). |
| **Chat** | Real-time ACP over a dedicated SSH exec channel. Markdown, tool-call cards, permission sheets, reasoning disclosure, streaming. **No** embedded terminal — rich chat only. |
| **Memory** | Read + write MEMORY.md, USER.md, SOUL.md (SOUL is in the Personalities feature on Mac; on iOS we fold it in here because you rarely want them separately on a phone). |
| **Cron** | List jobs, toggle enabled/disabled, edit schedule / prompt / skills / delivery route, add new, swipe-to-delete. Writes `~/.hermes/cron/jobs.json` atomically. |
| **Skills** | Read-only browse — categories + skill files per skill. |
| **Settings** | Read-only view of `config.yaml` grouped by section. Editing deferred — see M9. |
## Deliberately not on ScarfGo
- **Analytics features** (Activity, Logs, Health, Insights) — belong on the Mac where screen real-estate supports them.
- **Full-surface configuration** (CredentialPools, Gateway, Templates, MCP Servers, Platforms, Plugins, Profiles, Personalities, Tools, Webhooks, QuickCommands) — config flows live on the Mac. On the go you want to run and interact, not configure.
- **Terminal mode** (embedded SwiftTerm) — out of scope for a chat-first companion.
- **Local Hermes** — iOS can't spawn subprocesses (sandbox). ScarfGo is remote-only by design.
## Roadmap
### M7 — Stabilization (pre-TestFlight)
Bug fixes only, no new features. Unblocks the first internal TestFlight build. See the full issue list in [Known Issues](#known-issues).
### M8 — UX density pass
ScarfGo is a developer tool; it needs to show more on-screen than Apple's spacious defaults. Research-driven changes:
- Migrate root navigation from "Dashboard-is-hub" to a `TabView` with `.sidebarAdaptable` style — Chat, Dashboard, Memory, More. Primary nav stops hiding below the fold.
- Clamp Dynamic Type at scene root: `.dynamicTypeSize(.xSmall ... .accessibility2)`. Semantic fonts + `@ScaledMetric`.
- Tighten list density — `.listRowSpacing(0)` + 6pt vertical insets + `.defaultMinListRowHeight(36)`, preserving 44pt hit targets via `.contentShape(Rectangle()).frame(minHeight: 44)`.
- Chat code blocks: horizontal scroll inside bubble, never wrap, `maxHeight: 240` + Expand.
- Chat tool calls: `DisclosureGroup` collapsed; title = action + elapsed ms.
- Chat scroll anchoring: iOS 18 `.defaultScrollAnchor(.bottom, for: .sizeChanges)` + suspend auto-follow on user scroll-up + "↓ new messages" pill.
- Message/row actions via `.contextMenu` — not visible buttons.
- Sheets with custom peek detents (`.presentationDetents([.height(180), .large])`) — never `.medium`.
### M9 — On-the-go essentials
Features that only make sense on mobile, in priority order:
1. **Multi-server support.** Root becomes a server list (nickname + host + status pill). Each server has two actions: **Disconnect** (soft — closes live transport, keeps Keychain key + config, one-tap to reconnect) and **Forget** (destructive — wipes credentials, re-onboards). The underlying transport factory is already `ServerID`-keyed; changes are storage layer + root nav + onboarding entry point.
2. **Project-scoped chat.** The `+` button in Chat opens a picker: "Quick chat" (default) or "In project…" — SFTP-read `~/.hermes/scarf/projects.json`, pick one, SFTP-write the scarf-managed project-context block into `<project>/AGENTS.md`, spawn `hermes acp` with `cwd = project.path`. After session id comes back, SFTP-write the attribution row into `session_project_map.json`. Unlocks the iOS parity of `ProjectAgentContextService` + `SessionAttributionService`.
3. **Session resume.** Dashboard's Recent Sessions list becomes tappable — taps call `ACPClient.loadSession(id:)` instead of starting a new session. Resume a conversation that was started on a Mac, continue it on the phone.
4. **APNs push — cron completion + pending permissions.** Notification for "your cron just ran" or "your agent needs approval to run X." The client half is ~200 LOC; the Hermes-side sender is a separate upstream feature.
5. **Lock-screen quick-approve.** Notification action button for "Approve" / "Deny" on pending permissions so the agent keeps running while you're away from the app.
6. **Scoped Settings editor.** Not a generic YAML round-trip editor — a curated set of high-value fields (model / provider / approval mode / max turns / display toggles) that save via `hermes config set <key> <value>` over SSH exec. Hermes owns the YAML round-trip; Scarf just picks values.
### M10 — TestFlight
App Store Connect + provisioning profile + internal TestFlight group of 1 (Alan). Public TestFlight after 23 real sessions on a real iPhone without crashes.
## Known Issues
All tracked from the 2026-04-24 pass-1 smoke test. This list is the truth about what's broken today — filed publicly in the interest of transparency. A ✅ means the fix has already landed on the `scarf-mobile-development` branch.
### Blocking TestFlight — must fix in M7
| # | Summary | Scope | Status |
|---|---|---|---|
| 1 | **Primary navigation hidden below Dashboard fold.** Chat / Memory / Cron / Skills / Settings links lived as the 4th section in a `List`. Replaced with `.tabViewStyle(.sidebarAdaptable)` root: 4 primary tabs (Chat / Dashboard / Memory / More) + collapse to sidebar on iPadOS later with zero UI code change. | ScarfGo | ✅ Fixed |
| 2 | **Non-retryable provider errors → perpetual spinner.** ACP error triplet (`acpError`, `acpErrorHint`, `acpErrorDetails`) promoted to ScarfCore so Mac + ScarfGo share state; ChatView renders an inline banner with Copy Details / Expand. `handlePromptComplete` now calls `recordPromptStopFailureUsingProvider(stopReason:)` on non-`end_turn` stops with the stderr tail appended. | Cross-platform | ✅ Fixed |
| 3 | **No connecting feedback when entering Chat.** ChatController's existing `.connecting` state now drives a `.regularMaterial` overlay with "Connecting to <nickname>…" + ProgressView. | ScarfGo | ✅ Fixed |
| 4 | **`isAgentWorking` doesn't clear after primary response.** Split into computed `isGenerating` (agent still producing text) + `isPostProcessing` (agent done producing; ACP `promptComplete` not yet fired). Prominent spinner drops as soon as the reply is visible; subtle "Finishing up…" pill covers auxiliary post-work. Applied cross-platform. | Cross-platform | ✅ Fixed |
| 5 | **ACP command missing PATH prefix.** SSH exec runs a non-interactive shell whose PATH is `/usr/bin:/bin:/usr/sbin:/sbin`. Fixed by prepending common install locations (`~/.local/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, `~/.hermes/bin`) to PATH inline in the exec command. Mirrors `HermesPathSet.hermesBinaryCandidates`. | ScarfGo | ✅ Fixed |
| 6 | **SFTP `~` tilde not expanded.** Per-connection cached `resolveHome()` on `ConnectionHolder` + `resolveSFTPPath()` helper applied to every SFTP entry point (`readFile` / `writeFile` / `fileExists` / `stat` / `listDirectory` / `createDirectory` / `removeFile`). | ScarfGo | ✅ Fixed |
| 7 | **No loading state on Memory editor.** Switched to throwing read (#8) so `lastError` populates on real failures instead of silently showing "empty" — the existing error banner now renders. | ScarfGo | ✅ Fixed |
| 8 | **`ServerContext.readText` swallows errors.** New `readTextThrowing(_:)` distinguishes "file absent" from "transport error"; old nil-returning `readText` stays as a `try?` shim for callers that really don't care. Memory editor uses the throwing variant. | Cross-platform | ✅ Fixed |
| 9 | **TextEditor keyboard obscures cursor.** `.scrollDismissesKeyboard(.interactively)` on the TextEditor, error pill + Saved pill moved into `.safeAreaInset(edge: .bottom)` so SwiftUI draws them above the keyboard. | ScarfGo | ✅ Fixed |
| 10 | **Save confirmation not visible.** Saved pill is now a full-width material strip inside `.safeAreaInset`, holds 2.5s (up from 1.5s), and cancels any in-flight hide task on subsequent saves so rapid saves don't drop the pill mid-fade. | ScarfGo | ✅ Fixed |
| 11 | **Cron schedule + next-run shown as machine formats.** New `CronScheduleFormatter` in ScarfCore translates the common cron shapes (every N minutes / hourly / daily at H / weekdays at H / weekends / specific weekday / monthly on day D + @-macros) into English phrases and falls back to raw expression on unrecognised shapes. Sibling `formatNextRun(iso:)` parses Hermes's ISO-8601 next-run and renders `"in 4 hours"` etc. 17 unit tests. Applied Mac + ScarfGo. | Cross-platform | ✅ Fixed |
| 12 | **"Disconnect" is factory reset.** Split properly into **Disconnect** (soft — keeps Keychain key + config, returns to ServerListView, next tap reconnects with no re-onboarding) and **Forget** (hard — removes that server's key + config, returns to list or onboarding if list becomes empty). Lives on the More tab. | ScarfGo | ✅ Fixed |
### Cross-platform (fix on Mac too)
- **Model picker accepts unknown models.** `ModelCatalogService.validateModel(_:for:)` returns `.valid` / `.unknownProvider(id)` / `.invalid(providerName, suggestions)`. Overlay-only providers (Nous / OpenAI Codex / Qwen OAuth) short-circuit to `.valid` because their catalogs aren't in models.dev. Mac `ModelPickerSheet.submitSelection` routes through the validator and raises an alert with suggestions on `.invalid`. 5 unit tests. — ✅ Fixed
### Hermes-side (upstream, not ours)
- Auxiliary `title_generation` appears to hang when the main provider returns a 404 — likely retries the failed call. Worth filing upstream; causes bug #4 to manifest. — still open upstream.
## Post-pass-1 feature work
The pass-1 session also surfaced the user-facing roadmap we delivered through M8 (UX density) and M9 (on-the-go features):
### M8 — UX density (all shipped)
- Dynamic Type clamped at scene root to `.xSmall ... .accessibility2`.
- TabView root nav replacing Dashboard-as-hub (see fix #1 above).
- `.scarfGoCompactListRow()` + `.scarfGoListDensity()` tokens applied to Memory / Cron / Skills / Dashboard / MoreTab / ServerList for ~48pt rows that still meet the 44pt tap-target invariant.
- Chat: fenced code blocks render in a horizontally-scrollable `CodeBlockView` (240pt collapsed, Expand to full) instead of soft-wrapping into unreadable columns; message bubbles gained `.contextMenu` (Copy + Share); iOS 17+ `.defaultScrollAnchor(.bottom)` + iOS 18's `.defaultScrollAnchor(.bottom, for: .sizeChanges)` replace the manual `scrollTo` dance.
- Custom `.presentationDetents` per sheet — `[.height(220), .large]` for permission sheet, `[.large]` for cron editor; never the misleading `.medium`.
### M9 — Multi-server + on-the-go (all shipped)
- **Multi-server** — storage layer (UserDefaults + Keychain) now keys by `ServerID`, with one-shot v1 → v2 migration so updating the app doesn't re-onboard anyone. New `ServerListView` root shows every configured server with nickname / user@host:port / tap-to-connect / swipe-to-forget. "+" button re-enters onboarding for a fresh server. ScarfGoTabRoot splits the old factory-reset "Disconnect" into soft Disconnect + destructive Forget rows in the More tab.
- **Session resume** — Dashboard Recent Sessions rows are now tappable; `ScarfGoCoordinator` routes the tap to the Chat tab with a `pendingResumeSessionID`; ChatController.startResuming calls `session/resume` (or falls back to `session/load` on older Hermes) with the full transcript preserved.
- **Project-scoped chat** — "+" in Chat opens a picker: Quick chat vs. In project…. Project list loads from `~/.hermes/scarf/projects.json` over SFTP. On project select, ScarfGo SFTP-writes the Scarf-managed block into `<project>/AGENTS.md` via the shared `ProjectContextBlock` service (same byte-for-byte markers as the Mac app — projects round-trip cleanly), spawns `hermes acp` with `cwd = project.path`, and records the session attribution in `session_project_map.json`. `SessionAttributionService` moved from Mac target into ScarfCore so both apps use the same store.
- **Scoped Settings editor** — curated list of 7 editable keys (model.default, model.provider, approvals.mode, agent.max_turns, display.show_cost / show_reasoning / streaming) as a Quick Edits section at the top of Settings. Save routes through `hermes config set <key> <value>` on the remote (Hermes owns the YAML round-trip); Scarf just picks the value. Inline error banner on sheet if the remote command fails.
- **APNs push skeleton** — `APNSTokenStore` + `NotificationRouter` ship ready for a future Hermes-side push sender. Lock-screen "Approve" / "Deny" action category is registered. Capability stays OFF in Xcode until Hermes gains a sender + we have an APNs auth key; flipping the capability on is a ~5-line follow-up.
## Reporting issues
- **Bugs:** <https://github.com/awizemann/scarf/issues> — tag with `component: scarfgo`.
- **Feature requests:** same, tag with `feature: scarfgo`.
- **Security / credential handling concerns:** use the repo's security policy.
## For contributors
See [Architecture Overview](Architecture-Overview) for how ScarfCore + ScarfIOS fit together. The `scarf-mobile-development` branch is the working branch; the M7/M8/M9 changes above live there pending a pass-2 smoke test before merging to `main`.
---
_Last updated: 2026-04-24 — post M7/M8/M9 implementation, pass-2 pending_
+81 -93
@@ -1,127 +1,115 @@
# ScarfGo — the iOS companion
# ScarfGo — iOS companion for Hermes
ScarfGo is the on-the-go iPhone companion to [Scarf](Home). Its scope is deliberately narrower than the Mac app: **remotely manage and interact with a running Hermes agent** from your phone. It is **not** a feature-parity port — Mac handles the full operator surface; ScarfGo handles what's valuable away from a desk.
ScarfGo is the iPhone companion to [Scarf](Home), the macOS GUI for the [Hermes AI agent](https://github.com/awizemann/hermes-agent). It connects from your phone to a Hermes server you operate (your Mac, a home Linux box, a cloud VM — anywhere reachable over SSH), and lets you run sessions, review memory, manage cron jobs, and resume conversations on the go.
**Current state:** pre-TestFlight dogfood. M6 milestone merged; pass-1 smoke test has completed against a local Hermes install. See [Known Issues](#known-issues) below before filing bugs.
> **Status:** Public beta in TestFlight. See **[Installation](#installation)** below.
**Tech stack:**
## What ScarfGo is, in one paragraph
- **Transport:** pure-Swift SSH via [Citadel](https://github.com/orlandos-nl/Citadel) 0.12.x — no OpenSSH client subprocess (iOS sandbox).
- **Shared core:** `ScarfCore` SPM package — Models / Transport / Services / ViewModels portable across macOS and iOS, unit-tested on Linux in CI.
- **iOS-only code:** `ScarfIOS` package (Citadel glue, Keychain key storage) + `Scarf iOS/` SwiftUI views.
- **Target:** iPhone, iOS 18+. iPad / macCatalyst deferred.
ScarfGo is a fully native iOS app — not a web view, not a remote desktop. It speaks SSH (Citadel under the hood, no `ssh` binary needed on iOS), reads your Hermes state directly via SFTP + SQLite snapshots, and streams real-time agent output over the [Agent Client Protocol](ACP-Subprocess) on a long-lived SSH exec channel. Every byte stays between your device and the Hermes host you configured. There are no developer-controlled servers in between.
## What's shipped today (M6)
## System requirements
| Feature | Behavior |
- iPhone running **iOS 18.0** or later.
- An **SSH-reachable Hermes host** running Hermes v0.10.0 or later. See [Hermes Version Compatibility](Hermes-Version-Compatibility).
- Your iPhone needs to reach that host on the network — same Wi-Fi, VPN, Tailscale, port-forwarded public address, or anything else SSH can dial.
- A spare second or two for onboarding to generate an SSH keypair.
## Installation
ScarfGo is in **public TestFlight**. Apple-provided test environment, free to join, no payment, no Apple ID needed for beta installs.
1. **Get the TestFlight app** — install from the App Store if you don't have it.
2. **Open the public TestFlight invite link**_link will appear here once Apple's Beta Review approves the first build. Bookmark this page._
3. **Tap "Accept" and "Install"** — TestFlight installs ScarfGo alongside your other apps.
4. **Open ScarfGo** — onboarding walks you through host details, generates a new SSH keypair, and gives you the public-key snippet to paste into your Hermes host's `~/.ssh/authorized_keys`.
Onboarding details:
- ScarfGo generates a fresh Ed25519 keypair on first run. The private half lives in the iOS Keychain (`kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` — never iCloud-synced).
- The public-key snippet is shown for you to copy and append to `~/.ssh/authorized_keys` on your Hermes host. Plain `ssh-copy-id` doesn't work from iPhone, so the manual paste is the safest path.
- A one-tap "Test connection" verifies SSH + the `hermes` binary's path before saving.
## Features
| Feature | What you can do |
|---|---|
| **Onboarding** | 8-step: host form → generate or import Ed25519 → show public key → you add to `~/.ssh/authorized_keys` → test probe → done. Single server v1 — multi-server in M9. |
| **Dashboard** | Session count, message count, tool-call count, token totals, last 5 sessions. Pulled from remote `~/.hermes/state.db` via `sqlite3 .backup` + SFTP download (WAL-safe snapshot). |
| **Chat** | Real-time ACP over a dedicated SSH exec channel. Markdown, tool-call cards, permission sheets, reasoning disclosure, streaming. **No** embedded terminal — rich chat only. |
| **Memory** | Read + write MEMORY.md, USER.md, SOUL.md (SOUL is in the Personalities feature on Mac; on iOS we fold it in here because you rarely want them separately on a phone). |
| **Cron** | List jobs, toggle enabled/disabled, edit schedule / prompt / skills / delivery route, add new, swipe-to-delete. Writes `~/.hermes/cron/jobs.json` atomically. |
| **Skills** | Read-only browse — categories + skill files per skill. |
| **Settings** | Read-only view of `config.yaml` grouped by section. Editing deferred — see M9. |
| **Multi-server** | Configure as many Hermes hosts as you like. Soft Disconnect keeps credentials; Forget wipes a server end-to-end. |
| **Dashboard** | Total sessions / messages / tool calls + a 25-session list with project badges. Filter by project. |
| **Chat** | Streamed agent responses, tool-call disclosure groups, code blocks with horizontal scroll. Project-scoped chat picks a project from your registry, writes the same Scarf-managed `AGENTS.md` block as the Mac app, and spawns `hermes acp` with the project as the working directory. |
| **Session resume** | Tap a row on the Dashboard → opens that session's transcript in Chat. CLI-started sessions hydrate from `state.db`; ACP sessions show an empty-state because Hermes doesn't persist ACP transcripts to the DB (same on Mac). |
| **Memory** | Read + edit `MEMORY.md` and `USER.md`. The "Saved" pill survives keyboard dismissal; Revert undoes unsaved edits. |
| **Cron** | List view of `~/.hermes/cron/jobs.json` with **human-readable schedules** ("Every 6 hours", "Weekdays at 09:00") and a relative next-run ("in 4 hours"). Read-only in v1 — editing comes later. |
| **Skills** | Browse the skills tree from `~/.hermes/skills/`. Read-only. |
| **Settings** | Read-only view of `config.yaml`. An in-app editor is planned but not in v1 — see [Platform Differences](Platform-Differences). |
## Deliberately not on ScarfGo
## Project-scoped chat
- **Analytics features** (Activity, Logs, Health, Insights) — belong on the Mac where screen real-estate supports them.
- **Full-surface configuration** (CredentialPools, Gateway, Templates, MCP Servers, Platforms, Plugins, Profiles, Personalities, Tools, Webhooks, QuickCommands) — config flows live on the Mac. On the go you want to run and interact, not configure.
- **Terminal mode** (embedded SwiftTerm) — out of scope for a chat-first companion.
- **Local Hermes** — iOS can't spawn subprocesses (sandbox). ScarfGo is remote-only by design.
Picking a project at the start of a chat tells the agent exactly which directory it's operating in. ScarfGo does the same handshake the Mac app does:
## Roadmap
1. SFTP-reads `~/.hermes/scarf/projects.json` for the project registry.
2. On selection, SFTP-writes a managed block into `<project>/AGENTS.md` (between `<!-- scarf-project:begin -->` and `:end -->` markers — preserves anything outside).
3. Spawns `hermes acp` with `cwd = <project.path>`.
4. After the session ID returns, records the attribution in `~/.hermes/scarf/session_project_map.json`.
### M7 — Stabilization (pre-TestFlight)
The block contains the project name, directory, dashboard path, configuration field names (never values — secrets stored in the Keychain are surfaced as field names only), and any cron jobs registered to the project. Hermes's startup context scan picks it up automatically. Ask a fresh chat _"what project am I in?"_ and the agent answers with the right name + path.
Bug fixes only, no new features. Unblocks the first internal TestFlight build. See the full issue list in [Known Issues](#known-issues).
If the SFTP write fails (permissions, disk full, network drop), ScarfGo surfaces a banner — "Project context not written — agent will proceed without it" — and starts the session anyway. The session works; it just doesn't have the augmented context.
### M8 — UX density pass
## Limitations in v1
ScarfGo is a developer tool; it needs to show more on-screen than Apple's spacious defaults. Research-driven changes:
- **No local mode.** ScarfGo only operates against an SSH-reachable Hermes host. There's no local Hermes runtime on iOS.
- **No push notifications yet.** The skeleton (UNNotificationCenter delegate, "Approve / Deny" action category) ships in the binary but is gated behind an internal feature flag because: (a) the Push Notifications capability is not yet enabled in the Xcode target, (b) Hermes doesn't yet have a push sender. When both land, push lights up on the iOS side without an app update — well, with one update to flip the flag. Watch this page.
- **No in-app config editor.** Settings is read-only in v1. Use the Mac app or a remote shell to change values.
- **No template install UI.** `.scarftemplate` install + uninstall is Mac-only in v1.
- **No terminal mode.** Rich-chat (ACP) only.
- **English only.** The Mac app ships in 7 languages; ScarfGo is English-only for v1.
- **Push from Hermes server-side is upstream work.** The iOS side is ready; Hermes needs the sender.
- Migrate root navigation from "Dashboard-is-hub" to a `TabView` with `.sidebarAdaptable` style — Chat, Dashboard, Memory, More. Primary nav stops hiding below the fold.
- Clamp Dynamic Type at scene root: `.dynamicTypeSize(.xSmall ... .accessibility2)`. Semantic fonts + `@ScaledMetric`.
- Tighten list density — `.listRowSpacing(0)` + 6pt vertical insets + `.defaultMinListRowHeight(36)`, preserving 44pt hit targets via `.contentShape(Rectangle()).frame(minHeight: 44)`.
- Chat code blocks: horizontal scroll inside bubble, never wrap, `maxHeight: 240` + Expand.
- Chat tool calls: `DisclosureGroup` collapsed; title = action + elapsed ms.
- Chat scroll anchoring: iOS 18 `.defaultScrollAnchor(.bottom, for: .sizeChanges)` + suspend auto-follow on user scroll-up + "↓ new messages" pill.
- Message/row actions via `.contextMenu` — not visible buttons.
- Sheets with custom peek detents (`.presentationDetents([.height(180), .large])`) — never `.medium`.
See [Platform Differences](Platform-Differences) for a full Mac-vs-iOS feature matrix.
### M9 — On-the-go essentials
## Troubleshooting
Features that only make sense on mobile, in priority order:
**Onboarding can't connect.** First check that `ssh user@host` works from your Mac with the same hostname/port. If that fails, ScarfGo can't connect either — fix SSH first. If the Mac connection works:
1. **Multi-server support.** Root becomes a server list (nickname + host + status pill). Each server has two actions: **Disconnect** (soft — closes live transport, keeps Keychain key + config, one-tap to reconnect) and **Forget** (destructive — wipes credentials, re-onboards). The underlying transport factory is already `ServerID`-keyed; changes are storage layer + root nav + onboarding entry point.
2. **Project-scoped chat.** The `+` button in Chat opens a picker: "Quick chat" (default) or "In project…" — SFTP-read `~/.hermes/scarf/projects.json`, pick one, SFTP-write the scarf-managed project-context block into `<project>/AGENTS.md`, spawn `hermes acp` with `cwd = project.path`. After session id comes back, SFTP-write the attribution row into `session_project_map.json`. Unlocks the iOS parity of `ProjectAgentContextService` + `SessionAttributionService`.
3. **Session resume.** Dashboard's Recent Sessions list becomes tappable — taps call `ACPClient.loadSession(id:)` instead of starting a new session. Resume a conversation that was started on a Mac, continue it on the phone.
4. **APNs push — cron completion + pending permissions.** Notification for "your cron just ran" or "your agent needs approval to run X." The client half is ~200 LOC; the Hermes-side sender is a separate upstream feature.
5. **Lock-screen quick-approve.** Notification action button for "Approve" / "Deny" on pending permissions so the agent keeps running while you're away from the app.
6. **Scoped Settings editor.** Not a generic YAML round-trip editor — a curated set of high-value fields (model / provider / approval mode / max turns / display toggles) that save via `hermes config set <key> <value>` over SSH exec. Hermes owns the YAML round-trip; Scarf just picks values.
- Make sure you appended the ScarfGo-shown public-key to `~/.ssh/authorized_keys` on the host.
- Make sure `hermes` is in the SSH user's PATH on the host. ScarfGo prepends common pipx / Homebrew install paths to the exec command, but if `hermes` lives somewhere unusual, run `which hermes` over SSH and ensure the path is one of: `~/.local/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, or `~/.hermes/bin`.
### M10 — TestFlight
**Dashboard shows zero sessions but I know there are some.** ScarfGo downloads a snapshot of `~/.hermes/state.db` over SFTP. If your Hermes install hasn't yet written the DB (no sessions ever started), the snapshot is empty. Start a session via the Mac app or CLI first.
App Store Connect + provisioning profile + internal TestFlight group of 1 (Alan). Public TestFlight after 23 real sessions on a real iPhone without crashes.
**Memory says "Save failed" silently.** Pull-to-refresh — usually a transient SFTP hiccup. If it persists, check the SSH user has write permission on `~/.hermes/memories/`.
## Known Issues
**The agent's running forever after a non-retryable error.** v2.5 added a banner for HTTP 4xx/5xx provider errors; older builds (or upstream Hermes hangs we haven't worked around yet) might still show "Thinking…" indefinitely. Tap the Stop button in Chat to abort — that always works.
All tracked from the 2026-04-24 pass-1 smoke test. This list is the truth about what's broken today — filed publicly in the interest of transparency. A ✅ means the fix has already landed on the `scarf-mobile-development` branch.
**Biometric prompt loops or fails.** Cancelling Face ID / passcode prompts no longer drops you into fresh onboarding (v2.5 fix); the app surfaces a banner on the server list with a Dismiss button. Re-tap the server to retry.
### Blocking TestFlight — must fix in M7
## FAQ
| # | Summary | Scope | Status |
|---|---|---|---|
| 1 | **Primary navigation hidden below Dashboard fold.** Chat / Memory / Cron / Skills / Settings links lived as the 4th section in a `List`. Replaced with `.tabViewStyle(.sidebarAdaptable)` root: 4 primary tabs (Chat / Dashboard / Memory / More) + collapse to sidebar on iPadOS later with zero UI code change. | ScarfGo | ✅ Fixed |
| 2 | **Non-retryable provider errors → perpetual spinner.** ACP error triplet (`acpError`, `acpErrorHint`, `acpErrorDetails`) promoted to ScarfCore so Mac + ScarfGo share state; ChatView renders an inline banner with Copy Details / Expand. `handlePromptComplete` now calls `recordPromptStopFailureUsingProvider(stopReason:)` on non-`end_turn` stops with the stderr tail appended. | Cross-platform | ✅ Fixed |
| 3 | **No connecting feedback when entering Chat.** ChatController's existing `.connecting` state now drives a `.regularMaterial` overlay with "Connecting to <nickname>…" + ProgressView. | ScarfGo | ✅ Fixed |
| 4 | **`isAgentWorking` doesn't clear after primary response.** Split into computed `isGenerating` (agent still producing text) + `isPostProcessing` (agent done producing; ACP `promptComplete` not yet fired). Prominent spinner drops as soon as the reply is visible; subtle "Finishing up…" pill covers auxiliary post-work. Applied cross-platform. | Cross-platform | ✅ Fixed |
| 5 | **ACP command missing PATH prefix.** SSH exec runs a non-interactive shell whose PATH is `/usr/bin:/bin:/usr/sbin:/sbin`. Fixed by prepending common install locations (`~/.local/bin`, `/opt/homebrew/bin`, `/usr/local/bin`, `~/.hermes/bin`) to PATH inline in the exec command. Mirrors `HermesPathSet.hermesBinaryCandidates`. | ScarfGo | ✅ Fixed |
| 6 | **SFTP `~` tilde not expanded.** Per-connection cached `resolveHome()` on `ConnectionHolder` + `resolveSFTPPath()` helper applied to every SFTP entry point (`readFile` / `writeFile` / `fileExists` / `stat` / `listDirectory` / `createDirectory` / `removeFile`). | ScarfGo | ✅ Fixed |
| 7 | **No loading state on Memory editor.** Switched to throwing read (#8) so `lastError` populates on real failures instead of silently showing "empty" — the existing error banner now renders. | ScarfGo | ✅ Fixed |
| 8 | **`ServerContext.readText` swallows errors.** New `readTextThrowing(_:)` distinguishes "file absent" from "transport error"; old nil-returning `readText` stays as a `try?` shim for callers that really don't care. Memory editor uses the throwing variant. | Cross-platform | ✅ Fixed |
| 9 | **TextEditor keyboard obscures cursor.** `.scrollDismissesKeyboard(.interactively)` on the TextEditor, error pill + Saved pill moved into `.safeAreaInset(edge: .bottom)` so SwiftUI draws them above the keyboard. | ScarfGo | ✅ Fixed |
| 10 | **Save confirmation not visible.** Saved pill is now a full-width material strip inside `.safeAreaInset`, holds 2.5s (up from 1.5s), and cancels any in-flight hide task on subsequent saves so rapid saves don't drop the pill mid-fade. | ScarfGo | ✅ Fixed |
| 11 | **Cron schedule + next-run shown as machine formats.** New `CronScheduleFormatter` in ScarfCore translates the common cron shapes (every N minutes / hourly / daily at H / weekdays at H / weekends / specific weekday / monthly on day D + @-macros) into English phrases and falls back to raw expression on unrecognised shapes. Sibling `formatNextRun(iso:)` parses Hermes's ISO-8601 next-run and renders `"in 4 hours"` etc. 17 unit tests. Applied Mac + ScarfGo. | Cross-platform | ✅ Fixed |
| 12 | **"Disconnect" is factory reset.** Split properly into **Disconnect** (soft — keeps Keychain key + config, returns to ServerListView, next tap reconnects with no re-onboarding) and **Forget** (hard — removes that server's key + config, returns to list or onboarding if list becomes empty). Lives on the More tab. | ScarfGo | ✅ Fixed |
**Q: Can ScarfGo run Hermes locally on the iPhone?**
A: No. Hermes is a Python agent that needs Python plus a model provider's CLI plus a writable filesystem. iOS doesn't make any of that practical. ScarfGo is a thin client.
### Cross-platform (fix on Mac too)
**Q: Will my SSH key sync to my other devices?**
A: No. The Keychain entry is `ThisDeviceOnly` — explicitly excluded from iCloud Keychain sync. Adding a second device means a second key + a second `authorized_keys` line.
- **Model picker accepts unknown models.** `ModelCatalogService.validateModel(_:for:)` returns `.valid` / `.unknownProvider(id)` / `.invalid(providerName, suggestions)`. Overlay-only providers (Nous / OpenAI Codex / Qwen OAuth) short-circuit to `.valid` because their catalogs aren't in models.dev. Mac `ModelPickerSheet.submitSelection` routes through the validator and raises an alert with suggestions on `.invalid`. 5 unit tests. — ✅ Fixed
**Q: Can I use ScarfGo with a Hermes host that's not on my LAN?**
A: Yes — anywhere reachable over SSH. Tailscale, port forwarding, a VPS, anything. The Hermes host doesn't know it's being driven by an iPhone vs a Mac.
### Hermes-side (upstream, not ours)
**Q: Why is push disabled?**
A: Two reasons that need to land together: (1) the Push Notifications capability requires Apple Developer Program enrollment + an APNs auth key, (2) Hermes needs a server-side push sender to actually emit pushes. The iOS skeleton ships ready; flipping it on is one app update + one Hermes update.
- Auxiliary `title_generation` appears to hang when the main provider returns a 404 — likely retries the failed call. Worth filing upstream; causes bug #4 to manifest. — still open upstream.
**Q: Is my data sent to anyone?**
A: No. See the [privacy policy](https://awizemann.github.io/scarf/privacy/). The apps make exactly three kinds of network connections: (1) SSH to your Hermes hosts, (2) Sparkle update checks (Mac only), (3) HTTPS to GitHub Pages for the public template catalog. Zero analytics.
## Post-pass-1 feature work
The pass-1 session also surfaced the user-facing roadmap we delivered through M8 (UX density) and M9 (on-the-go features):
### M8 — UX density (all shipped)
- Dynamic Type clamped at scene root to `.xSmall ... .accessibility2`.
- TabView root nav replacing Dashboard-as-hub (see fix #1 above).
- `.scarfGoCompactListRow()` + `.scarfGoListDensity()` tokens applied to Memory / Cron / Skills / Dashboard / MoreTab / ServerList for ~48pt rows that still meet the 44pt tap-target invariant.
- Chat: fenced code blocks render in a horizontally-scrollable `CodeBlockView` (240pt collapsed, Expand to full) instead of soft-wrapping into unreadable columns; message bubbles gained `.contextMenu` (Copy + Share); iOS 17+ `.defaultScrollAnchor(.bottom)` + iOS 18's `.defaultScrollAnchor(.bottom, for: .sizeChanges)` replace the manual `scrollTo` dance.
- Custom `.presentationDetents` per sheet — `[.height(220), .large]` for permission sheet, `[.large]` for cron editor; never the misleading `.medium`.
### M9 — Multi-server + on-the-go (all shipped)
- **Multi-server** — storage layer (UserDefaults + Keychain) now keys by `ServerID`, with one-shot v1 → v2 migration so updating the app doesn't re-onboard anyone. New `ServerListView` root shows every configured server with nickname / user@host:port / tap-to-connect / swipe-to-forget. "+" button re-enters onboarding for a fresh server. ScarfGoTabRoot splits the old factory-reset "Disconnect" into soft Disconnect + destructive Forget rows in the More tab.
- **Session resume** — Dashboard Recent Sessions rows are now tappable; `ScarfGoCoordinator` routes the tap to the Chat tab with a `pendingResumeSessionID`; ChatController.startResuming calls `session/resume` (or falls back to `session/load` on older Hermes) with the full transcript preserved.
- **Project-scoped chat** — "+" in Chat opens a picker: Quick chat vs. In project…. Project list loads from `~/.hermes/scarf/projects.json` over SFTP. On project select, ScarfGo SFTP-writes the Scarf-managed block into `<project>/AGENTS.md` via the shared `ProjectContextBlock` service (same byte-for-byte markers as the Mac app — projects round-trip cleanly), spawns `hermes acp` with `cwd = project.path`, and records the session attribution in `session_project_map.json`. `SessionAttributionService` moved from Mac target into ScarfCore so both apps use the same store.
- **Scoped Settings editor** — curated list of 7 editable keys (model.default, model.provider, approvals.mode, agent.max_turns, display.show_cost / show_reasoning / streaming) as a Quick Edits section at the top of Settings. Save routes through `hermes config set <key> <value>` on the remote (Hermes owns the YAML round-trip); Scarf just picks the value. Inline error banner on sheet if the remote command fails.
- **APNs push skeleton** — `APNSTokenStore` + `NotificationRouter` ship ready for a future Hermes-side push sender. Lock-screen "Approve" / "Deny" action category is registered. Capability stays OFF in Xcode until Hermes gains a sender + we have an APNs auth key; flipping the capability on is a ~5-line follow-up.
**Q: Where can I see what's planned next?**
A: [ScarfGo Roadmap](ScarfGo-Roadmap) tracks shipped milestones (M6 / M7 / M8 / M9) and remaining work. The [main Roadmap](Roadmap) covers cross-platform plans.
## Reporting issues
- **Bugs:** <https://github.com/awizemann/scarf/issues> — tag with `component: scarfgo`.
- **Feature requests:** same, tag with `feature: scarfgo`.
- **Security / credential handling concerns:** use the repo's security policy.
## For contributors
See [Architecture Overview](Architecture-Overview) for how ScarfCore + ScarfIOS fit together. The `scarf-mobile-development` branch is the working branch; the M7/M8/M9 changes above live there pending a pass-2 smoke test before merging to `main`.
- **Bugs:** [github.com/awizemann/scarf/issues](https://github.com/awizemann/scarf/issues) — tag `component: scarfgo`.
- **Feature requests:** same, tag `feature: scarfgo`.
- **TestFlight feedback:** the Send Beta Feedback button in TestFlight goes straight to the developer.
- **Security / credential concerns:** use the repo's security policy.
---
_Last updated: 2026-04-24 — post M7/M8/M9 implementation, pass-2 pending_
_Last updated: 2026-04-25 — v2.5 public TestFlight._
+5
@@ -4,6 +4,10 @@
- [Updating](Updating)
- [Uninstalling](Uninstalling)
**ScarfGo (iOS)**
- [ScarfGo](ScarfGo)
- [Platform Differences](Platform-Differences)
**User Guide**
- [Dashboard](Dashboard)
- [Insights & Activity](Insights-and-Activity)
@@ -39,6 +43,7 @@
**Contributing**
- [Contributing](Contributing)
- [Wiki Maintenance](Wiki-Maintenance)
- [ScarfGo Roadmap](ScarfGo-Roadmap) (dev reference)
**Release History**
- [Release Notes Index](Release-Notes-Index)