using System.Text.Json; namespace AgentWikiUi.Core; record Msg(string Role, string Text); // Role: user | ai | err // Engine e SessionId mancano nei file scritti prima dei motori multipli: valgono Claude Code e SessionId = Id. record Stored(string Id, string Title, DateTime Updated, bool Started, List Messages, string? Engine = null, string? SessionId = null) { public string EngineId => Engine ?? "claude"; public string? EffectiveSessionId => SessionId ?? (EngineId == "claude" ? Id : null); } // Cronologia: un JSON per chat, il nome file è l'id di sessione di `claude`. static class ChatStore { // Nomi precedenti dell'app: le loro chat vengono spostate nella cartella attuale al primo avvio. static readonly string[] LegacyNames = ["LlmRun"]; static readonly string Dir = Prepare(); static string Prepare() { var data = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); var dir = Path.Combine(data, AppInfo.Name, "chats"); if (Directory.Exists(dir)) return dir; foreach (var name in LegacyNames) { var old = Path.Combine(data, name, "chats"); if (!Directory.Exists(old)) continue; try { Directory.CreateDirectory(Path.GetDirectoryName(dir)!); Directory.Move(old, dir); } catch (IOException) { } // se non si riesce a spostare, si riparte da una cartella vuota break; } return dir; } // L'id può arrivare dalla UI e diventa un nome file: accettare solo Guid. static string? PathFor(string id) => Guid.TryParse(id, out var g) ? Path.Combine(Dir, g.ToString() + ".json") : null; public static void Save(string id, string engine, string? sessionId, bool started, List messages) { if (messages.Count == 0) return; Directory.CreateDirectory(Dir); var first = messages.First(m => m.Role == "user").Text.ReplaceLineEndings(" ").Trim(); var title = first.Length > 50 ? first[..50] + "…" : first; File.WriteAllText(PathFor(id)!, JsonSerializer.Serialize(new Stored(id, title, DateTime.Now, started, messages, engine, sessionId))); } public static Stored? Load(string id) { var file = PathFor(id); return file is not null && File.Exists(file) ? Read(file) : null; } // Dalla più recente; i file corrotti sono ignorati. public static List List() => Directory.Exists(Dir) ? Directory.EnumerateFiles(Dir, "*.json").Select(Read).OfType().OrderByDescending(c => c.Updated).ToList() : []; public static void Delete(string id) { var file = PathFor(id); if (file is not null && File.Exists(file)) File.Delete(file); } static Stored? Read(string file) { try { return JsonSerializer.Deserialize(File.ReadAllText(file)); } catch (Exception e) when (e is IOException or JsonException) { return null; } } }