# 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 && ./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//logs/gitea-issues/agent-loop.log 2>&1 & ``` ## Fichier témoin (actif / inactif) - **Emplacement** : `projects//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//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//logs/gitea-issues/agent-loop.status` | Chemin du fichier témoin (sous ia_dev, par projet). | | `AGENT_LOOP_PENDING_FILE` | `projects//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//logs/gitea-issues/agent-loop.log ``` Ou en arrière-plan : ```bash nohup ./ia_dev/gitea-issues/agent-loop.sh 60 >> ia_dev/projects//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 ` (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 && ./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//logs/gitea-issues/agent-loop-chat-iterations.log`. Les mails en attente sont dans `projects//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//logs/gitea-issues/agent-loop-chat-iterations.log`. Boucle illimitée sans relance : `cd && 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//data/issues/*.pending` (spooler) ou `projects//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.