From f35bc910e4b45d4035ce0221da9563ed5806b39e Mon Sep 17 00:00:00 2001 From: Alan Wizemann Date: Sat, 25 Apr 2026 09:29:26 +0200 Subject: [PATCH] feat(memory): hermes memory reset toolbar action + v0.11 CLI doc (Phase 5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adopt the lowest-risk new CLI subcommand from Hermes v2026.4.23 — `hermes memory reset --yes` — and document the deferred ones for v2.6. Wholesale plugin/profile/webhook/logs adoption is forward- compatible work the existing services don't block on; deferring keeps v2.5 scope tight. MemoryView: - Toolbar button "Reset memory…" with .arrow.counterclockwise icon. - Confirmation dialog explaining the destructive semantics (no undo, wipes both MEMORY.md and USER.md). Routes through context.runHermes(["memory", "reset", "--yes"]); on non-zero exit shows the stderr in an alert. Refreshes the on-screen content on success. CLAUDE.md: - "Hermes Version" section now leads with v2026.4.23 (v0.11.0) and enumerates the v2.5-adopted features (slash steer, state.db deltas, new skills, frontmatter chips, memory reset) with file pointers. v2.6-deferred CLIs (plugins / profile / webhook / insights / logs) are flagged so future bandwidth knows where to pick up. Verified: Mac build clean. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 11 +++- .../Features/Memory/Views/MemoryView.swift | 52 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 990bf4d..5c13967 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -84,7 +84,16 @@ Public documentation lives in the GitHub wiki at https://github.com/awizemann/sc ## Hermes Version -Targets Hermes v0.10.0 (v2026.4.16). Log lines may carry an optional `[session_id]` tag between the level and logger name — `HermesLogService.parseLine` treats the session tag as an optional capture group, so older untagged lines still parse. +Targets Hermes v2026.4.23 (v0.11.0). Log lines may carry an optional `[session_id]` tag between the level and logger name — `HermesLogService.parseLine` treats the session tag as an optional capture group, so older untagged lines still parse. + +**v2026.4.23 (v0.11.0)** added (Scarf-relevant subset): + +- `/steer ` — non-interruptive mid-run guidance slash command. Surfaced in Scarf chat menus via `RichChatViewModel.nonInterruptiveCommands`; `ChatViewModel.sendViaACP` (Mac) and `ChatController.send` (iOS) skip the "Agent working…" status flip and show a transient toast instead. +- New CLI subcommands: `hermes plugins` / `profile` / `webhook` / `insights` / `logs` / `memory reset` / `completion` / `dashboard`. Scarf v2.5 adopts **`hermes memory reset`** (toolbar button on MemoryView with destructive confirmation). The other CLIs are documented here for v2.6 — Scarf still reads `~/.hermes/plugins/`, `~/.hermes/profiles/` etc directly today; switching those paths to the canonical CLI is a forward-compatible change to make when bandwidth permits. +- New state.db columns: `messages.reasoning_content` + `sessions.api_call_count`. `HermesDataService.detectSchema` flips `hasV011Schema` only when both are present (partial migrations stay on v0.7 path). Surfaced as the "API" chip on session rows + a network-icon counter in DashboardView. `HermesMessage.preferredReasoning` picks the newer column when both reasoning channels are populated. +- New skills: `design-md` (Google's DESIGN.md authoring; needs `npx`/Node 18+ on host — checked via `SkillPrereqService` and surfaced as a yellow banner) and `spotify` (OAuth via `hermes auth spotify` — driven by `SpotifyAuthFlow` + `SpotifySignInSheet`, mirroring v2.3 Nous Portal pattern). +- Updated skills: `research-paper-writing` 1.1.0 (+SciencePlots dep), `segment-anything-model` (expanded docs), `google-workspace` (gws CLI prefer + granular OAuth scopes), `hermes-agent` (in-tree). +- SKILL.md frontmatter gains `allowed_tools` / `related_skills` / `dependencies` lists. `HermesSkill` carries them as optional fields; `SkillsView` (Mac) + `SkillDetailView` (iOS) render them as chip rows when populated. v0.10.0 introduced the **Tool Gateway** — paid Nous Portal subscribers route web search, image generation, TTS, and browser automation through their subscription without separate API keys. In Scarf: diff --git a/scarf/scarf/Features/Memory/Views/MemoryView.swift b/scarf/scarf/Features/Memory/Views/MemoryView.swift index 8d77137..edc3499 100644 --- a/scarf/scarf/Features/Memory/Views/MemoryView.swift +++ b/scarf/scarf/Features/Memory/Views/MemoryView.swift @@ -3,6 +3,8 @@ import ScarfCore struct MemoryView: View { @State private var viewModel: MemoryViewModel + @State private var showResetConfirm: Bool = false + @State private var resetError: String? @Environment(HermesFileWatcher.self) private var fileWatcher init(context: ServerContext) { @@ -61,6 +63,56 @@ struct MemoryView: View { .sheet(isPresented: $viewModel.isEditing) { editorSheet } + .toolbar { + // v2.5: `hermes memory reset` (Hermes v2026.4.23+) wipes + // both MEMORY.md and USER.md atomically — useful when a + // session went off the rails. Destructive, confirmation- + // gated, surfaced as a small toolbar button rather than + // a prominent button to avoid accidental clicks. + ToolbarItem(placement: .primaryAction) { + Button { + showResetConfirm = true + } label: { + Label("Reset memory…", systemImage: "arrow.counterclockwise") + } + .help("Reset MEMORY.md and USER.md to empty (Hermes v2026.4.23+)") + } + } + .confirmationDialog( + "Reset memory?", + isPresented: $showResetConfirm, + titleVisibility: .visible + ) { + Button("Reset", role: .destructive) { + resetMemoryRemotely() + } + Button("Cancel", role: .cancel) {} + } message: { + Text("Wipes MEMORY.md and USER.md to empty via `hermes memory reset --yes`. The agent's accumulated knowledge for this server is gone immediately. Use this when a session went off the rails — there's no undo.") + } + .alert("Couldn't reset memory", isPresented: Binding( + get: { resetError != nil }, + set: { if !$0 { resetError = nil } } + )) { + Button("OK") { resetError = nil } + } message: { + Text(resetError ?? "") + } + } + + /// Run `hermes memory reset --yes` over the active context's + /// transport. Refreshes the on-screen content on success; surfaces + /// stderr in an alert on failure. + private func resetMemoryRemotely() { + let result = viewModel.context.runHermes(["memory", "reset", "--yes"]) + if result.exitCode == 0 { + viewModel.load() + } else { + let trimmed = result.output.trimmingCharacters(in: .whitespacesAndNewlines) + resetError = trimmed.isEmpty + ? "hermes memory reset exited with status \(result.exitCode)." + : trimmed + } } private func memorySection(_ title: String, content: String, charCount: Int, target: MemoryViewModel.EditTarget) -> some View {