diff --git a/CLAUDE.md b/CLAUDE.md index 994d532..831ecda 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -24,11 +24,12 @@ agent-wiki-ui (già `LlmRun`; il nome ha un'unica sorgente, vedi [docs/README.md Decisioni e motivazioni negli ADR ([docs/adr/](docs/adr/)); qui solo la mappa del codice. - [Program.cs](Program.cs): top-level statements. Con argomenti è la CLI (`EngineRunner.RunInherited`, motore scelto con `--engine ` come primo argomento, default Claude Code); senza avvia Avalonia (`App`). -- [Core/Engine.cs](Core/Engine.cs): astrazione "motore" (ADR-0002): `Engine` (id, nome, eseguibile, modelli, effort, `BuildArgs`, `Parse`), `ClaudeEngine`, `CodexEngine`, registro `Engines` e ricerca nel PATH (`PathSearch`). Gli elenchi modelli sono fissi, da aggiornare quando ne escono di nuovi. -- [Core/EngineRunner.cs](Core/EngineRunner.cs): **unico punto di avvio dei motori** (cartella dell'exe, stdin chiuso, `ArgumentList`), CLI con output ereditato e GUI con output catturato e interpretato da `Engine.Parse`. +- [Core/Engine.cs](Core/Engine.cs): astrazione "motore" (ADR-0002): `Engine` (id, nome, eseguibile, modelli, effort, `BuildArgs`, `Parse`, `FetchLatestVersionAsync` per il controllo delle versioni), `ClaudeEngine`, `CodexEngine`, registro `Engines` e ricerca nel PATH (`PathSearch`). Gli elenchi modelli sono fissi, da aggiornare quando ne escono di nuovi. +- [Core/EngineRunner.cs](Core/EngineRunner.cs): **unico punto di avvio dei motori** (cartella dell'exe, stdin chiuso, `ArgumentList`): CLI con output ereditato, GUI con output catturato e interpretato da `Engine.Parse`, e `RunToolAsync` per i comandi di servizio (`--version`, `update`) con tempo massimo. +- [Core/EngineUpdater.cs](Core/EngineUpdater.cs): controllo delle versioni in sola lettura (`CheckAsync`, `IsNewer`) e aggiornamento di un CLI con il suo `update` ufficiale (ADR-0004): versione prima/dopo, esito leggibile, metodo di installazione dedotto dal percorso reale e comando manuale da suggerire (l'app lo mostra, non lo esegue). - [Core/ChatStore.cs](Core/ChatStore.cs): cronologia, un JSON per chat in `//chats/.json` (nome file = id della chat, validato come Guid prima dell'uso; campi `Engine` e `SessionId` opzionali: i file vecchi sono chat di Claude Code). - [Core/AppInfo.cs](Core/AppInfo.cs): nome dell'app, letto dall'`AssemblyName` del csproj (unica sorgente). -- [Gui/MainViewModel.cs](Gui/MainViewModel.cs): stato e logica della chat (invio, apertura/eliminazione, salvataggio subito all'invio e di nuovo alla risposta, riapertura dell'ultima chat all'avvio, interruttore CLI: cambiare motore apre una nuova chat). +- [Gui/MainViewModel.cs](Gui/MainViewModel.cs): stato e logica della chat (invio, apertura/eliminazione, salvataggio subito all'invio e di nuovo alla risposta, riapertura dell'ultima chat all'avvio, interruttore CLI: cambiare motore apre una nuova chat; aggiornamento dei CLI con un solo aggiornamento alla volta, bloccando invio e cambio motore). - [Gui/MainWindow.axaml](Gui/MainWindow.axaml) (+ `.cs`): layout XAML con binding compilati. [Gui/App.axaml](Gui/App.axaml): token di colore come risorse di tema chiaro/scuro e stili; specifiche in `docs/MVP.md` → "Design (UI/UX)". Cambiando l'aspetto aggiornare quella sezione. - [Gui/MarkdownView.cs](Gui/MarkdownView.cs): renderer markdown proprio (albero Markdig → controlli Avalonia, `DisableHtml`). - Dipendenze NuGet: Avalonia (+ Desktop, Themes.Fluent), Markdig. @@ -39,6 +40,8 @@ Decisioni e motivazioni negli ADR ([docs/adr/](docs/adr/)); qui solo la mappa de - Lo stdin del motore va redirezionato e chiuso subito: `claude` aspetta 3 s, `codex` stampa "Reading additional input from stdin…" e attende. - `claude` è un eseguibile nativo (`~/.local/bin`), avviabile direttamente. Se fosse `claude.cmd` (Windows) serve `cmd.exe /c`. - Codex (`CodexEngine`): l'id di sessione lo genera Codex e si legge da `thread.started` nell'output JSONL (`--json`); la risposta è l'ultimo `agent_message`; `exec resume` non accetta `-s`, la sola lettura si impone con `-c 'sandbox_mode="read-only"'`; serve `--skip-git-repo-check` (la cartella dell'exe non è un repository); `--` prima del prompt. Dettagli e prove in ADR-0002. Il modello predefinito è quello di `~/.codex/config.toml`: se richiede un Codex più recente l'errore compare nella chat (vedi docs/INSTALL.md). +- Il controllo delle versioni contatta solo `downloads.claude.ai` (Claude Code) e `api.github.com` (Codex, serve lo User-Agent), al massimo ogni 5 minuti e in background; un errore di rete non è un errore dell'app (stato "sconosciuto"). +- L'aggiornamento di un CLI è sempre un gesto esplicito dell'utente (pulsante Aggiorna): nessun aggiornamento automatico e nessun fallback che esegua npm/brew/winget al posto suo. - Il nome dell'app non va scritto a mano nel codice: usare `AppInfo.Name`. Cambiare nome cambia la cartella dati (vedi checklist in docs/README.md; `ChatStore` migra le cartelle dei nomi precedenti). - Avalonia 12: la clipboard si usa con `using Avalonia.Input.Platform;` (`SetTextAsync` è un'estensione); `TextBox.Watermark` è obsoleto, usare `PlaceholderText`. - Nel renderer markdown i colori si risolvono al momento del rendering (`TryFindResource` con `ActualThemeVariant`) e la vista si ricostruisce al cambio di tema. @@ -47,7 +50,7 @@ Decisioni e motivazioni negli ADR ([docs/adr/](docs/adr/)); qui solo la mappa de ## Skill del repo -`.claude/settings.json` abilita il plugin `ui-ux-pro-max` (marketplace `nextlevelbuilder/ui-ux-pro-max-skill`). Usarlo per le modifiche alla GUI WinForms (layout, usabilità, accessibilità). +`.claude/settings.json` abilita il plugin `ui-ux-pro-max` (marketplace `nextlevelbuilder/ui-ux-pro-max-skill`). Usarlo per le modifiche alla GUI Avalonia (layout, usabilità, accessibilità). ## Commit diff --git a/README.md b/README.md index d1bc60b..61966bf 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Eseguibile C# (.NET 10, Windows e Linux) che lancia Claude Code (`claude -p`) o ## Uso -GUI (nessun argomento): finestra di chat in stile claude.ai, con la conversazione in markdown in alto e il campo messaggio in basso (`Invio` invia, `Maiusc+Invio` va a capo, "Nuova chat" azzera il contesto). Il riquadro **Impostazioni** (in alto a destra) permette di scegliere la **CLI** (Claude Code o Codex), il **modello** e l'**effort** di ragionamento ("Predefinito" = nessun flag, vale la config del motore). Le chat si salvano su disco e l'ultima si riapre all'avvio. +GUI (nessun argomento): finestra di chat in stile claude.ai, con la conversazione in markdown in alto e il campo messaggio in basso (`Invio` invia, `Maiusc+Invio` va a capo, "Nuova chat" azzera il contesto). Il riquadro **Impostazioni** (in alto a destra) permette di scegliere la **CLI** (Claude Code o Codex), il **modello** e l'**effort** di ragionamento ("Predefinito" = nessun flag, vale la config del motore). Le chat si salvano su disco e l'ultima si riapre all'avvio. Sempre nel riquadro, la sezione **Aggiornamenti** dice se Claude Code e Codex hanno una versione nuova (pulsante evidenziato) e li aggiorna uno alla volta. ``` dotnet run ``` diff --git a/docs/INSTALL.md b/docs/INSTALL.md index 18ccbc4..075b315 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -73,6 +73,18 @@ codex exec --help # uso non interattivo, usato dall'app **Problema noto: "requires a newer version of Codex".** Se il modello predefinito del tuo `~/.codex/config.toml` richiede un Codex più recente, `codex exec` fallisce con quell'errore (l'app lo mostra nella chat). Aggiorna con `codex update`, oppure scegli un altro modello nelle Impostazioni dell'app. Controllato su questa macchina il 2026-10-15 con Codex 0.144.3. +## Aggiornare un CLI dall'app +Nel riquadro Impostazioni, sezione **Aggiornamenti**, l'app controlla da sola, all'avvio e all'apertura del riquadro, se c'è una versione più recente (Claude Code: `downloads.claude.ai`; Codex: release di GitHub): se c'è, la riga dice "Aggiornamento disponibile", il pulsante **Aggiorna** si evidenzia e compare un puntino sul pulsante Impostazioni. Il pulsante esegue `claude update` o `codex update` (controllano e installano solo se c'è una versione nuova). Nulla si aggiorna senza un clic. Se non va a buon fine l'app mostra l'errore e il comando manuale, che dipende da come hai installato il CLI: + +| Installazione | Claude Code | Codex | +|---|---|---| +| npm | `npm install -g @anthropic-ai/claude-code@latest` | `npm install -g @openai/codex@latest` | +| Homebrew | `brew upgrade claude-code` | `brew upgrade --cask codex` | +| WinGet | `winget upgrade Anthropic.ClaudeCode` | reinstalla con lo script PowerShell | +| Nativo / script | `claude update` | `codex update` (o rilancia lo script di installazione) | + +Dettagli e decisioni: [ADR-0004](adr/0004-aggiornamento-dei-cli.md). + ## Controllo finale per l'app Dallo stesso terminale da cui avvii l'app: ``` diff --git a/docs/MVP.md b/docs/MVP.md index 78ba70d..97d60c0 100644 --- a/docs/MVP.md +++ b/docs/MVP.md @@ -25,6 +25,7 @@ Senza argomenti si apre una finestra Avalonia (C# + XAML, nessun HTML) in stile - **Schermata iniziale** (chat vuota): solo logo, titolo e sottotitolo (nessun suggerimento precompilato). - **Composer** (in basso, scheda arrotondata): campo di testo che cresce fino a ~8 righe e pulsante Invia circolare con icona. `Invio` invia, `Maiusc+Invio` va a capo. Hint sulla tastiera sotto la scheda. Non contiene più i selettori di modello ed effort. - **Impostazioni** (in alto a destra, pulsante con icona a cursori, simmetrico a quello della barra laterale): aprono un riquadro a comparsa con l'interruttore **CLI** (Claude Code, Codex; un motore non installato è mostrato "non installato" e non è selezionabile; disabilitato durante l'attesa), poi **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). Si chiude con `Esc` o con un clic fuori; le scelte restano valide per i messaggi successivi (non sono ancora salvate su disco). Passati come `--model` / `--effort`; con "Predefinito" il flag viene omesso. Solo GUI. +- **Aggiornamenti** (nello stesso riquadro, sotto le impostazioni): una riga per ogni CLI installato con nome, versione e pulsante **Aggiorna**. L'app **controlla prima** se esiste una versione più recente (all'avvio, all'apertura del riquadro se l'ultimo controllo ha più di 5 minuti, e dopo ogni aggiornamento): se c'è, la riga dice "Aggiornamento disponibile: X", il pulsante è evidenziato e un puntino compare sul pulsante Impostazioni; se no, la riga dice "Aggiornato" e nulla è evidenziato; senza rete "Controllo non riuscito". Il pulsante esegue il comando ufficiale ` update` e mostra l'esito sulla riga: "Aggiornato da X a Y", "Già aggiornato (X)" o l'errore con il comando manuale adatto al metodo di installazione (npm, Homebrew, WinGet, nativo). Un solo aggiornamento alla volta; i pulsanti sono disabilitati durante una risposta. Nessun aggiornamento parte da solo. Vedi [ADR-0004](adr/0004-aggiornamento-dei-cli.md). - **Motori** ([ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md)): una chat usa un solo motore. Cambiare **CLI** apre una nuova chat con quel motore; modello ed effort tornano a "Predefinito" e le liste dipendono dal motore (Codex: modelli GPT-5.6 Sol/Terra/Luna, stessi livelli di effort). Aprendo una chat salvata il selettore passa al motore di quella chat. L'etichetta sulle risposte, il segnaposto del composer e il messaggio d'attesa usano il nome del motore, e l'elenco chat lo indica sotto il titolo. Con Codex la sessione è letta dall'output (`thread_id`) e usata per riprendere la chat; i permessi sono di sola lettura. - **Contesto**: `claude -p --session-id ` al primo messaggio, `--resume ` ai successivi. "Nuova chat" svuota la vista e genera un nuovo guid. - **Cronologia**: dopo ogni scambio la chat è salvata in `//chats/.json`, dove la cartella dati è `Environment.SpecialFolder.LocalApplicationData` (`%LOCALAPPDATA%` su Windows, `~/.local/share` su Linux; vedi [Nome del progetto](README.md#nome-del-progetto)). Il file contiene guid (= id di sessione di `claude`), titolo, data, flag `started`, messaggi con ruolo e testo, motore (`Engine`) e id di sessione del motore (`SessionId`); i file senza questi campi sono chat di Claude Code. Riaprendo una chat si rimostra la conversazione e si prosegue con `--resume `, quindi `claude` ricorda 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 di `claude`. Non si può aprire/eliminare una chat mentre Claude sta rispondendo. diff --git a/docs/README.md b/docs/README.md index 57784a6..a0c2484 100644 --- a/docs/README.md +++ b/docs/README.md @@ -39,3 +39,4 @@ Se il nome cambia di nuovo, aggiornare solo questi punti: | [0001](adr/0001-gui-multipiattaforma-avalonia.md) | GUI multipiattaforma in solo C# con Avalonia | Accettata | | [0002](adr/0002-motori-cli-claude-codex-antigravity.md) | Più motori CLI (Claude Code, Codex, Antigravity) su Windows e Linux | Accettata (Claude Code, Codex) | | [0003](adr/0003-strategia-di-test.md) | Strategia di test e regola "nuova funzione = nuovi test" | Proposta | +| [0004](adr/0004-aggiornamento-dei-cli.md) | Aggiornamento dei CLI dalle Impostazioni | Proposta | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 2625113..17e5f47 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -26,7 +26,7 @@ Dettagli dell'MVP in [MVP.md](MVP.md). - [ ] Test automatici e CI (vedi Fase 6) ## Fase 4 — Più motori (Claude Code, Codex, Antigravity) -Decisione, tabella dei comandi e rischi: [ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md). Qui solo i passi. Va fatta dopo (o insieme a) la Fase 5, che estrae il punto unico di avvio processo. +Decisione, tabella dei comandi e rischi: [ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md). Qui solo i passi. Il punto unico di avvio processo l'ha estratto la Fase 5. - [x] Verificare i flag reali di `codex` (provati su Linux/WSL, ADR-0002 accettata per Claude Code e Codex); `agy` e Windows da verificare - [x] Astrazione "motore" in C# (`Core/Engine.cs`, `Core/EngineRunner.cs`) @@ -39,6 +39,9 @@ Decisione, tabella dei comandi e rischi: [ADR-0002](adr/0002-motori-cli-claude-c - [x] Permessi di Codex in sola lettura (nessun flag che salta i controlli) - [ ] Opzione esplicita per concedere permessi di scrittura - [x] CLI: opzione `--engine`, coerente con la GUI +- [x] Aggiornamento dei CLI dalle Impostazioni, un pulsante per CLI con `update` ufficiale ed esito leggibile ([ADR-0004](adr/0004-aggiornamento-dei-cli.md)) +- [x] Controllo preventivo della versione più recente (sorgenti ufficiali, cache di 5 minuti); pulsante Aggiorna evidenziato e puntino sul pulsante Impostazioni solo se c'è una versione nuova +- [ ] Provare gli aggiornamenti reali (finora provati solo con CLI finti, e rilevamento del metodo di installazione sui percorsi reali) su Linux e Windows (installazione nativa e npm) e portare l'ADR-0004 ad "Accettata" ## Fase 5 — Multipiattaforma (Windows + Linux), GUI in solo C# Decisione e motivazioni: [ADR-0001](adr/0001-gui-multipiattaforma-avalonia.md). Qui solo i passi. diff --git a/docs/TESTING.md b/docs/TESTING.md index 1ada4d2..c74b1f9 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -32,6 +32,7 @@ tests/FakeClaude/ eseguibile finto: registra argv/cwd/stdin; testo, ## Predisposizioni di testabilità (prerequisito dei test) - [ ] `ChatStore` come istanza con cartella iniettata (oggi statico su `LocalApplicationData`) - [ ] `IEngineRunner` dietro `Core/EngineRunner.cs`, iniettato nel `MainViewModel` +- [x] `EngineUpdater.CheckAsync` accetta un `HttpClient`: i test usano un gestore finto (rete assente, risposta non valida, versioni di prova) - [ ] `InternalsVisibleTo` verso il progetto di test - [ ] Spostamento in `src/` e `tests/` con soluzione unica @@ -82,6 +83,31 @@ Le righe CL valgono per ogni motore; queste sono specifiche di Codex (ADR-0002). | CX-11 | In modalità CLI (output ereditato) non si usa `--json` | U | `Codex_cli_senza_json` | [ ] | | CX-12 | Con un `FakeClaude` configurato come Codex il ciclo primo messaggio + ripresa funziona (id letto e riusato) | P | `Codex_ciclo_completo_con_finto` | [ ] | +### Aggiornamento dei CLI — `Core/EngineUpdater.cs`, `Gui/MainViewModel.cs` (ADR-0004) +Si provano con `FakeClaude`/`FakeCodex` che rispondono a `--version` e `update`; nessun test aggiorna un CLI reale. +| ID | Requisito | Liv. | Test | | +|---|---|---|---|---| +| UP-01 | Versione letta da `--version` (prima riga ripulita); eseguibile assente ⇒ versione nulla | P | `Versione_letta` | [ ] | +| UP-02 | Versione cambiata dopo `update` ⇒ esito "Aggiornato da X a Y" | P | `Aggiornato` | [ ] | +| UP-03 | Exit 0 e versione invariata ⇒ "Già aggiornato (X)" | P | `Gia_aggiornato` | [ ] | +| UP-04 | Exit ≠ 0 ⇒ errore con le ultime righe dell'output e il comando manuale | P | `Aggiornamento_fallito` | [ ] | +| UP-05 | Tempo massimo superato ⇒ processo terminato ed errore chiaro | P | `Aggiornamento_scaduto` | [ ] | +| UP-06 | Eseguibile assente ⇒ riga non offerta / errore "non trovato" | U | `Aggiornamento_senza_eseguibile` | [ ] | +| UP-07 | Metodo di installazione dedotto dal percorso reale (npm, Homebrew, WinGet, nativo) con collegamenti simbolici risolti | U | `Metodo_di_installazione` | [ ] | +| UP-08 | Comando manuale corretto per ogni coppia motore × metodo (tabella dell'ADR-0004) | U | `Comando_manuale` | [ ] | +| UP-09 | Un solo aggiornamento alla volta; durante una risposta in corso i pulsanti sono disabilitati e la chat non parte durante l'aggiornamento | U | `Aggiornamento_esclusivo` | [ ] | +| UP-10 | Dopo un aggiornamento riuscito le versioni mostrate si rileggono; la chat aperta resta invariata | U | `Versioni_rilette` | [ ] | +| UP-11 | Le righe di aggiornamento compaiono solo per i CLI installati | U/G | `Righe_solo_installati` | [ ] | +| UP-12 | Il pulsante Aggiorna del riquadro agisce solo sul CLI della sua riga | G | `Pulsante_per_riga` | [ ] | +| UP-13 | Nessun aggiornamento parte senza un clic | U | `Nessun_aggiornamento_automatico` | [ ] | +| UP-14 | Claude Code: l'ultima versione si legge dal testo di `…/claude-code-releases/latest` (spazi e a capo ripuliti) | U | `Ultima_versione_claude` | [ ] | +| UP-15 | Codex: l'ultima versione si legge da `tag_name` della release GitHub (`rust-v0.162.1` ⇒ 0.162.1); intestazione User-Agent inviata | U | `Ultima_versione_codex` | [ ] | +| UP-16 | Confronto: ultima > installata ⇒ disponibile; uguale o più nuova ⇒ aggiornato; versioni con testo attorno (`2.1.300 (Claude Code)`, `codex-cli 0.162.1`) lette bene | U | `Confronto_versioni` | [ ] | +| UP-17 | Rete assente, risposta non valida o timeout ⇒ stato "sconosciuto", nessuna eccezione, pulsante normale | U | `Controllo_senza_rete` | [ ] | +| UP-18 | Disponibile ⇒ riga con "Aggiornamento disponibile: X", pulsante evidenziato e puntino sul pulsante Impostazioni; aggiornato ⇒ nessuno dei tre | U/G | `Evidenziazione_aggiornamento` | [ ] | +| UP-19 | Il controllo parte all'avvio e all'apertura del riquadro solo se l'ultimo ha più di 5 minuti; dopo un aggiornamento riuscito si ripete; non blocca invio né interfaccia | U | `Quando_si_controlla` | [ ] | +| UP-20 | Il controllo non installa nulla e non modifica i CLI | P | `Controllo_in_sola_lettura` | [ ] | + ### Cronologia — `Core/ChatStore.cs`, `Core/AppInfo.cs` | ID | Requisito | Liv. | Test | | |---|---|---|---|---| diff --git a/docs/adr/0002-motori-cli-claude-codex-antigravity.md b/docs/adr/0002-motori-cli-claude-codex-antigravity.md index 1782fd0..d25f3a2 100644 --- a/docs/adr/0002-motori-cli-claude-codex-antigravity.md +++ b/docs/adr/0002-motori-cli-claude-codex-antigravity.md @@ -14,7 +14,7 @@ L'app lancia solo `claude -p`. Si vuole usare dalla stessa chat anche **OpenAI C 4. Nessuna scorciatoia che disattivi i controlli di permesso senza una scelta esplicita dell'utente. ## Decisione -- **Astrazione "motore"** in C#. Ogni motore descrive: nome e eseguibile, argomenti per il primo messaggio e per la ripresa, elenco modelli, supporto effort, modo per ottenere l'id di sessione. `RunClaude` diventa `RunEngine(engine, …)`. +- **Astrazione "motore"** in C#. Ogni motore descrive: nome e eseguibile, argomenti per il primo messaggio e per la ripresa, elenco modelli, supporto effort, modo per ottenere l'id di sessione. `RunClaude` è diventato `EngineRunner` con il motore come parametro. - **Rilevamento** dei motori cercando l'eseguibile nel PATH (su Windows anche le estensioni di PATHEXT). I motori assenti sono disabilitati nelle Impostazioni, con messaggio chiaro; la CLI esce con 127 come oggi. - **Una chat = un motore**. Il file JSON della chat memorizza il motore; il contesto di sessione non passa da un motore all'altro, quindi cambiare motore apre una nuova chat. - **Impostazioni** nella GUI: interruttore **CLI** (motore attivo), modello, effort. Cambiare motore apre una nuova chat; modello ed effort tornano a "Predefinito" e le liste sono quelle del motore. La persistenza su disco delle impostazioni è rinviata. La CLI ottiene `--engine ` (`claude`, `codex`). diff --git a/docs/adr/0004-aggiornamento-dei-cli.md b/docs/adr/0004-aggiornamento-dei-cli.md new file mode 100644 index 0000000..a368dcb --- /dev/null +++ b/docs/adr/0004-aggiornamento-dei-cli.md @@ -0,0 +1,72 @@ +# ADR-0004 — Aggiornamento dei CLI dalle Impostazioni + +- **Stato**: Proposta +- **Data**: 2026-10-15 +- **Collegato a**: [ADR-0002](0002-motori-cli-claude-codex-antigravity.md) (motori), [INSTALL.md](../INSTALL.md) + +## Contesto +L'app usa i CLI già installati dall'utente. Un CLI vecchio può smettere di funzionare senza che l'app c'entri: su questa macchina Codex 0.144.3 ha come modello predefinito (`~/.codex/config.toml`) `gpt-6-astra`, che richiede un Codex più recente, e ogni messaggio con "Predefinito" fallisce con `requires a newer version of Codex`. L'unico rimedio è aggiornare il CLI, oggi da terminale, e l'utente non sa nemmeno che esiste una versione nuova. + +Sulla macchina di sviluppo il 2026-10-15 i due CLI sono stati poi aggiornati (Codex 0.144.3 → 0.162.1, Claude Code 2.1.252 → 2.1.300) e Codex con il modello predefinito risponde: il problema si risolve aggiornando. Resta il bisogno di saperlo e di farlo dall'app. + +Entrambi i CLI hanno un comando di aggiornamento che controlla e, se c'è una versione nuova, la installa: `claude update` (alias `upgrade`: "Check for updates and install if available") e `codex update` ("Update Codex to the latest version"). Verificato con `--help` il 2026-10-15; l'esecuzione reale non è ancora stata provata. + +## Criteri di decisione +1. L'app dice se c'è una versione nuova **prima** di proporre l'aggiornamento: evidenzia solo dove serve. +1. L'utente aggiorna un CLI alla volta, senza uscire dall'app. +2. L'app non tocca nulla senza un gesto esplicito (nessun aggiornamento automatico). +3. Si usa il comando ufficiale del CLI, non una procedura inventata dall'app. +4. Se l'aggiornamento non è possibile (installazione via gestore di pacchetti, permessi, rete) l'utente vede il motivo e il comando manuale giusto. +5. Funziona su Windows e Linux e non blocca la GUI. + +## Decisione +- Nel riquadro Impostazioni, sezione **Aggiornamenti**: una riga per ogni CLI installato con nome, versione e pulsante **Aggiorna**. Ogni pulsante agisce solo su quel CLI. +- **Controllo prima dell'aggiornamento** (sola lettura, nessuna installazione): l'app confronta la versione installata (` --version`) con l'ultima pubblicata. + - Claude Code: `GET https://downloads.claude.ai/claude-code-releases/latest`, testo con la versione (la stessa sorgente dell'installer nativo). + - Codex: `GET https://api.github.com/repos/openai/codex/releases/latest`, campo `tag_name` (es. `rust-v0.162.1`, si estrae `0.162.1`). + - Le versioni si ricavano con il primo numero `N.N.N` del testo e si confrontano come versioni. Verificate il 2026-10-15: restituivano 2.1.300 e 0.162.1. + - **Disponibile** (ultima > installata): la riga mostra "Aggiornamento disponibile: X", il pulsante Aggiorna è **evidenziato** (colore d'accento) e un puntino d'accento compare sul pulsante Impostazioni. **Aggiornato** (uguale o più nuova): riga neutra con "Aggiornato", pulsante normale, nessun puntino; resta cliccabile per forzare `update`. **Sconosciuto** (rete assente, risposta non valida, timeout di 10 s): "Controllo non riuscito", pulsante normale, nessun errore invasivo. + - Quando si controlla: all'avvio, in background, e ogni volta che si apre il riquadro Impostazioni se l'ultimo controllo ha più di 5 minuti; subito dopo un aggiornamento. Mai in modo da rallentare la chat. Le richieste non inviano identificativi; l'app contatta solo i due indirizzi sopra. +- **Flusso** di un clic: legge la versione (` --version`), lancia ` update` (stdin chiuso, output catturato, tempo massimo 10 minuti), rilegge la versione e mostra l'esito sulla riga: + - versione cambiata ⇒ "Aggiornato da X a Y"; + - exit 0 e versione invariata ⇒ "Già aggiornato (X)"; + - exit ≠ 0, tempo scaduto o eseguibile assente ⇒ errore con le ultime righe dell'output e il **comando manuale** adatto (sotto). +- Pulsanti disabilitati mentre una risposta è in corso o un altro aggiornamento è attivo; la chat non parte durante l'aggiornamento. +- **Comando manuale** scelto dal metodo di installazione, dedotto dal percorso reale dell'eseguibile (collegamenti simbolici risolti): + + | Percorso contiene | Metodo | Claude Code | Codex | + |---|---|---|---| + | `node_modules`, `.npm` | npm | `npm install -g @anthropic-ai/claude-code@latest` | `npm install -g @openai/codex@latest` | + | `Cellar`, `homebrew`, `linuxbrew` | Homebrew | `brew upgrade claude-code` (o `claude-code@latest`) | `brew upgrade --cask codex` | + | `WinGet`, `WindowsApps` | WinGet | `winget upgrade Anthropic.ClaudeCode` | script PowerShell di [INSTALL.md](../INSTALL.md) | + | altro | nativo/script | `claude update` da terminale | script di installazione di [INSTALL.md](../INSTALL.md) | + + L'app **mostra** il comando, non lo esegue: un'installazione globale con npm o un gestore di pacchetti può richiedere permessi o scelte che spettano all'utente. +- Dopo un aggiornamento riuscito le versioni mostrate si rileggono; la chat aperta non cambia. +- L'aggiornamento di un CLI non rilancia la chat in corso: l'eventuale errore di versione già mostrato resta nella cronologia. + +## Opzioni considerate +| Opzione | Esito | Motivo | +|---|---|---| +| Solo documentare `codex update` | Scartata | L'utente deve uscire dall'app e conoscere il comando. | +| Solo `update`, senza controllo preventivo | Scartata | Non dice se serve e non può evidenziare il pulsante: `update` controlla ma installa subito. | +| Controllo con `npm view` | Scartata | Richiede npm anche per installazioni native. | +| Aggiornamento automatico all'avvio | Scartata | Modifica programmi dell'utente senza un suo gesto. | +| Fallback automatico a npm/brew/winget | Scartata | Permessi (`sudo`), scelta del canale e rischi di installazioni doppie spettano all'utente. | +| **Controllo con le sorgenti ufficiali (annuncio delle versioni) + pulsante per CLI con `update` ufficiale + comando manuale suggerito** | **Scelta** | Ufficiale, sicura, uguale su Windows e Linux, evidenzia solo quando serve. | + +## Conseguenze +**Positive**: il caso "richiede un Codex più recente" si risolve dall'app; l'esito è comprensibile; nessuna azione nascosta. +**Negative / costi**: il controllo richiede rete e due indirizzi esterni (GitHub ha un limite di richieste anonime, per questo c'è la cache di 5 minuti); il canale scelto da Claude Code (`latest` o `stable`) può dare un "disponibile" che `claude update` non installa, e in quel caso l'esito dopo l'aggiornamento è "Già aggiornato"; l'esito dipende dal testo e dal comportamento dei comandi `update` dei CLI, che possono cambiare; su Windows l'aggiornamento di un eseguibile in uso da un'altra sessione può fallire; un controllo della versione senza installare non c'è. + +## Da verificare +- [ ] Il controllo evidenzia correttamente in tutti i casi (installato più vecchio, uguale, più nuovo di `latest`) e senza rete. +- [ ] Utente sul canale `stable` di Claude Code: il confronto con `latest` dà falsi "disponibile"; valutare di leggere anche `/stable` o `autoUpdatesChannel`. +- [ ] Esecuzione reale di `claude update` e `codex update` su Linux e Windows, con installazione nativa e con npm: esito, exit code e testo. (Su Linux/WSL i due CLI risultano aggiornati alle ultime versioni; non è stato registrato l'output.) +- [ ] Cosa stampano quando sono già aggiornati e quando falliscono per permessi. +- [ ] Che `--version` di entrambi si possa leggere in un secondo dopo l'aggiornamento (nessun file bloccato). +- [ ] Il tempo massimo di 10 minuti è adeguato (rete lenta). + +## Riferimenti +- Passi: [ROADMAP.md](../ROADMAP.md), Fase 4. +- Matrice dei test: [TESTING.md](../TESTING.md), righe UP.