1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
|
# Référence : outils intégrés
> Description neutre des outils déclarés au modèle. Un outil ne renvoie jamais d'erreur Go pour un échec ordinaire : le texte de l'échec est renvoyé au modèle comme sortie de l'outil. Chaque outil enregistre son appel dans le détecteur de boucle ; le même outil, la même entrée et la même sortie trois fois de suite ajoutent `[LOOP_DETECTED: …]` à la sortie.
## `bash`
Déclaré quand `bashTool: true` (la valeur par défaut).
| Champ | Type | Description |
|-------|------|-------------|
| `command` | chaîne | La commande shell à exécuter. |
| Aspect | Comportement |
|--------|--------------|
| Shell | `bash -c <command>`, un nouveau shell par appel, pas de stdin. |
| Répertoire de travail | Le répertoire du processus en mode terminal ; le `cwd` de la session en mode ACP. |
| Délai | 30 s ; la sortie se termine alors par `[error: 30s timeout exceeded]`. |
| Sortie | stdout et stderr fusionnés, tronqués à `maxOutput` caractères. |
| Code de sortie non nul | La sortie se termine par `[exit code: exit status N]`. |
| Affichage terminal | `🛠️ bash: <command>` avant exécution, puis jusqu'à `previewLines` lignes grisées et indentées, ou `(no output)`. |
| ACP | Appel d'outil de genre `execute` ; permission demandée avant exécution ; un refus renvoie `[command rejected by the user]` avec le statut `failed`. |
| Mode fenêtre | Une ligne `‣ bash <commande>` avec statut, durée et sortie repliée. Avec `permissions: ask`, attend `a`/`s`/`r` ; un refus renvoie le même texte qu'en ACP. |
## `read_skill`
Déclaré seulement quand `skillsDir` contient au moins un skill (`<nom>.md` ou `<nom>/SKILL.md`).
| Champ | Type | Description |
|-------|------|-------------|
| `name` | chaîne | Nom du skill, exactement tel que listé dans la description de l'outil. |
| Aspect | Comportement |
|--------|--------------|
| Description | Phrase fixe suivie du catalogue : une ligne ` <name> — <description>` par skill, triées par nom. |
| Sortie | Le markdown complet de `<skillsDir>/<name>.md`, ou de `<skillsDir>/<name>/SKILL.md` s'il n'y a pas de fichier à plat, tronqué à `maxOutput`. |
| Nom inconnu | `No skill named "x". Available skills: a, b, c` |
| Sécurité des chemins | Seul le nom de base de `name` est utilisé ; pas de traversée de répertoire. |
| Affichage terminal | `📖 read_skill: <name>` ; le contenu n'est pas affiché. |
| ACP | Appel d'outil de genre `read` ; pas de dialogue de permission. |
## `read_file`
Déclaré quand `editTools: true`.
| Champ | Type | Défaut | Description |
|-------|------|--------|-------------|
| `path` | chaîne | — | Chemin du fichier, relatif au répertoire de travail. |
| `start` | entier | `0` | Première ligne (base 1) ; `0` = depuis le début. |
| `end` | entier | `0` | Dernière ligne (base 1, incluse) ; `0` = jusqu'à la fin. |
| `numbered` | booléen | `false` | Préfixe chaque ligne de son numéro. |
| Aspect | Comportement |
|--------|--------------|
| Fichier absent | `<path> does not exist. Create it with write_file` |
| Sortie | Les lignes, tronquées à `maxOutput` ; le saut de ligne final n'est conservé que si la fin du fichier est affichée. |
| ACP | Genre `read`, avec le chemin absolu comme emplacement. |
## `write_file`
Déclaré quand `editTools: true`.
| Champ | Type | Description |
|-------|------|-------------|
| `path` | chaîne | Chemin du fichier, relatif au répertoire de travail ; les répertoires parents sont créés. |
| `content` | chaîne | Le contenu entier du fichier. |
| Aspect | Comportement |
|--------|--------------|
| Fichier existant | Entièrement remplacé. |
| Contenu identique | Rien n'est écrit ; le résultat le dit. |
| Sortie | Un en-tête d'une ligne et le diff par rapport au contenu précédent. |
| ACP | Genre `edit` ; permission demandée ; le changement voyage comme un diff (diff de nouveau fichier à la création). |
## `edit_file`
Déclaré quand `editTools: true`.
| Champ | Type | Défaut | Description |
|-------|------|--------|-------------|
| `path` | chaîne | — | Chemin d'un fichier existant. |
| `edits` | tableau de `{old, new}` | — | Remplacements, tous résolus par rapport au fichier original. |
| `edits[].old` | chaîne | — | Texte exact à trouver, présent exactement une fois. |
| `edits[].new` | chaîne | — | Remplacement ; vide supprime le texte trouvé. |
| `dry_run` | booléen | `false` | Rapporte ce qui changerait, n'écrit rien. |
| Règle | Conséquence si violée |
|-------|-----------------------|
| `old` apparaît exactement une fois | Refusé : introuvable, ou ambigu — `old text appears N times (lines 11, 20) — to change ONE of them, include its surrounding lines in old until the text is unique; to change several, send one edit per place…`. |
| Deux éditions ne doivent ni se chevaucher, ni s'imbriquer, ni se répéter | Refus. |
| Les éditions sont résolues sur le texte original, jamais en cascade | — |
| Toute édition refusée | Rien n'est écrit. |
| Aspect | Comportement |
|--------|--------------|
| Sortie | Un en-tête d'une ligne (éditions appliquées, lignes ajoutées et supprimées, première ligne modifiée) et le diff avec numéros de ligne. |
| ACP | Genre `edit` ; permission demandée ; le changement voyage comme un diff avec ancien et nouveau texte. |
## `lsp`
Déclaré quand `lsp.servers` liste au moins un serveur dont la commande est sur le `PATH` au démarrage. Un serveur listé mais introuvable est ignoré avec un `[warning: lsp server <nom> (<commande>) not found on the PATH — skipped]` ; quand aucun n'est trouvé, l'outil n'est pas déclaré et la bannière le dit.
| Champ | Type | Défaut | Description |
|-------|------|--------|-------------|
| `action` | chaîne | — | `definition`, `references`, `hover`, `symbols`, `diagnostics` ou `rename`. |
| `path` | chaîne | — | Chemin du fichier, relatif au répertoire de travail. Son extension choisit le serveur. |
| `symbol` | chaîne | `""` | L'identifiant, tel qu'écrit dans le code. Guillemets et ponctuation parasites sont coupés, un préfixe `receveur.` ou `pkg.` est retiré (`Greet', ` et `person.Greet` désignent tous deux `Greet`). Ses occurrences dans le fichier sont essayées dans l'ordre — lignes de code d'abord, lignes de commentaire en dernier — jusqu'à ce que le serveur réponde. Pour `definition`, `references`, `hover`, `rename`. |
| `line` | entier | `0` | Ligne en base 1. Avec `symbol` : restreint la recherche à cette ligne. Avec `column` : la position exacte. |
| `column` | entier | `0` | Colonne en base 1, en caractères, à l'intérieur de l'identifiant. Nécessaire seulement sans `symbol`. |
| `new_name` | chaîne | `""` | Le nouvel identifiant, pour `rename`. Nettoyé comme `symbol`. Quand il est vide et que `symbol` contient plusieurs mots (`Greet 1 12 Hello` — un petit modèle qui écrit symbole, ligne, colonne et nouveau nom dans une seule chaîne), le premier identifiant est le symbole et le dernier le nouveau nom. |
| Aspect | Comportement |
|--------|--------------|
| Description | Nomme les serveurs et leurs extensions (`Servers available: gopls (.go), …`) et les six actions. |
| `definition`, `references` | Une ligne `chemin:ligne:colonne <ligne source>` par emplacement, triée par chemin puis position, doublons retirés ; les chemins du projet sont relatifs, les autres absolus. `references` est préfixé de `N reference(s):` et inclut la déclaration. |
| `hover` | Le texte du serveur tel qu'envoyé (signature, type, documentation ; Markdown pour la plupart des serveurs). |
| `symbols` | Une ligne `<sorte> <nom> [<détail>] line N` par symbole, enfants indentés de deux espaces. |
| `diagnostics` | Le fichier est synchronisé, puis l'appel attend jusqu'à 3 s la publication du serveur. `N diagnostic(s) in <chemin>:` puis une ligne `<sévérité> chemin:ligne:colonne <message> [<source>]` par constat ; `no diagnostics: <chemin> is clean` ; ` (as last published)` quand le fichier n'a pas changé depuis la dernière publication ; `no diagnostics received for <chemin> within 3s (…)` quand le serveur n'a jamais publié. |
| `rename` | Applique les modifications du serveur aux fichiers sur disque (positions UTF-16 converties), informe le serveur du nouveau contenu, et répond `renamed to <nouveau>: N edit(s) in M file(s)` suivi d'une ligne `chemin: N edit(s)` par fichier. Sans `new_name` : `[lsp: rename needs new_name — call lsp again with {"action": "rename", "path": …, "symbol": …, "new_name": "<the new identifier>"}; do not use edit_file for a rename]`. |
| Positions | Lignes et colonnes en base 1 à la frontière de l'outil, UTF-16 en base 0 sur le fil. Une position au-delà de la fin d'une ligne ou du fichier est refusée, avec le compte. |
| Synchronisation | Avant chaque requête, le fichier est relu sur disque et envoyé en `didOpen` (première fois) ou en `didChange` complet (quand son contenu a bougé) : les modifications faites avec `edit_file` sont vues sans étape supplémentaire. |
| Cycle de vie du serveur | Démarré au premier appel pour l'une de ses extensions, dans la racine du projet (le répertoire où `mm` tourne), gardé pour la session, arrêté (`shutdown`, `exit`, puis le processus) à la sortie de `mm`. Un serveur mort est relancé à l'appel suivant. |
| Échecs | Toujours du texte, jamais une erreur Go : `[lsp: no language server for "x.md" — configured: gopls (.go)]`, `[lsp: "foo" not found in main.go]`, `[lsp: column 40 is past the end of line 3 (15 characters)]`, `[lsp: gopls: hover failed: …]`, `[lsp: no definition found for the symbol at main.go:5:4]`. |
| Affichage terminal | `🔎 lsp: definition main.go greet` (ou `…:9:14` avec une position, `rename main.go greet → hello`), puis la réponse affichée comme la sortie d'une commande. |
| ACP / fenêtre | Sorte `read` pour la navigation, sans permission ; `rename` est de sorte `edit` et demande la permission comme `edit_file` (`[rename rejected by the user]` en cas de refus). |
| Compteur | `🔎 N lsp call(s)` dans la ligne de récapitulatif (voir plus bas). |
## Détection de boucle
Chaque outil enregistre `(outil, entrée, sortie)` dans le détecteur de boucle après chaque appel.
| Situation | Comportement |
|-----------|--------------|
| Le même triplet 3 fois de suite | La sortie de l'outil reçoit `[LOOP_DETECTED: You have attempted the same action multiple times with the same result. Please try a different approach or provide more information.]` en annexe. Rien ne s'arrête. |
| 3 appels identiques de plus (6 de suite) | L'outil renvoie une erreur au lieu de texte et le tour se termine : `[error: the model repeated the same edit_file call 6 times in a row with the same failing result — turn stopped by the loop detector; rephrase the request, or fix what the tool kept reporting]`. L'historique garde tout ce qui a tourné ; l'erreur est enregistrée dans le tableau `errors` du fichier de session (`where: generation`). |
| Un appel différent | Le compte repart. `/new` réinitialise le détecteur ; `/compact` non. |
| Un appel d'outil dont les arguments diffusés répètent un motif (les 256 derniers caractères sont un motif de 4 à 64 caractères répété) ou dépassent 256 Ko | La génération est arrêtée aussitôt : `[error: the model's tool call ran away: its arguments repeat the same pattern (N characters so far: …) — generation stopped; rephrase the request, or try another model]`. Sans cela, un modèle qui déraille compose du charabia pendant des minutes pendant que l'écran affiche « Thinking » (le chien de garde est nourri par chaque fragment). Pendant qu'un appel d'outil est diffusé, le spinner dit `Composing a tool call`. |
Les seuils (3, puis 3 de plus) sont fixés dans `main.go` ; `maxTurns` reste la borne extérieure d'un tour.
## Compteurs affichés après un tour
| Compteur | Compte |
|----------|--------|
| `⚙ N command(s)` | Réponses de l'outil `bash` dans le tour. |
| `📝 N file op(s)` | Réponses de `read_file`, `write_file`, `edit_file`. |
| `📖 N skill(s)` | Réponses de `read_skill`. |
| `🔎 N lsp call(s)` | Réponses de `lsp`. |
|