139 lines
10 KiB
Markdown
139 lines
10 KiB
Markdown
# 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 lui‑mê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 ci‑dessous) 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.
|