Test unitari e di processo per AG-01…AG-12 e UP-21, righe della matrice spuntate. Percorsi aggiornati in ROADMAP, ADR-0001 e TESTING; PROJECT_STATE con lo stato dei test e il debito sulla prova con agy reale. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
10 KiB
AGENT.md
Guida per gli agenti di codice (Claude Code, Gemini CLI, altri) che lavorano in questo repository. È l'unica sorgente delle istruzioni: CLAUDE.md e GEMINI.md sono puntatori che importano questo file, quindi ogni modifica si fa qui (vedi docs/README.md).
Progetto
agent-wiki-ui (già LlmRun; il nome ha un'unica sorgente, vedi docs/README.md): eseguibile C# (.NET 10, Windows e Linux) che invoca Claude Code (claude -p), Codex (codex exec) o Antigravity (agy … -p) da terminale. Senza argomenti apre una GUI di chat Avalonia, con argomenti funziona da CLI.
Workflow doc-first (obbligatorio)
- Stato attuale, stadio (MVP), debiti e cosa manca: docs/PROJECT_STATE.md; aggiornarlo nello stesso commit di ogni cambio di stadio, funzione, ADR o debito. Specifica e criteri di accettazione: docs/MVP.md. Fasi e stato: docs/ROADMAP.md. Indice dei doc e regole (SSOT, ADR): docs/README.md; una decisione architetturale si scrive solo nel suo ADR, gli altri doc la richiamano con un link. Uso: README.md.
- Test obbligatori (docs/TESTING.md, ADR-0003): ogni nuova funzione, correzione o cambio di comportamento va coperta da test nello stesso commit; prima si aggiunge la riga (con ID) alla matrice in TESTING.md, poi i test, poi il codice. Un bug si corregge partendo da un test che fallisce.
- Ad ogni richiesta di modifica: aggiornare prima i doc interessati (MVP.md, ROADMAP.md con le spunte, README.md se cambia l'uso; un nuovo ADR in
docs/adr/per ogni scelta architetturale), poi il codice, poi questo file se cambiano comandi, design o vincoli. Nessuna modifica deve lasciare i doc disallineati.
Comandi
- Build:
dotnet builddalla radice (soluzioneagent-wiki-ui.slnx: app insrc/agent-wiki-ui/, test intests/;net10.0, Avalonia) - GUI:
dotnet run --project src/agent-wiki-ui; CLI:dotnet run --project src/agent-wiki-ui -- "prompt" - Publish single-file (uno per sistema):
scripts/publish.sh(Linux/WSL/macOS) oscripts/publish.ps1(Windows PowerShell): creanodist/win-x64/edist/linux-x64/(dist/è ignorata da git); i due script vanno tenuti allineati, oppure a manodotnet publish src/agent-wiki-ui -c Release -r win-x64 --self-contained -p:PublishSingleFile=truee-r linux-x64 - Test:
dotnet testdalla radice (xUnit v3 + Avalonia.Headless con Skia reale;tests/FakeClaudeè ilclaudefinto per i test di processo). Per ora coprono le immagini e le formule del renderer, ilFakeClaudee il motore Antigravity (AG-01…AG-12, UP-21); il resto è pianificato in docs/TESTING.md (Fase 6). Per verificare la GUI senza schermo si può usareAvalonia.HeadlessconCaptureRenderedFrame()in un progetto di prova fuori dal repo.
Architettura
Decisioni e motivazioni negli ADR (docs/adr/); qui solo la mappa del codice.
- Program.cs: top-level statements. Con argomenti è la CLI (
EngineRunner.RunInherited, motore scelto con--engine <id>come primo argomento, default Claude Code); senza avvia Avalonia (App). - Core/Engine.cs: astrazione "motore" (ADR-0002):
Engine(id, nome, eseguibile, modelli, effort,BuildArgs,Parse,FetchLatestVersionAsyncper il controllo delle versioni),ClaudeEngine,CodexEngine,AgyEngine, registroEnginese ricerca nel PATH (PathSearch). Gli elenchi modelli sono fissi, da aggiornare quando ne escono di nuovi;DefaultModelIdindica il modello più piccolo, scelto all'avvio insieme a effortlow(se esce un modello più piccolo, aggiornarlo). - 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 daEngine.Parse, eRunToolAsyncper i comandi di servizio (--version,update) con tempo massimo. - Core/EngineUpdater.cs: controllo delle versioni in sola lettura (
CheckAsync,IsNewer) e aggiornamento di un CLI con il suoupdateufficiale (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: cronologia, un JSON per chat in
<LocalApplicationData>/<nome-app>/chats/<guid>.json(nome file = id della chat, validato come Guid prima dell'uso; campiEngineeSessionIdopzionali: i file vecchi sono chat di Claude Code). - Core/AppInfo.cs: nome dell'app, letto dall'
AssemblyNamedel csproj (unica sorgente). - 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 (+
.cs): layout XAML con binding compilati. Gui/App.axaml: token di colore come risorse di tema chiaro/scuro e stili; specifiche indocs/MVP.md→ "Design (UI/UX)". Cambiando l'aspetto aggiornare quella sezione. - Gui/MarkdownView.cs: renderer markdown proprio (albero Markdig → controlli Avalonia,
DisableHtml); la parte sulle immagini è in Gui/MarkdownView.Images.cs (partial); le formule LaTeX (ADR-0008) sono in Gui/MarkdownView.Math.cs (partial, libreriaSylinko.CSharpMath.Avalonia). Le immagini si mostrano solo da file locale (ADR-0006), mai da URL; i percorsi relativi si cercano nella cartella dell'exe e poi nelle superiori (radice della wiki). - Dipendenze NuGet: Avalonia (+ Desktop, Themes.Fluent), Markdig.
Vincoli non ovvi
- La working directory di
claudedeve essere la cartella dell'exe: usareAppContext.BaseDirectory, maiEnvironment.CurrentDirectorynéAssembly.Location(vuoto in single-file). - Lo stdin del motore va redirezionato e chiuso subito:
claudeaspetta 3 s,codexstampa "Reading additional input from stdin…" e attende,agypuò bloccarsi su output rediretto (issue #318). claudeeagysono eseguibili nativi (~/.local/bin,~/.gemini/bin), avviabili direttamente. Se fossero script.cmd(Windows) servirebbecmd.exe /c.- Codex (
CodexEngine): l'id di sessione lo genera Codex e si legge dathread.startednell'output JSONL (--json); la risposta è l'ultimoagent_message;exec resumenon 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). - Antigravity (
AgyEngine): eseguibileagy; tutti i flag di configurazione vanno passati prima di-p <prompt>; per usareagyin modalità non interattiva (-p) in sola lettura, nel file~/.gemini/antigravity-cli/settings.jsondeve essere presente la regola'read_file(*)'sotto'permissions.allow'(altrimenti headless mode negaread_filecon errore jetski: 'jetski: no output produced - a tool required the read_file permission that headless mode cannot prompt for'), mentre scritture e comandi restano negati di default; non passare--mode planné--mode autoe mai usare--dangerously-skip-permissions; per la GUI si usa--output-format jsone l'id di sessione si legge daconversation_id(OwnsSessionId => true); la ripresa della conversazione usa--conversation <id>; per evitare il blocco su sottoprocesso con output rediretto (issue #318) si passa--print-timeout 120se si chiude immediatamente lo stdin. - Il controllo delle versioni contatta solo
downloads.claude.ai(Claude Code),api.github.com(Codex, serve lo User-Agent) eantigravity-cli-auto-updater-974169037036.us-central1.run.app(Antigravity), 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.
- Windows: l'eseguibile è
WinExe(nessuna console al doppio clic); in modalità CLICore/ConsoleHost.csriaggancia il terminale padre conAttachConsolee collega stdout/stderr aCONOUT$solo se non sono già reindirizzati (ADR-0005). Il codice deve poter fallire in silenzio senza terminale. Conseguenza: su Windows l'exit code della CLI arriva solo se il chiamante attende il processo. - 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;ChatStoremigra le cartelle dei nomi precedenti). - Avalonia 12: la clipboard si usa con
using Avalonia.Input.Platform;(SetTextAsyncè un'estensione);TextBox.Watermarkè obsoleto, usarePlaceholderText. - Nel renderer markdown i colori si risolvono al momento del rendering (
TryFindResourceconActualThemeVariant) e la vista si ricostruisce al cambio di tema. - I link nel markdown si aprono con hit-test sul testo (
TextLayout.HitTestPoint) solo sehttp(s)e se non c'è una selezione in corso. - Il publish single-file richiede
IncludeNativeLibrariesForSelfExtract(nel csproj): Skia e HarfBuzz sono librerie native.
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 Avalonia (layout, usabilità, accessibilità).
Commit
Messaggi di commit in italiano: titolo breve all'indicativo o in forma nominale, eventuale corpo che spiega il perché. Mantenere la riga finale Co-Authored-By prevista dall'ambiente. Fare commit solo su richiesta.