mirror of
https://github.com/awizemann/scarf.git
synced 2026-05-10 18:44:45 +00:00
feat(ios-memory): hermes memory reset on iOS too (cross-platform parity)
Mac shipped the toolbar Reset button in Phase 5; iOS gets it in the final verification pass for parity. iOS MemoryListView: - Toolbar button (counterclockwise icon) opens a destructive confirmation dialog matching the Mac copy. - resetMemory() shells out via context.makeTransport().runProcess, using the same PATH-prefix trick IOSSettingsViewModel.saveValue uses so non-interactive remote shells find hermes in ~/.local/bin / /opt/homebrew/bin / ~/.hermes/bin. - Success and failure both surface alerts (success message confirms the wipe; failure surfaces stderr+stdout combined). Verified: iOS build clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,9 @@ import ScarfCore
|
||||
/// `IOSMemoryViewModel` which lives in ScarfCore.
|
||||
struct MemoryListView: View {
|
||||
let config: IOSServerConfig
|
||||
@State private var showResetConfirm = false
|
||||
@State private var resetError: String?
|
||||
@State private var resetSucceeded = false
|
||||
|
||||
private static let sharedContextID: ServerID = ServerID(
|
||||
uuidString: "00000000-0000-0000-0000-0000000000A1"
|
||||
@@ -32,6 +35,77 @@ struct MemoryListView: View {
|
||||
.scarfGoListDensity()
|
||||
.navigationTitle("Memory")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
// v2.5: `hermes memory reset` (Hermes v2026.4.23+) wipes
|
||||
// both MEMORY.md and USER.md atomically. Surfaced as a
|
||||
// toolbar button (smaller fat-finger target than a list
|
||||
// row) gated behind a destructive confirmation dialog.
|
||||
ToolbarItem(placement: .topBarTrailing) {
|
||||
Button {
|
||||
showResetConfirm = true
|
||||
} label: {
|
||||
Image(systemName: "arrow.counterclockwise")
|
||||
}
|
||||
.accessibilityLabel("Reset memory")
|
||||
}
|
||||
}
|
||||
.confirmationDialog(
|
||||
"Reset memory?",
|
||||
isPresented: $showResetConfirm,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Reset", role: .destructive) {
|
||||
Task { await resetMemory(context: ctx) }
|
||||
}
|
||||
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 only when a session went off the rails.")
|
||||
}
|
||||
.alert("Couldn't reset memory", isPresented: Binding(
|
||||
get: { resetError != nil },
|
||||
set: { if !$0 { resetError = nil } }
|
||||
)) {
|
||||
Button("OK") { resetError = nil }
|
||||
} message: {
|
||||
Text(resetError ?? "")
|
||||
}
|
||||
.alert("Memory reset", isPresented: $resetSucceeded) {
|
||||
Button("OK") {}
|
||||
} message: {
|
||||
Text("MEMORY.md and USER.md were cleared on the host.")
|
||||
}
|
||||
}
|
||||
|
||||
/// Run `hermes memory reset --yes` over the iOS context's transport
|
||||
/// (Citadel SSH exec). Mirrors the PATH-prefix trick
|
||||
/// IOSSettingsViewModel.saveValue uses so non-interactive shells
|
||||
/// find hermes even when it's in `~/.local/bin` or `/opt/homebrew/bin`.
|
||||
private func resetMemory(context: ServerContext) async {
|
||||
let hermes = context.paths.hermesBinary
|
||||
let script = "PATH=\"$HOME/.local/bin:/opt/homebrew/bin:/usr/local/bin:$HOME/.hermes/bin:$PATH\" \(hermes) memory reset --yes"
|
||||
let ctx = context
|
||||
do {
|
||||
let result = try await Task.detached {
|
||||
try ctx.makeTransport().runProcess(
|
||||
executable: "/bin/sh",
|
||||
args: ["-c", script],
|
||||
stdin: nil,
|
||||
timeout: 15
|
||||
)
|
||||
}.value
|
||||
if result.exitCode == 0 {
|
||||
resetSucceeded = true
|
||||
} else {
|
||||
let stderr = result.stderrString.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let stdout = result.stdoutString.trimmingCharacters(in: .whitespacesAndNewlines)
|
||||
let combined = [stderr, stdout].filter { !$0.isEmpty }.joined(separator: "\n")
|
||||
resetError = combined.isEmpty
|
||||
? "hermes memory reset exited with status \(result.exitCode)."
|
||||
: combined
|
||||
}
|
||||
} catch {
|
||||
resetError = "Couldn't reach Hermes: \(error.localizedDescription)"
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
|
||||
Reference in New Issue
Block a user