# pi-agy-subagent

> **pi-agy-subagent** — delega task di coding alla Google Antigravity CLI (`agy`) come
> sub-agent esterno di [pi](https://pi.dev). Progetto indipendente, non affiliato a Google.

[![npm](https://img.shields.io/npm/v/pi-agy-subagent)](https://www.npmjs.com/package/pi-agy-subagent)

Estensione **project-local** per pi che registra l'Antigravity CLI di Google (`agy`) come sub-agente esterno.

## Installazione

Come pacchetto pi (da npm o direttamente da GitHub):

```bash
pi install npm:pi-agy-subagent
# oppure
pi install github:AChiabodo/pi-agy-subagent
```

Come estensione project-local (questo repository): pi carica automaticamente
`.pi/extensions/agy-subagent/index.ts` **solo dopo che il progetto è stato trusted**
(pi chiede conferma all'avvio: rispondere "trust"). Nessun file di settings globale viene letto o modificato:
l'estensione vive interamente in `.pi/extensions/` di questo repository.

## Requisiti

- `agy` nel PATH (installato: `C:\Users\<user>\AppData\Local\agy\bin`). Override con variabile d'ambiente `AGY_BIN`.
- Autenticazione: eseguire `agy` in modalità interattiva una volta (credenziali nel keyring di Windows),
  oppure configurare headless auth con `modelProvider: "gemini"` + `GEMINI_API_KEY`
  (in `~/.gemini/antigravity-cli/settings.json` — file di Antigravity, non di pi).

## Tool registrati

### `agy_run`

Delega un task di coding self-contained all'agente Antigravity in modalità headless:

```
agy -p <prompt> --output-format stream-json --disable-slash-commands --print-timeout <N>m [opzioni]
```

Parametri principali:

| Parametro                         | Descrizione                                                               |
| --------------------------------- | ------------------------------------------------------------------------- |
| `prompt`                          | Task completo e autonomo (contesto, percorsi, deliverable)                |
| `cwd`                             | Directory di lavoro del run (default: directory di sessione pi)           |
| `model`                           | Slug modello (vedi tool `agy_models`)                                     |
| `effort`                          | `low` / `medium` / `high`                                                 |
| `mode`                            | `accept-edits` / `plan`                                                   |
| `agent`                           | Custom agent Antigravity (vedi tool `agy_agents`)                         |
| `conversationId` / `continueLast` | Riprende una conversazione agy precedente                                 |
| `jsonSchema`                      | Schema JSON per output strutturato (`details.structuredOutput`)           |
| `timeoutMinutes`                  | Limite wall-clock (default 10, max 120); il processo viene killato        |
| `skipPermissions`                 | **Pericoloso**: abilita `--dangerously-skip-permissions`. Default `false` |

Comportamento:

- Streaming live verso la UI di pi via `onUpdate`:
  - step `DONE` sempre (tool call con comando + preview output troncato, risposte con token);
  - step `ACTIVE` con throttle (500 ms) mentre un tool è in esecuzione lungo
    (`[agy] ▸ step 7 · edit · running… · 3.2k in/1.1k out · 45s`).
- Rendering custom in TUI: header compatto (prompt troncato, cwd, model, effort, mode) e
  risultato con stato ✓/✗, turni, durata, token, `conversationId`, response (completa in vista
  espansa, preview in vista compatta), notice di permessi e stderr tail in vista espansa.
- Abort (Ctrl+C in pi) killa l'intero albero processi `agy` (su Windows via `taskkill /T /F`).
- **Diffstat del workspace**: a run completata (se `cwd` è un repo git) il risultato include
  `[agy] workspace: N file(s) changed, +I -D (files…)` con i dettagli in `details.workspaceChanges`
  (solo lettura, `git --no-optional-locks`; fallback silenzioso su non-git).
- L'`usage` token di agy è riportato a pi (`Usage`), quindi compare nei totali di `/session`.
- Dettagli persistiti: `conversationId`, `status`, `cwd`, durata, numero turni, snapshot di progresso,
  notice di permessi soft-denied, flag `responseTruncated`/`fullOutputPath`.
- **Output policy** (parametro `outputMode`):
  - `"full"` (default): la response torna al modello verbatim, senza truncation;
  - `"tail"`: sopra le soglie `limits.*` (50 KiB / 2000 righe di default) viene restituita solo la
    coda della response (dove finiscono conclusioni/esiti QA), il testo integrale viene salvato su
    file (`%TEMP%\antigravity-runs\`) e il path è comunicato nel risultato (`details.fullOutputPath`).
  - `structured_output` non è mai troncato.
- **Memoria conversazioni**: ogni run registra `conversationId + cwd` nei dettagli; lo store viene
  ricostruito dal branch di sessione a ogni `session_start` (branch/fork compatibili).
  - `continueLast: true` riprende la conversazione più recente **dello stesso cwd**; se assente,
    errore esplicito (non usa il `--continue` della CLI, che ignora il working directory).
  - `agy_history` elenca le conversazioni della sessione (più recenti prima) con i `conversationId`
    da riusare in `agy_run`.
- `agy` scrive file nel workspace condiviso: le esecuzioni concorrenti sullo stesso cwd sono
  **serializzate con un lock per-cwd** (fallimento immediato di default; `parallelism.sameCwd: "wait"`
  per accodare). Task su cwd diversi restano paralleli. Gli agy_run di sessioni pi distinte
  (processi separati) NON sono coordinati tra loro.

### `agy_models` / `agy_agents` / `agy_history`

- `agy models` / `agy agents`: elenchi a basso costo per scegliere `model` / `agent` in `agy_run`.
- `agy_history`: conversazioni agy registrate nella sessione pi corrente (senza chiamare la CLI),
  per recuperare un `conversationId` da passare a `agy_run`.

### `agy_doctor`

Diagnostica del setup locale, utilizzabile anche senza `agy` installato o autenticato:

- Presenza del binario (`AGY_BIN`/PATH) e versione (`agy --version`).
- File di settings `~/.gemini/antigravity-cli/settings.json`: esistenza, `modelProvider`, numero di regole `permissions.allow`, JSON valido.
- `GEMINI_API_KEY` impostata (mai il valore).
- Hint azionabili (installazione, auth interattiva, chiave mancante).

Il report completo è in `details` del tool result; usare quando `agy_run` fallisce o prima del primo run.

## Configurazione di progetto

File opzionale `.pi/antigravity.json` (relativo alla directory di sessione). Precedenza:
parametri espliciti del tool > config di progetto > default built-in.

```jsonc
{
	"defaults": {
		"model": "gemini-3.7-flash-high",
		"effort": "medium",
		"mode": "plan",
		"timeoutMinutes": 20,
		"agent": "coder",
	},
	"security": {
		"allowSkipPermissions": false,
		"maxTimeoutMinutes": 60,
		"maxPromptChars": 100000,
	},
	"streaming": {
		"stepThrottleMs": 500,
		"showToolOutput": true,
	},
	"parallelism": {
		"sameCwd": "fail",
	},
}
```

| Chiave                          | Default  | Effetto                                                                                                                                                                           |
| ------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `defaults.*`                    | —        | Valori applicati quando il tool non li specifica                                                                                                                                  |
| `security.allowSkipPermissions` | `false`  | Double opt-in: `skipPermissions: true` nel tool fallisce a meno che anche questa flag sia `true`                                                                                  |
| `security.maxTimeoutMinutes`    | `120`    | Hard cap che vincola anche il parametro `timeoutMinutes` esplicito                                                                                                                |
| `security.maxPromptChars`       | `100000` | Limite lunghezza prompt                                                                                                                                                           |
| `streaming.stepThrottleMs`      | `500`    | Intervallo minimo tra update di step ACTIVE                                                                                                                                       |
| `streaming.showToolOutput`      | `true`   | Preview dell'output dei tool agy negli step DONE                                                                                                                                  |
| `limits.maxOutputBytes`         | `51200`  | Soglia byte per la truncation della response (solo con `outputMode: "tail"`; default allineato a pi: 50 KiB)                                                                      |
| `limits.maxOutputLines`         | `2000`   | Soglia righe per la truncation della response (solo con `outputMode: "tail"`)                                                                                                     |
| `parallelism.sameCwd`           | `"fail"` | Comportamento quando un altro run agy detiene lo stesso cwd: `"fail"` fallisce subito con errore azionabile, `"wait"` mette in coda (l'abort della chiamata in attesa la sblocca) |

Il file è ricaricato quando cambia mtime; se mancante si usano i default senza warning;
se non valido si ricade sui default con un **warning una tantum** per path.

## Sicurezza

- Di default `agy` gira senza `--dangerously-skip-permissions`: le azioni non autorizzate sono
  _soft-denied_ da agy (notice in `details.softDeniedNotices`), salvo regole in
  `~/.gemini/antigravity-cli/settings.json` (`permissions.allow`).
- `mode: "plan"` limita l'agente alla sola pianificazione.
- L'estensione passa il prompt con `--disable-slash-commands` per evitare espansioni impreviste.
- Input validati prima dello spawn: prompt vuoto/troppo lungo (max 100.000 caratteri) e
  `jsonSchema` malformato falliscono subito con errore esplicito.

## Troubleshooting rapido

| Sintomo                                          | Causa probabile                      | Azione                                                                                                                |
| ------------------------------------------------ | ------------------------------------ | --------------------------------------------------------------------------------------------------------------------- |
| "binary not found"                               | `agy` non su PATH / `AGY_BIN` errato | Installare agy o impostare `AGY_BIN`; verificare con `agy_doctor`                                                     |
| Errore con stderr che parla di auth/token/login  | Sessione non autenticata             | Eseguire `agy` interattivo una volta, oppure configurare headless auth (`modelProvider: "gemini"` + `GEMINI_API_KEY`) |
| "exited without producing a result" senza stderr | Installazione/auth corrotta          | `agy_doctor` per la diagnosi strutturata                                                                              |
| Notice di permessi soft-denied                   | Azioni non in `permissions.allow`    | Aggiungere regole in `~/.gemini/antigravity-cli/settings.json`                                                        |

## Note sulla versione CLI

| Versione agy | Stato      | Note                                                                                                                                             |
| ------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| 1.1.22       | ✅ testata | Versione di riferimento; issue upstream su headless/permissions (#45, #548); resume con `--conversation` può forkare la conversazione (nuovo id) |

Riverificare a ogni update della CLI (i caveats variano tra versioni).

## Sviluppo

Il workspace è configurato per typecheck, lint e test dell'estensione:

```bash
npm install        # setup una tantum
npm run check      # typecheck + lint + test (75 test, smoke esclusi)
npm run test:watch # test in watch mode
npm run format     # prettier

# Smoke test end-to-end con la CLI reale (spende token, richiede agy autenticato):
AGY_SMOKE=1 npx vitest run test/smoke.agy.test.ts
```

- Typecheck: `tsconfig.json` risolve i tipi di pi/pi-ai/typebox dalle `devDependencies`
  (versioni agganciate a quelle distribuite con `@earendil-works/pi-coding-agent`).
- Test unit (vitest): funzioni pure per modulo — `utils` (resolveCwd/describeStep),
  `diagnostics`, `doctor` (parsing settings), `render` (progresso/formattazione),
  `conversations` (memoria), `config` (precedenza/cache), `parallelism` (CwdLock).
- Smoke test gated (`AGY_SMOKE=1`): run E2E reali con la CLI — doctor, run banale, resume
  conversazione, timeout-kill.
- Le funzioni con side effect (spawn processi) vivono in `lib/process.ts` e non sono testate
  in unit: coperte dagli smoke test.

### Mappa dei moduli

```text
.pi/extensions/agy-subagent/
├── index.ts            # registrazione tool, wiring esecuzione, renderCall/renderResult
├── lib/
│   ├── process.ts      # spawn agy + parsing NDJSON + kill albero processi
│   ├── types.ts        # tipi protocollo stream-json, AgyRunDetails, AgyStatus
│   ├── utils.ts        # resolveCwd, describeStep, toPiUsage (pure)
│   ├── diagnostics.ts  # validazione input + diagnosi errori (pure)
│   ├── doctor.ts       # diagnostica setup locale (agy_doctor)
│   ├── render.ts       # progresso live e formattazione (pure)
│   ├── conversations.ts# memoria conversazioni (pure)
│   ├── config.ts       # .pi/antigravity.json: schema, load+cache, precedenza (pure)
│   └── parallelism.ts  # CwdLock per-cwd (mutex con coda)
└── CHANGELOG.md

test/                  # spec unit per ogni modulo + smoke.agy.test.ts (gated)
```
