Ogni chat è salvata come JSON in %LOCALAPPDATA%\LlmRun\chats (nome file = id di sessione di claude). La barra laterale elenca le chat, permette di riaprirle con --resume e di eliminarle. Il messaggio dell'utente è salvato subito, prima della risposta, e all'avvio si riapre l'ultima conversazione. Documentazione e roadmap aggiornate. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
6.7 KiB
MVP (doc-first)
Obiettivo
Eseguibile C# a riga di comando che inoltra un prompt a Claude Code (claude) lanciandolo nella directory dell'eseguibile, e ne mostra l'output.
Uso
llmrun.exe "spiega questo progetto"
Tutti gli argomenti sono uniti in un unico prompt.
Comportamento
- Se non ci sono argomenti: stampa l'uso ed esce con codice 2.
- Avvia
claude -p <prompt>(modalità non interattiva) conWorkingDirectory = AppContext.BaseDirectory. - Ritrasmette stdout e stderr del processo figlio in tempo reale.
- Esce con lo stesso exit code di
claude. - Se
claudenon è trovato: messaggio chiaro ed exit code 127.
GUI (chat)
Senza argomenti si apre una finestra WinForms che contiene solo un WebView2: l'intera interfaccia (header, conversazione, composer) è HTML/CSS in ui.html, in stile claude.ai/ChatGPT. Con argomenti resta la CLI.
Struttura della pagina
- Barra laterale (sinistra, comprimibile): logo e nome, pulsante Nuova chat, elenco delle chat salvate (titolo = inizio del primo messaggio, data/ora dell'ultimo scambio; la chat aperta è evidenziata). Un clic su una chat la riapre; la ✕ (con conferma) la elimina. Elenco vuoto: "Nessuna chat salvata".
- Conversazione: messaggi alternati utente/Claude, aggiunti al DOM uno alla volta (nessun ricaricamento, nessuno sfarfallio). Le risposte sono renderizzate in markdown (Markdig, lato C#): titoli, liste, tabelle, citazioni, blocchi di codice. Sulle risposte il pulsante Copia copia il markdown; sui blocchi di codice copia il codice.
- Schermata iniziale (chat vuota): titolo, sottotitolo e quattro suggerimenti cliccabili che precompilano il campo.
- Composer (in basso, scheda arrotondata): textarea che cresce fino a ~8 righe, selettori Modello (Predefinito, Fable 5.1, Opus 5.5, Sonnet 5.5, Haiku 5.5, "Altro ID…" con campo libero) ed Effort (Predefinito, low, medium, high, xhigh, max), pulsante Invia circolare con icona.
Invioinvia,Maiusc+Inviova a capo. Hint sulla tastiera sotto la scheda. - Passati come
--model/--effort; con "Predefinito" il flag viene omesso. Solo GUI. - Contesto:
claude -p --session-id <guid>al primo messaggio,--resume <guid>ai successivi. "Nuova chat" svuota la vista e genera un nuovo guid. - Cronologia: dopo ogni scambio la chat è salvata in
%LOCALAPPDATA%\LlmRun\chats\<guid>.json(guid = id di sessione diclaude, titolo, data, flagstarted, messaggi con ruolo e testo). Riaprendo una chat si rimostra la conversazione e si prosegue con--resume <guid>, quindiclaudericorda il contesto. Il messaggio dell'utente viene salvato subito, prima della risposta (così non si perde se l'app si chiude mentre Claude risponde); la risposta si aggiunge appena arriva. Una chat nuova e vuota non viene salvata. All'avvio l'app riapre automaticamente l'ultima conversazione (la più recente); se non ce ne sono parte con la schermata iniziale. "Nuova chat" resta sempre disponibile. Eliminare una chat cancella solo il file JSON, non la sessione diclaude. Non si può aprire/eliminare una chat mentre Claude sta rispondendo. - Limite noto: la sessione di
claudeè legata alla cartella in cui gira (quella dell'exe): se si sposta l'exe, le vecchie chat si leggono ancora ma--resumenon ritrova il contesto. - La risposta compare a fine generazione (nessuno streaming); durante l'attesa un indicatore a puntini animati e Invia disabilitato. Se
claudeesce con errore, lo stderr appare come messaggio di errore. - I link si aprono nel browser di sistema. L'HTML grezzo nel markdown è disabilitato.
- Finestra orizzontale con proporzioni 16:10: larga il 75% dell'area utile dello schermo (max 1440px), altezza di conseguenza (max 90% dell'area utile), centrata; minimo 900x600. Su un monitor Full HD 1920x1080 risulta circa 1440x900.
Protocollo pagina ↔ C# (WebMessage JSON). Pagina→C#: {type:"send", text, model, effort}, {type:"reset"}, {type:"list"}, {type:"open", id}, {type:"delete", id}. C#→pagina: {type:"ai", html, md}, {type:"err", text}, {type:"done"}, {type:"chats", current, items:[{id,title,updated}]}, {type:"load", messages:[{role,text,html?}]} (sostituisce la conversazione mostrata).
Design (UI/UX)
Sorgente di verità per l'aspetto, derivato dalla skill ui-ux-pro-max (stile "strumento per sviluppatori": slate + accento verde).
- Tema: segue Windows (chiaro/scuro) via
prefers-color-scheme; la barra del titolo segueApplication.SetColorMode(SystemColorMode.System); lo sfondo del WebView2 è impostato per evitare il lampo bianco all'avvio. - Token (chiaro / scuro): sfondo
#FFFFFF/#0F172A, superficie composer#FFFFFF/#1E293B, testo#0F172A/#F8FAFC, testo secondario#475569/#94A3B8, bordi#CBD5E1/#334155, accento#15803D/#22C55E, errore#B91C1C/#F87171. Contrasto testo ≥ 4.5:1. - Tipografia: Segoe UI Variable (corpo 15.5px, interlinea 1.65), Cascadia Code/Consolas per il codice. Nessun font web (funziona offline).
- Forma e movimento: raggi 10–20px, ombra leggera sul composer, transizioni 150ms, comparsa dei messaggi 180ms; con
prefers-reduced-motiontutto fermo. - Barra laterale: sfondo
--muted, 272px; voce attiva con barra d'accento a sinistra; ✕ visibile solo al passaggio/focus. - Accessibilità: focus sempre visibile,
aria-labelsui pulsanti a sola icona,role=log/aria-livesulla conversazione,role=statussull'attesa, target ≥ 36px, nessuna emoji come icona. - Non incluso: evidenziazione sintattica del codice (richiederebbe una libreria da incorporare).
Scelte tecniche
- .NET 10 (WinForms).
Program.cs(CLI + avvio) eGui.cs(chat). Dipendenze NuGet solo per la GUI:Markdig(markdown→HTML) eMicrosoft.Web.WebView2(richiede il runtime WebView2, già presente su Windows 11). ProcessStartInfo.ArgumentListper passare il prompt (niente problemi di quoting).claudeè un .exe nativo (~/.local/bin): si avvia direttamente, senzacmd /c. Lo stdin è chiuso subito per evitare l'attesa di 3s di claude.- Working dir:
AppContext.BaseDirectory, non la cwd del chiamante.
Fuori scope (MVP)
Streaming token-per-token nella GUI, annullamento di una risposta in corso, rinomina/ricerca delle chat, config file, logging su file, test automatici, supporto Linux/macOS.
Criteri di accettazione
- Eseguito da un'altra cartella,
claudeparte nella cartella dell'exe (verificabile chiedendo "pwd"). - L'output appare in streaming, non solo alla fine.
- L'exit code è propagato.
- Un prompt con virgolette e spazi arriva intatto.
Domande aperte
- Permessi: serve
--allowedToolso--permission-modeper l'uso non interattivo? - Formato output: testo semplice o
--output-format stream-json?