8.3 KiB
8.3 KiB
ADR-0002 — Più motori CLI (Claude Code, Codex, Antigravity) su Windows e Linux
- Stato: Accettata (Claude Code, Codex, Antigravity)
- Data: 2026-10-15 (aggiornato 2026-10-18 per Antigravity)
- Collegato a: ADR-0001 (GUI e piattaforme)
Contesto
L'app lanciava solo claude -p. Si vuole usare dalla stessa chat anche OpenAI Codex CLI e Antigravity CLI (agy), così come claude già installati da terminale dall'utente, su Windows e Linux. L'app non li installa e non gestisce login: sono programmi esterni, autenticati a parte.
Criteri di decisione
- Aggiungere un motore costa una definizione dichiarativa, non nuova logica sparsa.
- Funziona uguale su Windows e Linux: i motori si trovano nel PATH come da terminale.
- Un solo punto di avvio processo (cartella dell'exe, stdin chiuso,
ArgumentList), condiviso da CLI e GUI. - 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è diventatoEngineRunnercon 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 ai valori iniziali del motore (il modello più piccolo ed effort
low; "Predefinito" resta selezionabile) e le liste sono quelle del motore. La persistenza su disco delle impostazioni è rinviata. La CLI ottiene--engine <id>(claude,codex,agy). - Sessione: per Claude Code l'id lo sceglie l'app; per Codex e Antigravity lo genera il motore e l'app lo legge dall'output (
thread_idper Codex,conversation_idper Antigravity). Il JSON della chat ha quindi un campoEnginee un campoSessionIdseparato dall'id della chat (che resta il nome del file). I file scritti prima (senza questi campi) si leggono come chat di Claude Code conSessionId= id della chat. - Autore dei messaggi: l'etichetta sulle risposte e i segnaposto del composer usano il nome del motore della chat; l'elenco chat indica il motore.
- Permessi: di default sola lettura / nessun bypass dei controlli; concedere di più è un'opzione esplicita.
- Output: ogni motore ha un parser che lo normalizza in testo markdown prima del rendering.
Comandi dei motori
Claude Code, Codex e Antigravity sono verificati con prove reali (Linux/WSL, ottobre 2026).
| Claude Code | Codex CLI | Antigravity CLI | |
|---|---|---|---|
| Comando (GUI) | claude -p <prompt> |
codex exec --json --skip-git-repo-check -s read-only [opzioni] -- <prompt> |
agy --output-format json --print-timeout 120s [opzioni] -p <prompt> |
| Continuare | --session-id <guid>, poi --resume <guid> |
codex exec resume --json --skip-git-repo-check -c 'sandbox_mode="read-only"' [opzioni] <id> -- <prompt> |
agy --output-format json --print-timeout 120s --conversation <id> [opzioni] -p <prompt> |
| Modello | --model |
-m / --model |
--model |
| Effort | --effort |
-c model_reasoning_effort=<livello> |
--effort |
| Output usato | testo su stdout | JSONL su stdout (vedi sotto) | JSON su stdout (conversation_id, status, response) |
Codex: esito delle prove
- L'id di sessione non si sceglie: compare nel primo evento JSONL
{"type":"thread.started","thread_id":"<uuid>"}. Nel testo normale compare solo nell'intestazione su stderr. - Con
--json, stdout contiene solo eventi:thread.started,turn.started,item.completed(conitem.type = "agent_message"e il testo della risposta; con"error"sono avvisi non fatali),turn.completed, e in caso di erroreerroreturn.failed. Il messaggio di errore è una stringa che contiene a sua volta un JSON (error.message): l'app estrae il messaggio interno. L'exit code è 1 se il turno fallisce. - La risposta mostrata è l'ultimo
agent_messagedel turno (come--output-last-message). exec resumenon accetta-s/--sandbox: la sola lettura si ottiene con-c 'sandbox_mode="read-only"', verificato (un file richiesto in scrittura non viene creato). Accetta-me-c.--skip-git-repo-checkserve perché la cartella dell'exe non è un repository Git.--prima del prompt evita che un prompt che inizia con-sia letto come opzione (verificato).- Lo stdin va chiuso subito, come per Claude Code: altrimenti Codex stampa "Reading additional input from stdin…" e attende.
- Il modello predefinito è quello di
~/.codex/config.toml. Se richiede una versione più recente di Codex l'errore è chiaro (requires a newer version of Codex): l'app lo mostra e INSTALL.md indicacodex update. - Il modello più piccolo di Codex è assunto essere GPT-5.6 Luna (nome e supporto agli effort inferiori: non dichiarato da OpenAI nelle prove); da rivedere quando cambia il catalogo.
- Il catalogo modelli di Codex è dinamico (
~/.codex/models_cache.json); l'app ne usa uno fisso, da aggiornare, con "Predefinito" e "Altro ID…" sempre disponibili.
Antigravity (agy): esito delle prove
- Eseguibile nativo: ELF su Linux (
~/.gemini/bin/agy),.exenativo su Windows. - Permessi e sicurezza: per usare
agyin 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:a tool required the read_file permission that headless mode cannot prompt for), mentre scritture e comandi restano negati di default. Non si passa--mode plan(che fallisce anch'esso con errore jetski) né--mode auto(non disponibile per l'account) né alcun flag--mode. Mai usare--dangerously-skip-permissions. - Posizione di
-p: tutti i flag di configurazione vanno passati rigorosamente prima di-p <prompt>. - Formato output: per la GUI (
structured = true) si passa--output-format json; per la CLI si omette per ereditare testo normale. L'output JSON contieneconversation_id,status("SUCCESS") eresponsecon il testo generato. - Sessione: l'id di sessione è un UUID generato da
agy(OwnsSessionId => true), letto daconversation_id. La ripresa avviene passando--conversation <sessionId>. - Blocco sottoprocesso su output rediretto (issue #318): mitigato passando
--print-timeout 120s(formato durata Go) e assicurando la chiusura immediata dello stdin. - Modelli ed effort: modelli fissi da catalogo
agy models, modello più piccolo predefinitogemini-3.0-flash-low, effort supportatilow,medium,high,xhigh,max(defaultlow).
Conseguenze
Positive: un solo flusso di avvio; nuovi motori aggiungibili con una definizione; elimina la duplicazione CLI/GUI. Negative: i flag delle CLI cambiano nel tempo e vanno mantenuti; le sessioni non si trasferiscono tra motori; più superficie da testare su due sistemi operativi.
Rischi e da verificare
- Codex: l'id di sessione lo genera Codex; letto dall'evento
thread.started(vedi sopra). - Antigravity: verificato il comando non interattivo
agy [flag] -p <prompt>, output JSON--output-format json, ripresa con--conversation, timeout con--print-timeout 120se chiusura stdin. - Forma dell'eseguibile:
claudeeagysono binari nativi avviabili direttamente; per installazioni tramite gestori di pacchetti verificare eventuali script wrapper su Windows. - Autenticazione: ogni CLI deve essere già autenticata; l'app mostra l'errore del motore senza gestire login.
- Formato dell'output di Codex e Antigravity: Codex JSONL, Antigravity JSON su stdout, normalizzati in testo markdown.
- Mappatura effort e modelli per Codex e Antigravity (liste fisse, da aggiornare).
Riferimenti
- Passi: ROADMAP.md, Fase 4.
- Installazione e accesso dei motori: INSTALL.md.