feat(memory): hermes memory reset toolbar action + v0.11 CLI doc (Phase 5)

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) <noreply@anthropic.com>
This commit is contained in:
Alan Wizemann
2026-04-25 09:29:26 +02:00
parent 8057beb001
commit f35bc910e4
2 changed files with 62 additions and 1 deletions
+10 -1
View File
@@ -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 <prompt>` — 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:
@@ -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 {