Merge di main in benedetta: motore Antigravity sulla struttura attuale

Conflitti risolti in AGENT.md e README.md: percorsi src/agent-wiki-ui e
descrizione del renderer da main, AgyEngine e l'esempio --engine agy da benedetta.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
davideandClaude Sonnet 5.5 committed 2026-10-22 11:40:44 +02:00
commit dc7dbbd3bb
41 files changed
+1135 -40

No files matched your search

+12 -1
View File
@@ -8,7 +8,7 @@ Il motore va installato **nello stesso ambiente in cui gira l'app**:
| Dove avvii l'app | Dove installare il motore |
|---|---|
| Linux nativo | nel terminale Linux |
| WSL (anche con `dotnet run`) | nel terminale della distribuzione WSL |
| WSL (anche con `dotnet run --project src/agent-wiki-ui`) | nel terminale della distribuzione WSL |
| Windows (`.exe`) | in Windows (PowerShell), non in WSL |
Dentro WSL il PATH di Windows è visibile (compare `/mnt/c/…`): se in WSL non c'è un `claude`, `codex` o `agy`, il comando può risolvere nella versione Windows. Controllalo con `command -v claude`, `command -v codex` e `command -v agy`.
@@ -31,6 +31,17 @@ Dentro WSL il PATH di Windows è visibile (compare `/mnt/c/…`): se in WSL non
L'installazione nativa non mostra avanzamento durante il download. A fine installazione apri un **nuovo** terminale.
**Da zero su Linux, anche ARM64 (per esempio Raspberry Pi 5 con Raspberry Pi OS a 64 bit).** Il metodo nativo riconosce da solo l'architettura (`uname -m` deve dare `aarch64` o `x86_64`):
```
sudo apt install -y curl # se manca
curl -fsSL https://claude.ai/install.sh | bash
# apri un nuovo terminale, poi:
command -v claude # atteso: ~/.local/bin/claude
claude --version
claude # primo avvio: accesso nel browser
```
Su un Raspberry senza schermo l'accesso nel browser si fa da un altro dispositivo, seguendo l'indirizzo che `claude` stampa nel terminale (per esempio via SSH); in alternativa usa una API key o un provider cloud, vedi "Accesso". Se `~/.local/bin` non è nel PATH aggiungi `export PATH="$HOME/.local/bin:$PATH"` a `~/.bashrc`. Con la sola architettura ARM64 e Debian 10+ i requisiti sono soddisfatti; il metodo npm è l'alternativa se lo script nativo non funziona. Non provato su un Raspberry reale: le istruzioni vengono dalla guida ufficiale.
**Accesso**: esegui `claude` e segui le istruzioni nel browser. Serve un account Pro, Max, Team, Enterprise o Console (il piano gratuito di claude.ai non include Claude Code). In alternativa Amazon Bedrock, Google Cloud o Microsoft Foundry.
**Verifica**
+4 -4
View File
@@ -21,7 +21,7 @@ Senza argomenti si apre una finestra Avalonia (C# + XAML, nessun HTML) in stile
**Struttura della finestra**
- **Barra laterale** (sinistra, comprimibile con il pulsante a tre linee): logo e nome dell'app, 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. Le risposte sono renderizzate in markdown: titoli, liste, tabelle, citazioni, blocchi di codice, grassetto/corsivo/barrato, codice inline, link. Il rendering è un renderer proprio ([MarkdownView.cs](../Gui/MarkdownView.cs)) che trasforma l'albero di Markdig in controlli Avalonia; l'HTML grezzo è disabilitato. Il testo è selezionabile. Sulle risposte il pulsante **Copia** copia il markdown; sui blocchi di codice copia il codice.
- **Conversazione**: messaggi alternati utente/Claude. Le risposte sono renderizzate in markdown: titoli, liste, tabelle, citazioni, blocchi di codice, grassetto/corsivo/barrato, codice inline, link, immagini, formule LaTeX (`$…$` nel testo, `$$…$$` a blocco; [ADR-0008](adr/0008-formule-latex-nel-markdown.md); se il LaTeX non è valido compare il sorgente). Le immagini `![alt](percorso)` si mostrano se il percorso è locale (assoluto, `file://` o relativo: cercato nella cartella dell'exe e poi nelle cartelle superiori, cioè nella radice del progetto del motore) e il file è un'immagine leggibile entro i limiti (caricata in background: prima compare `[alt]`, poi l'immagine); altrimenti, e sempre per gli URL `http(s)`, compare il testo alternativo `[alt]` e non si scarica nulla ([ADR-0006](adr/0006-immagini-nel-markdown.md)). Il rendering è un renderer proprio ([MarkdownView.cs](../src/agent-wiki-ui/Gui/MarkdownView.cs)) che trasforma l'albero di Markdig in controlli Avalonia; l'HTML grezzo è disabilitato. Il testo è selezionabile. Sulle risposte il pulsante **Copia** copia il markdown; sui blocchi di codice copia il codice.
- **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, Antigravity; un motore non installato è mostrato "non installato" e non è selezionabile; disabilitato durante l'attesa), poi **Modello** (Predefinito, catalogo del motore, "Altro ID…" con campo libero) ed **Effort** (Predefinito, low, medium, high, xhigh, max). **Valori iniziali**: il modello più piccolo del motore (Claude Code: Haiku 5.5; Codex: GPT-5.6 Luna; Antigravity: Gemini 3.0 Flash Low) ed effort `low`, per risposte rapide e poco costose; sono quelli che si ritrovano all'avvio e a ogni cambio di CLI. 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.
@@ -35,7 +35,7 @@ Senza argomenti si apre una finestra Avalonia (C# + XAML, nessun HTML) in stile
- Finestra orizzontale con proporzioni 16:10: larga il 75% dell'area utile dello schermo (max 1440 px logici), altezza di conseguenza (max 90% dell'area utile), centrata; minimo 900x600. Su un monitor Full HD 1920x1080 risulta circa 1440x900.
### Design (UI/UX)
Sorgente di verità per l'aspetto, derivato dalla skill `ui-ux-pro-max` (stile "strumento per sviluppatori": slate + accento verde). I token vivono come risorse di tema in [Gui/App.axaml](../Gui/App.axaml).
Sorgente di verità per l'aspetto, derivato dalla skill `ui-ux-pro-max` (stile "strumento per sviluppatori": slate + accento verde). I token vivono come risorse di tema in [Gui/App.axaml](../src/agent-wiki-ui/Gui/App.axaml).
- **Tema**: segue il sistema (chiaro/scuro) tramite `RequestedThemeVariant="Default"`.
- **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**: font di sistema (Segoe UI Variable/Segoe UI su Windows; Inter, Ubuntu, Cantarell, Noto Sans, DejaVu Sans su Linux; corpo 15.5, titoli fino a 24), Cascadia Code/Consolas/DejaVu Sans Mono per il codice. Nessun font incorporato (funziona offline).
@@ -46,11 +46,11 @@ Sorgente di verità per l'aspetto, derivato dalla skill `ui-ux-pro-max` (stile "
## Scelte tecniche
Le scelte architetturali sono negli [ADR](README.md#indice-adr). Sintesi:
- .NET 10, un solo progetto `net10.0` per Windows e Linux. Avalonia (GUI), Markdig (parsing markdown). Il codice è diviso in `Core/` (avvio di `claude`, cronologia, nome app) e `Gui/` (finestra, ViewModel, renderer markdown); `Program.cs` sceglie CLI o GUI.
- .NET 10, un solo progetto `net10.0` per Windows e Linux. Avalonia (GUI), Markdig (parsing markdown), CSharpMath (formule LaTeX). Il codice è diviso in `Core/` (avvio di `claude`, cronologia, nome app) e `Gui/` (finestra, ViewModel, renderer markdown); `Program.cs` sceglie CLI o GUI.
- `ProcessStartInfo.ArgumentList` per passare il prompt (niente problemi di quoting).
- `claude` è un eseguibile nativo (`~/.local/bin`): si avvia direttamente, senza `cmd /c`. Lo stdin è chiuso subito per evitare l'attesa di 3s di claude.
- Working dir: `AppContext.BaseDirectory`, non la cwd del chiamante.
- L'avvio dei motori è in un solo punto ([Core/EngineRunner.cs](../Core/EngineRunner.cs)), usato da CLI e GUI; ogni motore descrive argomenti e parser in [Core/Engine.cs](../Core/Engine.cs).
- L'avvio dei motori è in un solo punto ([Core/EngineRunner.cs](../src/agent-wiki-ui/Core/EngineRunner.cs)), usato da CLI e GUI; ogni motore descrive argomenti e parser in [Core/Engine.cs](../src/agent-wiki-ui/Core/Engine.cs).
## Fuori scope (MVP)
Streaming token-per-token nella GUI, annullamento di una risposta in corso, rinomina/ricerca delle chat, config file, altri motori oltre a Claude Code, Codex e Antigravity ([ADR-0002](adr/0002-motori-cli-claude-codex-antigravity.md)), logging su file, supporto macOS. I test automatici sono pianificati: [TESTING.md](TESTING.md).
+3
View File
@@ -42,6 +42,9 @@ Da [MVP.md](MVP.md). Restano non spuntati finché non hanno test automatici (reg
| [0003](adr/0003-strategia-di-test.md) Strategia di test | Proposta |
| [0004](adr/0004-aggiornamento-dei-cli.md) Aggiornamento dei CLI | Proposta |
| [0005](adr/0005-windows-senza-console.md) Windows senza console | Proposta |
| [0006](adr/0006-immagini-nel-markdown.md) Immagini nel markdown (solo file locali) | Proposta (implementata, test verdi) |
| [0007](adr/0007-schema-wiki-per-query-con-immagini.md) Schema di una wiki interrogabile, con immagini | Proposta (solo documentazione; nessun codice) |
| [0008](adr/0008-formule-latex-nel-markdown.md) Formule LaTeX nel markdown | Proposta (implementata) |
## Per chiudere l'MVP
Proposta, derivata dai punti aperti sopra; da confermare.
+4 -1
View File
@@ -17,7 +17,7 @@ Indice e regole. Ogni informazione vive in **un solo documento**; gli altri la r
Il nome è **`agent-wiki-ui`** (prima `LlmRun`). Questa sezione è l'unica sorgente: gli altri doc scrivono "l'app" o `<nome-app>`.
Se il nome cambia di nuovo, aggiornare solo questi punti:
- [ ] `AssemblyName` in [../agent-wiki-ui.csproj](../agent-wiki-ui.csproj) (e il nome del file csproj): da lì il codice legge il nome (`Core/AppInfo.cs`) per titolo finestra, intestazione della barra laterale e cartella dati; l'eseguibile diventa `<nome-app>.exe` / `<nome-app>`
- [ ] `AssemblyName` in [../src/agent-wiki-ui/agent-wiki-ui.csproj](../src/agent-wiki-ui/agent-wiki-ui.csproj) (e il nome del file csproj): da lì il codice legge il nome (`Core/AppInfo.cs`) per titolo finestra, intestazione della barra laterale e cartella dati; l'eseguibile diventa `<nome-app>.exe` / `<nome-app>`
- [ ] `RootNamespace` e namespace C# (`AgentWikiUi.*`, anche in `x:Class` e `xmlns` dei file `.axaml`): il nome del namespace non può avere trattini
- [ ] **Cartella dati** `<LocalApplicationData>/<nome-app>/chats`: cambiando nome le chat salvate non si trovano più. `ChatStore` sposta una vecchia cartella (`LlmRun`) in quella nuova al primo avvio; per un nuovo cambio aggiungere il nome precedente all'elenco `LegacyNames`
- [ ] Titolo di [../README.md](../README.md) e riga iniziale di [../AGENT.md](../AGENT.md) (nome del csproj nei comandi)
@@ -52,3 +52,6 @@ Non si scrive nulla nei puntatori: ogni modifica va in AGENT.md. Per un altro st
| [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 |
| [0005](adr/0005-windows-senza-console.md) | Windows: eseguibile senza finestra di console | Proposta |
| [0006](adr/0006-immagini-nel-markdown.md) | Immagini nel markdown: solo file locali | Proposta |
| [0007](adr/0007-schema-wiki-per-query-con-immagini.md) | Schema di una wiki interrogabile, con immagini | Proposta |
| [0008](adr/0008-formule-latex-nel-markdown.md) | Formule LaTeX nel markdown | Proposta |
+5 -2
View File
@@ -22,6 +22,9 @@ Dettagli dell'MVP in [MVP.md](MVP.md). Stadio attuale e debiti: [PROJECT_STATE.m
## Fase 3 — Oltre
- [x] GUI: cronologia chat (salvataggio JSON, barra laterale, riapertura con `--resume`, eliminazione)
- [ ] GUI: streaming della risposta (`--output-format stream-json`), stop, rinomina/ricerca chat
- [x] GUI: immagini nel markdown da percorso locale, con ripiego sul testo alternativo ([ADR-0006](adr/0006-immagini-nel-markdown.md); test MD-14, MD-19…MD-30); immagini remote rimandate
- [x] Schema di una wiki interrogabile con immagini: convenzione per le istruzioni solo-query, l'indice e il catalogo ([ADR-0007](adr/0007-schema-wiki-per-query-con-immagini.md)); da provare con la chat reale su una wiki di esempio
- [x] GUI: formule LaTeX nel markdown, inline `$…$` e a blocco `$$…$$`, con ripiego sul sorgente ([ADR-0008](adr/0008-formule-latex-nel-markdown.md); test MD-31…MD-38)
- [ ] Config file accanto all'exe
- [ ] Test automatici e CI (vedi Fase 6)
@@ -65,9 +68,9 @@ Decisione e motivazioni: [ADR-0001](adr/0001-gui-multipiattaforma-avalonia.md).
Decisione: [ADR-0003](adr/0003-strategia-di-test.md). Piano, soglie e matrice: [TESTING.md](TESTING.md). Da questa fase in poi vale la regola: ogni nuova funzione arriva con i suoi test.
- [ ] Verificare le compatibilità dell'ADR-0003 (xUnit, Avalonia.Headless, coverlet, `dotnet test`) e portarlo ad "Accettata"
- [ ] Ristrutturare in `src/` e `tests/` con soluzione unica e `InternalsVisibleTo`
- [x] Ristrutturare in `src/` e `tests/` con soluzione unica e `InternalsVisibleTo`
- [ ] Predisposizioni di testabilità: `ChatStore` a istanza, `IClaudeRunner`
- [ ] `FakeClaude`: eseguibile finto per i test di processo
- [x] `FakeClaude`: eseguibile finto per i test di processo (`tests/FakeClaude`, prove FK-01…FK-05)
- [ ] Test `Core/` (CL, ST) e `MainViewModel` (VM)
- [ ] Test del renderer markdown (MD) e della GUI headless (UI)
- [ ] Test end-to-end e smoke di publish per `win-x64` e `linux-x64` (PUB)
+49 -6
View File
@@ -21,20 +21,34 @@ ADR (se serve) → MVP.md (requisito e criterio) → riga in questa matrice →
| **P** — processo | avvio reale di un processo (`Claude`) | `FakeClaude` nel PATH |
| **E** — end-to-end | l'eseguibile pubblicato (CLI, 127, exit code) | publish + processo figlio + `FakeClaude` |
## Struttura prevista
## Struttura
```
agent-wiki-ui.slnx
src/agent-wiki-ui/ app (Core/, Gui/, Program.cs)
tests/agent-wiki-ui.Tests/ U, R, G, P, E (Trait "Category": Unit | Gui | Process | E2E)
tests/FakeClaude/ eseguibile finto: registra argv/cwd/stdin; testo, stderr, exit code e ritardo da variabili d'ambiente
tests/agent-wiki-ui.Tests/ Support/ (TestApp, PngFactory), Gui/ (R, G), Process/ (P), Core/ (U, quando arrivano), E2E/ (E)
tests/FakeClaude/ eseguibile finto `claude` (vedi sotto)
```
Una cartella per livello, creata quando c'è il primo test; il livello si dichiara anche con `[Trait("Category", "Unit" | "Gui" | "Process" | "E2E")]`.
### FakeClaude
Piccolo eseguibile (`AssemblyName` `claude`, nel PATH dei test) che prende il posto del vero motore. Un test lo configura con variabili d'ambiente del processo che avvia:
| Variabile | Effetto |
|---|---|
| `FAKE_CLAUDE_LOG` | file in cui accoda una riga JSON per invocazione: `{"args": [...], "cwd": "...", "stdin": "..."}` |
| `FAKE_CLAUDE_OUT` | testo scritto su stdout (anche per `--version`) |
| `FAKE_CLAUDE_ERR` | testo scritto su stderr |
| `FAKE_CLAUDE_EXIT` | exit code (predefinito 0) |
| `FAKE_CLAUDE_DELAY_MS` | attesa prima di rispondere |
Legge stdin fino alla fine e lo registra: se il chiamante non lo chiude, il processo non termina (così CL-05 è verificabile). Il percorso del file compilato lo fornisce l'helper di test `FakeClaudePath`.
## 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
- [x] `InternalsVisibleTo` verso il progetto di test
- [x] Spostamento in `src/agent-wiki-ui/` e `tests/` con soluzione unica `agent-wiki-ui.slnx`
## Soglie di copertura (la build fallisce sotto soglia)
| Area | Righe | Rami |
@@ -181,7 +195,27 @@ Si provano con `FakeClaude`/`FakeCodex` che rispondono a `--version` e `update`;
| MD-11 | Link: colore d'accento e sottolineatura; il clic apre solo `http(s)` | R | `Link_http_https` | [ ] |
| MD-12 | Link: nessuna apertura se c'è una selezione in corso; cursore a mano sopra il link | R | `Link_con_selezione` | [ ] |
| MD-13 | Posizioni dei link corrette con enfasi, codice inline e a capo prima del link | R | `Intervalli_link` | [ ] |
| MD-14 | Immagini: si mostra il testo alternativo, non si scarica nulla | R | `Immagini_come_testo` | [ ] |
| MD-14 | Immagini con URL `http(s)` o altro schema: si mostra il testo alternativo, nessuna richiesta di rete | R | `Immagini_remote_come_testo` | [x] |
| MD-19 | Immagine con percorso assoluto di un file png valido ⇒ controllo immagine con le dimensioni attese (ADR-0006) | R | `Immagine_percorso_assoluto` | [x] |
| MD-20 | Percorso relativo risolto rispetto a `AppContext.BaseDirectory`; anche URI `file://` | R | `Immagine_percorso_relativo_e_file_uri` | [x] |
| MD-21 | File mancante, illeggibile, corrotto o con estensione non ammessa ⇒ testo alternativo, nessuna eccezione | R | `Immagine_non_valida_ripiego` | [x] |
| MD-22 | File oltre il limite di 20 MB ⇒ testo alternativo, file non decodificato | R | `Immagine_oltre_limite` | [x] |
| MD-23 | Immagine più larga della colonna ⇒ ridotta in larghezza mantenendo le proporzioni; mai ingrandita oltre l'originale | R | `Immagine_larghezza_massima` | [x] |
| MD-24 | Il testo alternativo è nome accessibile e suggerimento dell'immagine | R | `Immagine_testo_alternativo_accessibile` | [x] |
| MD-25 | Immagine dentro un link, in una lista o in una tabella ⇒ resa senza rompere intervalli dei link (MD-13) né il layout | R | `Immagine_in_contesti_annidati` | [x] |
| MD-26 | Cambio di tema con immagini presenti ⇒ vista ricostruita, immagini ancora mostrate | R | `Immagine_cambio_tema` | [x] |
| MD-27 | Il caricamento è in background: subito dopo il rendering c'è il segnaposto `[alt]` e un'immagine in attesa; a fine decodifica compare l'immagine; un file corrotto lascia `[alt]` | R | `Immagine_caricamento_in_background` | [x] |
| MD-28 | Percorso relativo non presente nella cartella dell'exe ma in una cartella superiore (radice del progetto, es. `<cartella>/<file>` con l'exe in una sottocartella) ⇒ immagine mostrata | R | `Immagine_percorso_relativo_alla_radice` | [x] |
| MD-29 | Se il file esiste in più cartelle, vince la più vicina all'exe; se non esiste in nessuna ⇒ testo alternativo | R | `Immagine_cartella_piu_vicina` | [x] |
| MD-30 | Risposta con immagine e, sotto, didascalia e link in markdown: l'immagine compare e la didascalia resta testo; l'app non legge nessun catalogo | R | `Immagine_risposta_con_didascalia` | [x] |
| MD-31 | Formula inline `$…$` ⇒ controllo formula inserito nel testo; il testo prima e dopo resta intatto e non sparisce più (ADR-0008) | R | `Formula_inline` | [x] |
| MD-32 | Formula a blocco `$$…$$` ⇒ controllo formula (non un blocco di codice col sorgente) | R | `Formula_a_blocco` | [x] |
| MD-33 | LaTeX non valido (inline e a blocco) ⇒ sorgente come testo, nessuna eccezione | R | `Formula_non_valida_ripiego` | [x] |
| MD-34 | Costrutti LaTeX comuni (frazioni, apici e pedici, `\mathbf`, `\overline`, `\times`, lettere greche, `\left…\right`, `\sum`) ⇒ nessun errore | R | `Costrutti_latex_comuni` | [x] |
| MD-35 | Cambio di tema con formule presenti ⇒ vista ricostruita, formule ancora mostrate | R | `Formula_cambio_tema` | [x] |
| MD-36 | Formula dentro una lista, una tabella o un link ⇒ resa senza rompere il layout né gli intervalli dei link (MD-13) | R | `Formula_in_contesti_annidati` | [x] |
| MD-37 | Il nome accessibile della formula è il suo sorgente LaTeX | R | `Formula_nome_accessibile` | [x] |
| MD-38 | Simboli `$` isolati (es. prezzi «10 $ e 20 $») restano testo, nessuna formula | R | `Dollari_isolati_restano_testo` | [x] |
| MD-15 | HTML grezzo disabilitato: i tag compaiono come testo, non come elementi | R | `Html_disabilitato` | [ ] |
| MD-16 | Cambio di tema (chiaro/scuro): i colori si aggiornano ricostruendo la vista | R | `Cambio_tema` | [ ] |
| MD-17 | Markdown malformato o blocchi sconosciuti non fanno crashare | R | `Markdown_malformato` | [ ] |
@@ -248,6 +282,15 @@ Per **ogni** motore, la stessa batteria di contratto:
| EN-17 | CLI `--engine <id> "prompt"`; id sconosciuto o prompt mancante ⇒ uso su stderr ed exit 2; senza `--engine` ⇒ Claude Code | E | [ ] |
| EN-18 | Rilevamento nel PATH: nome esatto su Linux, `.exe` su Windows; cartella inesistente nel PATH ignorata | U | [ ] |
### FakeClaude — `tests/FakeClaude`
| ID | Requisito | Liv. | Test | |
|---|---|---|---|---|
| FK-01 | Registra argomenti (anche con virgolette e spazi, uno per elemento), cartella di lavoro e stdin | P | `Registra_argomenti_cwd_e_stdin` | [x] |
| FK-02 | Scrive su stdout e stderr il testo configurato | P | `Output_e_stderr_configurati` | [x] |
| FK-03 | L'exit code configurato è quello del processo | P | `ExitCode_configurato` | [x] |
| FK-04 | Il ritardo configurato è rispettato | P | `Ritardo_configurato` | [x] |
| FK-05 | Termina solo dopo la chiusura di stdin | P | `Attende_la_chiusura_di_stdin` | [x] |
## Come si eseguono (a implementazione avvenuta)
Comandi previsti, da confermare con ADR-0003 → "Da verificare":
- Tutti i test: `dotnet test`
+47
View File
@@ -0,0 +1,47 @@
# ADR-0006 — Immagini nel markdown: solo file locali
- **Stato**: Proposta
- **Data**: 2026-10-15
- **Collegato a**: [ADR-0001](0001-gui-multipiattaforma-avalonia.md) (GUI), [ADR-0003](0003-strategia-di-test.md) (test)
## Contesto
Il renderer markdown ([Gui/MarkdownView.cs](../../src/agent-wiki-ui/Gui/MarkdownView.cs)) trasforma `![alt](percorso)` nel solo testo alternativo `[alt]`: non carica nulla. Un motore (Claude Code, Codex) può però produrre o citare un'immagine su disco (un grafico, uno screenshot) e rispondere con `![descrizione](percorso)`. Se il percorso è noto e il file esiste, l'utente si aspetta di vederla nella chat. Oggi l'app contatta la rete solo per il controllo delle versioni ([AGENT.md](../../AGENT.md), "Vincoli non ovvi"); aprire le immagini remote cambierebbe questo vincolo.
## Criteri di decisione
1. Mostrare l'immagine quando il percorso è locale e il file è leggibile.
2. Nessun accesso alla rete oltre a quello già previsto.
3. Nessun crash e nessun blocco della chat per file mancanti, enormi o corrotti: ripiego sul testo alternativo di oggi.
4. Coerenza con il resto del renderer: colori dal tema, testo selezionabile, accessibilità.
## Opzioni considerate
| Opzione | Esito | Motivo |
|---|---|---|
| Lasciare il testo `[alt]` | Scartata | Il percorso noto resta inutilizzato. |
| **Solo percorsi locali** | **Scelta** | Soddisfa il caso d'uso, nessuna rete, sola lettura. |
| Locali + URL `http(s)` | Rimandata | Una risposta del modello farebbe partire richieste di rete verso indirizzi scelti dal modello (tracciamento, indirizzo IP dell'utente esposto). Si può riprendere con un nuovo ADR e un consenso esplicito. |
## Decisione
- `![alt](destinazione)` mostra l'immagine se la destinazione è un **percorso locale**: assoluto, relativo oppure un URI `file://`.
- **Percorsi relativi: radice del progetto.** Il motore gira nella cartella dell'exe, ma le istruzioni che segue (il `CLAUDE.md` di una wiki) stanno di solito in una cartella superiore e scrivono i percorsi relativi a *quella* radice (esempio: una wiki con l'exe in `<wiki>/query/`, accanto a un `CLAUDE.md` solo-query, e istruzioni del tipo «`![Figura N](<cartella-immagini>/<file>)`, percorso relativo alla radice del repo»). Un percorso relativo si cerca quindi nella cartella dell'exe e poi in ciascuna cartella superiore, fino alla radice del filesystem; vince la prima in cui il file esiste (la più vicina all'exe).
- **Nessuno standard imposto (prototipo).** L'app non conosce né una cartella delle immagini né un formato di catalogo: la cartella delle immagini e il catalogo sono la convenzione di ciascuna wiki, non un requisito. Cartella e catalogo possono cambiare; la wiki lo dice al motore nelle sue istruzioni, e l'app mostra qualunque file locale raggiungibile con il percorso scritto nella risposta.
- **Cataloghi e metadati restano del motore.** Struttura tipo di una wiki con figure: un catalogo JSON con i metadati delle figure (file, didascalia stampata e generata, posizione, avvertenze) e una cartella delle immagini. Chi legge il catalogo e sceglie le figure è il motore, guidato dalle istruzioni della wiki; l'app non legge né interpreta il catalogo: mostra le immagini che la risposta cita in markdown, e la didascalia, la posizione e le avvertenze che il motore scrive sotto l'immagine sono testo markdown normale.
- Formati: png, jpg/jpeg, gif (primo fotogramma), webp, bmp. Altre estensioni ⇒ ripiego.
- Limiti: file fino a **20 MB**; se l'immagine è più larga di **1600 px** si decodifica a 1600 px di larghezza, per non occupare memoria inutile. In visualizzazione l'immagine non supera la larghezza della colonna dei messaggi (proporzioni mantenute) e non è mai ingrandita oltre l'originale.
- Un paragrafo fatto solo di immagini le mostra come blocchi; un'immagine dentro una riga di testo (o dentro un link) è inline, con larghezza massima di 480 px.
- **Ripiego** sul testo `[alt]` (come oggi) quando: la destinazione è `http(s)` o altro schema, il file manca o non è leggibile, il formato non è ammesso, il file supera il limite, la decodifica fallisce. Nessun errore a schermo, nessuna eccezione.
- **Caricamento in background**: al rendering un controllo di controllo rapido e sincrono (percorso, estensione, esistenza, dimensione) decide se l'immagine è plausibile; se no, compare subito `[alt]`. Se sì, compare `[alt]` come segnaposto e la decodifica avviene su un altro thread; a fine decodifica il segnaposto diventa l'immagine, se la decodifica fallisce resta `[alt]`. Così la GUI non si blocca con file grandi o dischi lenti.
- Accessibilità: l'immagine espone `alt` come nome per le tecnologie assistive e come suggerimento al passaggio del mouse.
- Nessuna azione al clic (non si apre il file). Il pulsante **Copia** della risposta continua a copiare il markdown originale.
## Conseguenze
**Positive**: le immagini prodotte dai motori si vedono nella chat; nessuna richiesta di rete in più; ripiego sicuro.
**Negative**: una risposta può far leggere e mostrare un file immagine qualsiasi dell'utente (sola lettura, solo a schermo, nessun invio altrove); le immagini remote restano non supportate e si vedono come testo.
## Da verificare
- Quali formati decodifica davvero Avalonia 12 con Skia su Windows e Linux (in particolare webp e gif) e se il limite di 20 MB è adeguato.
- Il comportamento dei percorsi Windows (`C:\…`, UNC) e di quelli WSL (`/mnt/c/…`).
- Se la ricerca nelle cartelle superiori debba fermarsi alla prima cartella con un marcatore di progetto (`CLAUDE.md`, `.git`) invece di salire fino alla radice del filesystem.
- Se e quando emergerà uno standard per cataloghi e cartelle delle immagini (nomi, campi minimi come file, didascalia, posizione), valutare un nuovo ADR; per ora è fuori dall'app.
## Riferimenti
- [MVP.md](../MVP.md) → "GUI (chat)"; matrice MD-14, MD-19…MD-30 in [TESTING.md](../TESTING.md).
@@ -0,0 +1,62 @@
# ADR-0007 — Schema di una wiki interrogabile, con immagini (prototipo)
- **Stato**: Proposta
- **Data**: 2026-10-15
- **Collegato a**: [ADR-0006](0006-immagini-nel-markdown.md) (l'app mostra le immagini citate nella risposta)
## Contesto
agent-wiki-ui è il terminale di chat per interrogare una wiki mantenuta da un motore (Claude Code, Codex), per esempio una wiki di studio o di documentazione tecnica: pagine markdown, un indice, un catalogo JSON delle figure con i metadati e una cartella di immagini. Per rispondere in modo strutturato — meglio di un RAG, che recupera frammenti di testo e perde le relazioni — il motore deve sapere **dove cercare** (indice → pagina → tabella o figura) e **come citare** un'immagine perché la chat la stampi. Oggi questo sapere sta in un file scritto per una sola wiki. Non esiste uno standard: la cartella delle immagini e il formato del catalogo cambiano da wiki a wiki, ed è un prototipo.
## Criteri di decisione
1. Un solo posto per i percorsi delle immagini, senza duplicarli a mano.
2. Le mappe sono derivate dal catalogo (si rigenerano), non scritte a mano.
3. Nessuna cartella o nome di file imposti: la wiki dichiara la sua struttura.
4. Degrado sicuro: un percorso sbagliato dà `[alt]` nella chat (ADR-0006), mai un errore.
5. Nessun codice nell'app: la logica sta nelle istruzioni al motore e negli script della wiki.
6. Pochi campi minimi, perché lo standard possa nascere dall'uso.
## Opzioni considerate
| Opzione | Esito | Motivo |
|---|---|---|
| Solo RAG sul testo (embedding di `search_text`) | Scartata come unica via | Perde la struttura che rende affidabile la risposta: dato → pagina unica, avvertenze, difetti noti, figura → file. Resta come ripiego (ricerca testuale). |
| L'app legge il catalogo e inserisce le immagini nelle risposte | Scartata | Lega l'app a un formato non standard e duplica la scelta delle figure, che il motore fa già leggendo la domanda. |
| **Schema in tre parti, letto dal motore; l'app stampa** | **Scelta** | Conserva la struttura, non tocca l'app, si adatta a wiki diverse. |
## Decisione
Una wiki interrogabile ha tre parti. Il formato è una convenzione consigliata, non un requisito dell'app.
1. **Indice** (`index.md`): catalogo delle pagine, una riga per pagina, `- [[pagina]] — sintesi`. Una pagina con figure aggiunge in coda gli id del catalogo: `· figure: fig-026, fig-027`. L'indice non elenca le figure una per una e non contiene percorsi di file.
2. **Catalogo delle figure** (JSON, nome e posizione dichiarati nello schema della wiki). Campi minimi per figura:
- `id` stabile, `label` (ad es. «Figura 26»);
- `caption_printed` (testo della fonte, alla lettera) e, a parte, ogni testo generato dall'immagine, contrassegnato come non verificato;
- `location` (sezione e riga nella fonte);
- `panels[]` con `file` e `path` (relativo alla cartella del catalogo) e una descrizione;
- `quality`: `flags` e `review_notes`.
Indici facoltativi per sezione, composto, tipo e concetto. L'ordine di preferenza per trovare una figura è: indice dell'indice → pagina → `figures` nel frontmatter → indici del catalogo → ricerca testuale.
3. **Istruzioni per le query**: un file `CLAUDE.md` (per Codex `AGENTS.md`) in una cartella dedicata sotto la radice della wiki, da cui si avvia l'app. Si aggiunge a quello della radice e prevale su di esso. Impone la **sola lettura**, instrada le domande per tipo verso l'indice giusto e fissa le regole per le immagini.
**Regole per le immagini nella risposta:**
- Il percorso si ricava da catalogo e `path`, si **verifica che esista** e si scrive **relativo alla radice** della wiki, senza `..` (l'app lo cerca nella sua cartella e poi nelle superiori, ADR-0006).
- Subito sotto: didascalia stampata alla lettera, posizione nella fonte, link alla nota della figura; una riga di avvertenza se `flags` o `review_notes`.
- Ciò che deriva dall'immagine (descrizioni, valori letti da un grafico) si dichiara non verificato. Massimo 3-4 immagini per risposta.
**Percorsi derivati.** Se il catalogo ha percorsi relativi alla propria cartella, la wiki genera con uno script una mappa figura → percorso dalla radice → pagine che la citano (per esempio colonne in più di `figure-indice.md`). Lo script, e un controllo in fase di lint che ogni percorso della mappa esista, sono della wiki, non di questo repo.
**Chi fa cosa**
| Parte | Compito |
|---|---|
| Wiki | Mantiene indice, catalogo e immagini; rigenera la mappa; controlla che i percorsi esistano |
| Motore | Instrada la domanda, legge indici e pagine, sceglie le figure, scrive il markdown con l'immagine e la didascalia |
| App | Stampa il markdown; mostra l'immagine se il file esiste, altrimenti `[alt]` |
## Conseguenze
**Positive**: una risposta ripercorre indice → pagina → figura invece di cercare frammenti; i percorsi stanno in un solo posto e si rigenerano; l'app non cambia; wiki diverse adottano lo stesso modello cambiando pochi campi.
**Negative**: il rispetto dello schema dipende dal motore che segue le istruzioni (può sbagliare percorso: l'app lo mostra come `[alt]`); la wiki deve mantenere lo script e il controllo dei percorsi; lo standard è provvisorio.
## Da verificare
- Che Claude Code, avviato dalla cartella dedicata, legga e rispetti sia il file di quella cartella sia quello della radice (prova con la chat reale su una wiki di esempio).
- L'equivalente per Codex (`AGENTS.md`) e come si comporta con le immagini nei suoi comandi.
- Se il campo minimo `path` debba diventare un percorso dalla radice nel catalogo stesso, eliminando la mappa derivata.
## Riferimenti
- Uso con le immagini: [README.md](../../README.md), sezione «Immagini nelle risposte».
@@ -0,0 +1,45 @@
# ADR-0008 — Formule LaTeX nel markdown
- **Stato**: Proposta
- **Data**: 2026-10-20
- **Collegato a**: [ADR-0006](0006-immagini-nel-markdown.md) (stesso renderer, stessi vincoli di sicurezza), [ADR-0007](0007-schema-wiki-per-query-con-immagini.md) (stesso tipo di risposte)
## Contesto
Le risposte dei motori possono contenere formule in LaTeX: `$ ... $` nel testo e `$$ ... $$` a blocco. Il renderer markdown ([Gui/MarkdownView.cs](../../src/agent-wiki-ui/Gui/MarkdownView.cs)) usa Markdig con `UseAdvancedExtensions()`, che comprende l'estensione matematica: il parser riconosce le formule ma il renderer non le gestisce. Quelle a blocco finiscono in un blocco di codice con il sorgente LaTeX; quelle nel testo non vengono disegnate (nodo ignorato). L'utente vede sorgente illeggibile o formule mancanti.
## Criteri di decisione
1. Mostrare le formule composte (frazioni, apici e pedici, lettere greche, sommatorie, sopralineature) come in un testo tipografico.
2. Nessuna rete e nessuna esecuzione di codice: la formula è solo dato da disegnare (come per le immagini, ADR-0006).
3. Degrado sicuro: un LaTeX non valido mostra il sorgente come testo, mai un errore né un blocco della chat.
4. Coerenza con il resto del renderer: colori dal tema, ricostruzione al cambio di tema, testo circostante selezionabile, accessibilità.
5. Funziona su Windows e Linux nel publish single-file, senza runtime aggiuntivi.
6. Dipendenza manutenuta e compatibile con la versione di Avalonia in uso.
## Opzioni considerate
| Opzione | Esito | Motivo |
|---|---|---|
| Lasciare com'è | Scartata | Il sorgente LaTeX o l'assenza delle formule rendono illeggibili le risposte con formule. |
| Convertire il LaTeX in testo Unicode (`x_1` → `x`+pedice) con un mini-interprete proprio | Scartata | Copre solo i casi semplici; frazioni, `\overline`, `\left…\right` e matrici richiederebbero di riscrivere un motore di composizione. |
| Rendere con un motore web (KaTeX/MathJax in una WebView) | Scartata | Aggiunge un browser incorporato e codice JavaScript, contro il criterio 2 e il peso dell'eseguibile. |
| **Libreria di composizione TeX per Avalonia (`Sylinko.CSharpMath.Avalonia`, licenza MIT, basata su CSharpMath)** | **Scelta** | Disegna LaTeX come controllo Avalonia, senza rete né script; l'ultima versione (12.0.0) è per Avalonia 12, come l'app. |
## Decisione
- La formula tra `$ … $` è un'**immagine inline** composta dal controllo della libreria, inserita nel testo (`InlineUIContainer`) con allineamento al testo; `$$ … $$` è un **blocco** a sé, allineato a sinistra, con scorrimento orizzontale se più larga della colonna.
- Il colore è quello del testo del tema (`Fg`, o il colore ereditato se il token manca); al cambio di tema la vista si ricostruisce (già così per il resto).
- **Ripiego**: se il LaTeX non è valido la formula compare come testo sorgente (`$…$` con carattere monospazio), senza eccezioni.
- Il nome accessibile del controllo è il sorgente LaTeX; la formula non è selezionabile come testo (è un disegno): il pulsante **Copia** della risposta continua a copiare il markdown originale, quindi anche il sorgente delle formule.
- I simboli `$` isolati, ad esempio un prezzo («10 $ e 20 $»), restano testo. Markdig da solo li prenderebbe per una formula (e toglie gli spazi dal contenuto): il renderer applica la regola di Pandoc sul testo originale, cioè niente spazio subito dopo il `$` di apertura né subito prima di quello di chiusura. Per questo gli intervalli di Markdig si leggono su un testo con fine riga uniformi (`\n`).
- Nessuna rete e nessuna lettura di file: la formula è solo dato per il disegno.
## Conseguenze
**Positive**: le risposte con formule mostrano le formule composte; nessun cambiamento ai motori né ai prompt.
**Negative**: una dipendenza in più (e le sue librerie di disegno); il pacchetto è un fork per Avalonia 12 e va tenuto d'occhio quando Avalonia aggiorna; le formule non sono selezionabili come testo; il supporto non copre tutto LaTeX (le macro non supportate cadono nel ripiego).
## Da verificare
- Dimensione dell'eseguibile pubblicato (single-file) dopo l'aggiunta della libreria e dei suoi font.
- Allineamento verticale delle formule inline rispetto al testo con font diversi, su Windows e Linux.
- Quali comandi LaTeX comuni (matrici, ambienti `align`, estensioni a pacchetto) sono supportati.
- Il comportamento con formule molto lunghe in una riga di testo (a capo).
## Riferimenti
- [MVP.md](../MVP.md) → "GUI (chat)"; matrice MD-31…MD-38 in [TESTING.md](../TESTING.md).