Files
agent-wiki-ui/Core/ChatStore.cs
T
davideandClaude Sonnet 5.5 e60d64ed58 Motori multipli: Codex e interruttore CLI nelle Impostazioni
Introduce l'astrazione Engine (Claude Code, Codex) con argomenti e parser
propri e EngineRunner come unico punto di avvio, al posto di Claude.cs. Il
JSON delle chat salva motore e id di sessione (i file vecchi restano chat di
Claude Code). Cambiare CLI apre una nuova chat; modelli ed effort seguono il
motore; un motore non installato non è selezionabile. La CLI accetta
--engine <id>. Codex gira in sola lettura e la sessione si legge da
thread.started.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 11:40:47 +02:00

72 lines
2.9 KiB
C#

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<Msg> 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<Msg> 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<Stored> List() => Directory.Exists(Dir)
? Directory.EnumerateFiles(Dir, "*.json").Select(Read).OfType<Stored>().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<Stored>(File.ReadAllText(file)); }
catch (Exception e) when (e is IOException or JsonException) { return null; }
}
}