ia_dev/gitea-issues/AGENT_LOOP.md

139 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Boucle agent (agent-loop) surveillance des mails et fichier témoin
Script qui tourne en boucle dans l'environnement Cursor de ce projet pour surveiller les mails non lus et maintenir un **fichier témoin** indiquant si la boucle est active.
**Agent orchestrateur** : `.cursor/agents/agent-loop.md` (lance les boucles, x fois récupération + traitement).
## Rôle du script
- Exécuter périodiquement `mail-list-unread.sh` (sans modifier l'état des mails).
- Mettre à jour à chaque tour un **fichier témoin** (statut + horodatage) pour savoir si la boucle est active.
- Quand des mails non lus sont détectés, écrire un fichier **pending** et afficher un message invitant à lancer l'agent dans Cursor.
Le script **ne traite pas** les mails luimême : le traitement (réponse, issues, commits) est fait par l'**agent gitea-issues-process**. Vous pouvez soit lancer l'agent à la main dans Cursor, soit faire lancer l'agent par la boucle en activant `AGENT_LOOP_RUN_AGENT=1` (voir cidessous) si la **Cursor Agent CLI** est installée.
## Environnement au démarrage
Au démarrage, le script **source systématiquement** `~/.bashrc` (si le fichier existe et est lisible), puis ajoute `~/.local/bin` au `PATH` si ce répertoire existe. Ainsi, la commande `agent` (Cursor Agent CLI) est trouvée même si la boucle est lancée depuis un contexte où le shell n'a pas chargé le profil (nohup, cron, etc.).
## Lancement
Si le fichier `.secrets/gitea-issues/agent-loop.env` existe, il est sourcé au démarrage (voir `agent-loop.env.example`).
Depuis la **racine du dépôt projet** (parent de ia_dev) :
```bash
cd <racine_projet> && ./ia_dev/gitea-issues/agent-loop.sh [interval_sec]
```
Avec un intervalle en secondes (défaut 60) :
```bash
./ia_dev/gitea-issues/agent-loop.sh 120
```
Ou via une variable d'environnement :
```bash
AGENT_LOOP_INTERVAL_SEC=120 ./ia_dev/gitea-issues/agent-loop.sh
```
Pour l'exécuter en arrière-plan et garder la boucle active après fermeture du terminal, utiliser `nohup` ou un gestionnaire de processus (systemd, screen, tmux) :
```bash
nohup ./ia_dev/gitea-issues/agent-loop.sh 60 >> ia_dev/projects/<id>/logs/gitea-issues/agent-loop.log 2>&1 &
```
## Fichier témoin (actif / inactif)
- **Emplacement** : `projects/<id>/logs/gitea-issues/agent-loop.status` (ou `AGENT_LOOP_STATUS_FILE` si défini).
- **Contenu** : trois lignes
1. Horodatage ISO 8601 du dernier tour.
2. Statut : `idle` | `mails_pending` | `running` | `error`.
3. Détail optionnel (ex. message pour l'utilisateur).
**Considérer la boucle comme active** si le fichier a été modifié depuis moins de **2 × intervalle** (ex. moins de 120 s si intervalle = 60 s). Au-delà, la boucle est considérée arrêtée.
## Fichier pending (mails en attente)
- **Emplacement** : `projects/<id>/logs/gitea-issues/agent-loop.pending`.
- **Rôle** : quand des mails non lus sont détectés, le script y écrit un bloc (horodatage, statut `mails_pending`, puis la sortie de `mail-list-unread.sh`). Permet de voir quels mails attendent un traitement par l'agent.
- Quand il n'y a plus de non lus, le script vide ce fichier au tour suivant.
**Hook Cursor** : un hook (`.cursor/hooks.json``sessionStart` / `beforeSubmitPrompt`) peut exécuter un script qui lit ce fichier pour « remonter » les mails reçus au contexte de l'agent (voir section Référence hooks).
## Variables d'environnement
Variables possibles dans `.secrets/gitea-issues/agent-loop.env` ou en export shell :
| Variable | Défaut | Description |
|----------|--------|-------------|
| `AGENT_LOOP_INTERVAL_SEC` | 60 | Intervalle entre deux vérifications (secondes). Peut aussi être passé en premier argument au script. |
| `AGENT_LOOP_RUN_AGENT` | 0 | Si mis à `1`, la boucle lance la **Cursor Agent CLI** (`agent`) quand des mails non lus sont détectés, avec un prompt qui exécute le workflow mails de gitea-issues-process. Nécessite que la commande `agent` soit installée (voir [Cursor CLI](https://cursor.com/docs/cli/using)). Si `agent` n'est pas dans le PATH, la boucle se contente de mettre à jour le statut et le fichier pending. |
| `AGENT_LOOP_MODEL` | `sonnet-4.6` | Modèle utilisé par la CLI (`agent --model ...`). Par défaut `sonnet-4.6` pour limiter les blocages liés aux quotas Opus. Ex. : `AGENT_LOOP_MODEL=gpt-5.4-low` ; liste : `agent models`. |
| `AGENT_LOOP_STATUS_FILE` | `projects/<id>/logs/gitea-issues/agent-loop.status` | Chemin du fichier témoin (sous ia_dev, par projet). |
| `AGENT_LOOP_PENDING_FILE` | `projects/<id>/logs/gitea-issues/agent-loop.pending` | Chemin du fichier pending (sous ia_dev, par projet). |
| `GITEA_ISSUES_DIR` | répertoire du script | Racine des scripts gitea-issues (pour appeler les scripts mail). |
## Traiter les mails
Dès que le statut est `mails_pending` ou que des mails apparaissent dans `agent-loop.pending` :
1. **Option A (manuel)** : ouvrir le projet dans **Cursor**, lancer l'**agent gitea-issues-process** (commande `/gitea-issues-process` ou via l'interface des agents).
2. **Option B (automatique)** : lancer la boucle avec `AGENT_LOOP_RUN_AGENT=1` et la **Cursor Agent CLI** installée (`agent` dans le PATH). Lorsque des mails non lus sont détectés, le script invoque `agent -p "..." -f` pour exécuter le workflow mails (fil, log, réponse, marquage lu). Voir [Cursor CLI](https://cursor.com/docs/cli/using) pour l'installation.
L'agent lit les non lus, consulte les fils, répond par mail, crée des issues si besoin, et marque les mails comme lus. **Important** : le corps de chaque réponse doit contenir la **réponse réelle** à la question du mail (ex. si on demande « Décrit les rôles », envoyer une description des rôles), jamais le sujet du mail, la question reçue ou un message précédent du fil ; uniquement le contenu rédigé par l'agent en réponse à la demande.
Après passage de l'agent, au prochain tour de la boucle le statut repassera à `idle` et le fichier pending sera vidé (s'il n'y a plus de non lus).
## Logs
Le script n'écrit pas de log structuré par défaut. Pour garder une trace des tours et des messages affichés :
```bash
./ia_dev/gitea-issues/agent-loop.sh 60 2>&1 | tee -a ia_dev/projects/<id>/logs/gitea-issues/agent-loop.log
```
Ou en arrière-plan :
```bash
nohup ./ia_dev/gitea-issues/agent-loop.sh 60 >> ia_dev/projects/<id>/logs/gitea-issues/agent-loop.log 2>&1 &
```
## Arrêter la boucle
- Si le script est en premier plan : `Ctrl+C`.
- Si lancé en arrière-plan : `kill <pid>` (ou `pkill -f agent-loop.sh`).
Après arrêt, le fichier témoin ne sera plus mis à jour ; après 2 × intervalle, il doit être considéré comme inactif.
## Boucle limitée depuis le chat (sans API payante)
Pour lancer depuis le chat Cursor une boucle limitée : récupération des mails → attente 1 min → récupération → … sans consommer de crédits API ni lancer la CLI `agent` :
1. Demander dans le chat : *« Lance la boucle récupération emails : N itérations »* (ou similaire).
2. L'agent exécute `cd <racine_projet> && ./ia_dev/gitea-issues/agent-loop-chat-iterations.sh [N] [--repeat]`. Par défaut N=3 ; `--repeat` = à la fin des N itérations, relancer. Pour 600 itérations avec relance : `... 600 --repeat`.
3. Au lancement, le script envoie un mail de test à nicolas.cantu@pm.me pour vérifier SMTP ; si l'envoi échoue, le script s'arrête (code 1).
4. Chaque itération exécute `mail-list-unread.sh` puis attend 60 secondes. **Log** : expéditeur (From), titres (Subject) et sortie sont écrits dans `projects/<id>/logs/gitea-issues/agent-loop-chat-iterations.log`. Les mails en attente sont dans `projects/<id>/logs/gitea-issues/agent-loop.pending`. Lors de l'envoi d'une réponse, toujours appeler `mail-thread-log.sh append-sent` avec `--body` pour logger la réponse.
Script : `gitea-issues/agent-loop-chat-iterations.sh [N] [--repeat]`. Log : `projects/<id>/logs/gitea-issues/agent-loop-chat-iterations.log`. Boucle illimitée sans relance : `cd <racine_projet> && while true; do ./ia_dev/gitea-issues/mail-list-unread.sh; sleep 60; done`.
## Référence : hooks Cursor
Les **hooks Cursor** permettent d'observer, contrôler ou modifier le cycle de l'agent (et de Tab) via des scripts. Ils ne permettent **pas** de lancer un agent depuis un script externe.
**Documentation officielle** : [cursor.com/docs/agent/hooks](https://cursor.com/docs/agent/hooks).
**Configuration** : fichier `hooks.json` à l'un des emplacements suivants (tous les hooks correspondants sont exécutés) :
- Projet : `/.cursor/hooks.json` (scripts relatifs à la racine du projet, ex. `.cursor/hooks/format.sh`)
- Utilisateur : `~/.cursor/hooks.json` (scripts relatifs à `~/.cursor/`, ex. `./hooks/format.sh`)
**Événements disponibles (Agent / Cmd+K)** : `sessionStart`, `sessionEnd`, `beforeSubmitPrompt`, `beforeReadFile`, `afterFileEdit`, `beforeShellExecution`, `afterShellExecution`, `beforeMCPExecution`, `afterMCPExecution`, `subagentStart`, `subagentStop`, `preToolUse`, `postToolUse`, `postToolUseFailure`, `preCompact`, `afterAgentResponse`, `afterAgentThought`, `stop`.
**Fonctionnement** : chaque hook est un processus lancé par Cursor ; il reçoit du JSON sur l'entrée standard et peut renvoyer du JSON (ex. `continue`, `permission`: allow/deny/ask). Les hooks sont **déclenchés par** une action (ouverture de session, envoi de prompt, exécution shell, etc.) ; ils ne **déclenchent pas** une session ou un envoi de prompt. Il n'existe pas de hook du type « quand le fichier X change, lancer l'agent ».
**Hook « remonter les mails reçus »** : le projet peut définir un hook `sessionStart` ou `beforeSubmitPrompt` qui exécute un script (ex. `.cursor/hooks/remonter-mails.sh`) lisant les mails en attente depuis `projects/<id>/data/issues/*.pending` (spooler) ou `projects/<id>/logs/gitea-issues/agent-loop.pending` (legacy) et renvoyant le contenu pour injection dans le contexte ou le prompt.
**Pour lancer un agent depuis un script** : la seule option côté Cursor est d'appeler la **Cursor Agent CLI** (`agent -p "..." -f`) depuis le script (voir `AGENT_LOOP_RUN_AGENT=1` dans ce document). Les hooks ne remplacent pas cet appel.