52 KiB
MAIA
Assistant IA en ligne de commande : un seul agent (MAIA), le LLM se choisit parmi ceux de LiteLLM, l'interface utilise Ink (React pour CLI).
Démarrer
pnpm install # gestionnaire de paquets : pnpm@12.14.0 (champ packageManager, via Corepack)
mkdir -p ~/.maia && cp settings.example.json ~/.maia/settings.json # puis renseigner l'URL de LiteLLM
pnpm dev # le modèle est choisi dans la liste au démarrage
pnpm dev -- --model litellm:gpt-4o # nom du modèle tel que LiteLLM le connaît
pnpm test # test de la boucle agent (provider factice)
pnpm build && node packages/cli/dist/index.js
Lancer MAIA depuis un autre dossier
pnpm dev s'exécute depuis la racine du dépôt : le dossier de travail est alors cette racine (./MAIA.md, ./.maia/skills… y sont cherchés).
Pour utiliser MAIA sur un autre projet ./X, lancez-le depuis ./X :
pnpm build
# une fois : installe la commande `maia` (pnpm 12 n'a plus `pnpm link --global`)
mkdir -p ~/.local/bin && ln -sf "$PWD/packages/cli/dist/index.js" ~/.local/bin/maia && chmod +x packages/cli/dist/index.js
# (~/.local/bin doit être dans le PATH ; alternative : `cd packages/cli && npm link`)
cd ~/X && maia # le dossier de travail est ~/X
# ou sans installer : cd ~/X && node /chemin/vers/maia/packages/cli/dist/index.js
# ou en développement : MAIA_CWD=~/X pnpm --dir /chemin/vers/maia dev
Pourquoi pas pnpm --filter … dev : pnpm exécute alors le script depuis packages/cli, pas depuis le dossier où vous avez tapé la commande. MAIA
tient compte de MAIA_CWD, puis de INIT_CWD (dossier où pnpm/npm a été lancé), puis de process.cwd().
Architecture
| Paquet | Rôle |
|---|---|
core |
Contrats (LLMProvider, Tool, AgentUI…), boucle agent, sessions, !, @fichier, §skill |
providers |
Adaptateurs LLM : litellm (provider unique : requêtes au format OpenAI vers LiteLLM, voir ci-dessous) |
ui |
Interface Ink/React (journal, saisie, listes de choix, confirmations, spinner) |
agent |
L'agent MAIA : contexte (MAIA.md / CLAUDE.md), skills .maia/skills + outils Read/Glob/Grep/Write/Edit/Bash/TodoWrite/WebFetch/CurrentModel |
cli |
Point d'entrée : configuration, boucle de conversation |
Résolution du modèle (pas de défaut : s'il n'est pas défini, il est demandé) : option --model > variable MAIA_MODEL
.maia/config.json(projet) >~/.maia/config.json> défauts.
Il n'y a plus de notion de mode ni d'extensions : un seul agent, MAIA.
Spécifications techniques détaillées (contrats, formats, constantes) : SPECS_TECH.md.
Commandes
| Saisie | Effet |
|---|---|
/help |
aide (commandes, skills) |
/permissions |
affiche les règles de permissions chargées, par fichier : badges ALLOW, ASK, DENY suivis des règles |
/model |
recharge la liste des modèles LiteLLM et change de modèle (conversation conservée) |
/clear |
supprime le contexte de la session et recharge MAIA.md / CLAUDE.md et les skills, comme au démarrage (la session garde son nom) |
/createAgent [nom] |
crée un sous-agent depuis le modèle .ressources/AGENT.md : demande le niveau (projet ./.maia/agents ou global ~/.maia/agents) puis le nom |
/agents |
liste les sous-agents chargés (nom, origine, description) |
/mcp |
liste les serveurs MCP activés : OK (vert) ou FAIL (rouge) |
/compact |
résume la conversation et repart de ce résumé (voir « Compactage ») |
/rename <nom> · /load [nom] · /new |
sessions (voir plus bas) |
/exit (ou Ctrl+D) |
quitte (la session est sauvegardée) |
!commande · @fichier · §skill · &agent |
commande shell, fichier injecté, skill (voir plus bas) |
Option de lancement : --model <spec> (ex. litellm:gpt-4o).
Interface (Ink)
L'interface est une application React rendue par Ink (Ink 7 : React ≥ 19.2 ; vérifiez la version de Node qu'il exige).
Le CLI garde une façade impérative (InkUI : info, select, readInput, confirm, withSpinner…) au-dessus d'un UIStore sans dépendance à React,
ce qui rend la logique testable (store.test.ts). Les composants (saisie, liste, confirmation, spinner) sont écrits à la main ; seuls les badges viennent de @inkjs/ui.
Un terminal interactif (TTY) est requis.
Présentation : la bannière d'accueil reprend le style « MAIA » : image à gauche, affichée telle quelle en couleurs réelles (demi-blocs ▀▄█ : 32 × 32 pixels, soit 32 colonnes × 16 lignes ; sans pixelisation). Elle vient de packages/ui/assets/mask.png ; python3 packages/ui/scripts/make-mask.py [taille] (Pillow) régénère src/mask-data.ts à une autre taille, titre « Maia » en gros caractères pixel avec ombre et dégradé arc-en-ciel à droite, puis trois textes (vert : « Assistant IA en ligne de commande… », rose : « Un seul agent, plusieurs modèles », puis la signature « Arnaud VINCENT » en jaune et « ClaudeCode » en orange). Terminal d'au moins 58 colonnes pour la version complète ; entre 25 et 57 : sans le masque ; en dessous : cadre à double bordure compact, titre centré ;
une ligne vide sépare chaque message.
Messages du journal, avec le composant Badge de @inkjs/ui :
| Badge | Usage | Texte |
|---|---|---|
INFO (bleu) |
information | bleu clair (LIGHT_BLUE) |
WARN (jaune) |
avertissement | par défaut |
FAIL (rouge) |
erreur | #DC8ADD (ERROR_TEXT) |
ASK (vert) |
question posée à l'utilisateur (liste de choix, confirmation o/n), à gauche de la question | gras |
Les réponses de l'agent sont rendues en markdown (titres, listes, code, tableaux, gras…) avec markdansi. Tout est isolé dans packages/ui/src/markdown.tsx : pour le retirer, supprimer ce fichier, la dépendance markdansi et remplacer <Markdown /> par <Text> dans components.tsx. Le texte en cours de streaming reste brut ; la mise en forme apparaît quand la réponse est terminée. MAIA_NO_MARKDOWN=1 désactive le rendu sans toucher au code, et un échec de markdansi retombe sur le texte brut.
Les couleurs sont dans packages/ui/src/colors.ts. Une fois la réponse validée, la question est remplacée par la ligne ◆ Question : choix dans le journal.
Barre de contexte : sous la zone de saisie, en bas à droite, une barre de carrés ■ (de gauche à droite) montre la taille du contexte de la conversation par rapport à la taille maximale du modèle, à l'échelle 1 carré = 10 000 tokens (200 000 tokens → 20 carrés), avec le libellé « 45.7k / 200k · 23% ». Les carrés sont verts, jaunes à partir de 60 %, rouges à partir de 85 %. Si la barre ne tient pas dans le terminal (très grands contextes), l'échelle est agrandie par paliers de 10 000 et indiquée (1■ = 20k).
- Taille maximale : après le choix du modèle (au démarrage, avec
--model/config, et avec/model), MAIA demande au LLM « Taille maximale du contexte pour ce modèle » (réponse en tokens, lue parparseTokenCount:128000,128 000 tokens,200k,1,5 million…). La valeur est annoncée (INFO) ; une réponse par modèle et par exécution. Si la réponse est illisible, ou si la question échoue ou dépasse 60 s (Ctrl+C l'abandonne), un avertissement s'affiche et la barre est masquée. Un LLM peut se tromper sur sa propre limite : la valeur affichée en INFO permet de la vérifier. - Contexte utilisé = tokens de tout ce que la session enverrait au modèle : prompt système (MAIA.md…), définition des outils, messages (texte, appels d'outils et leurs résultats, +4 tokens de structure par message) et contexte en attente (résultats de commandes
!, fichiers@). Le calcul est local et déterministe, avec js-tiktoken (vocabulaireo200k_base) ; la consommation renvoyée par le serveur n'est plus utilisée, car elle faisait varier la valeur. C'est une somme des comptes de chaque élément : la valeur ne fait qu'augmenter quand la conversation s'enrichit, et ne diminue que si le contexte est réduit (/clear,/compact, tour interrompu, autre session). Le comptage est refait avant chaque saisie de l'utilisateur (et après chaque réponse du LLM) ; le compte de chaque message est mémorisé, donc le recalcul reste instantané. Pour les modèles qui n'utilisent pas ce vocabulaire (Claude, Mistral…), la valeur est une bonne approximation, pas le compte exact du serveur.
La zone de saisie est encadrée par une bordure en pointillés (┈) au-dessus et au-dessous seulement, en greenBright, sans bords gauche ni droit.
À sa gauche, le spinner arrow3 de cli-spinners s'anime en continu. Pendant l'attente de la réponse après validation de la saisie, « En réflexion » s'affiche avec le spinner dots10 à sa gauche, en yellowBright (même spinner pour les autres attentes : chargement des modèles, exécution d'une commande !).
| Contexte | Touches |
|---|---|
| Saisie | Entrée : envoyer · ←/→, Ctrl+A/E : se déplacer · ↑/↓ : historique · Ctrl+U/W : effacer · Ctrl+C ou Ctrl+D (saisie vide) : quitter · le collage multi-lignes est conservé |
Liste (/model, /load, choix initial) |
↑/↓ (ou j/k) · Entrée : valider · Échap : annuler |
| Confirmation d'une action (Write, Edit, Bash…) | o : autoriser · n ou Échap : refuser · Entrée seule est ignorée (réponse explicite exigée) |
| Pendant un tour de l'agent | Ctrl+C : interrompt la requête et restaure la conversation avant cette demande |
Dossier de base
Le dossier où MAIA est lancé (ex. ./X) est la base de tous les chemins relatifs : fichiers lus, créés ou modifiés par les outils
(Read, Write, Edit, Glob, Grep, Bash), .maia/config.json, MAIA.md / CLAUDE.md et .maia/skills/. Un chemin complet est utilisé tel quel
(~/… désigne le dossier personnel). Ce dossier est capturé une fois au démarrage, affiché (« Dossier de travail : … ») et transmis aux outils et aux providers ;
les règles de permissions (Read(./.env), Edit(src/**)) sont elles aussi relatives à ce dossier. Seul ~/.maia (config, settings.json et skills utilisateur) est lu depuis le dossier personnel.
Commande ! : exécuter une commande shell
!commande exécute la commande directement, sans passer par le modèle. Deux syntaxes :
| Saisie | Commande exécutée |
|---|---|
!ls · !ls -la src |
tout ce qui suit le ! |
!'ls -la' |
le texte entre guillemets simples (délimite la commande avec ses paramètres) |
!'ls' -la src · !'ls -la' | wc -l |
le texte entre guillemets, puis ce qui suit est ajouté comme paramètres : ls -la src, ls -la | wc -l |
!'echo "l\'ami"' |
\' donne un guillemet simple littéral dans la commande |
!'ls -la |
erreur (guillemet non fermé) : rien n'est exécuté |
Le reste du comportement :
- elle tourne dans le dossier de lancement (voir « Dossier de base ») ; un
cdn'a donc pas d'effet durable ; - stdout et stderr sont affichés dans l'ordre d'arrivée, avec le code de sortie en cas d'échec ; durée maximale 2 minutes ;
- aucune confirmation n'est demandée (c'est vous qui la tapez) et les règles de permissions des outils ne s'appliquent pas ;
- le résultat est ajouté au contexte de votre prochaine demande (jusqu'à 30 000 caractères, milieu tronqué au-delà), pour que le modèle sache ce qui a été lancé.
/clearl'efface ; - Ctrl+C interrompt la commande ;
!seul affiche l'aide. Les programmes interactifs (vim, less, saisie au clavier…) ne sont pas pris en charge.
Références @fichier
@chemin/vers/fichier dans votre message injecte le contenu du fichier dans le contexte de la demande (en plus de le laisser dans le texte) :
| Saisie | Effet |
|---|---|
explique @src/a.ts |
injecte src/a.ts |
compare @a.ts et @ b.ts |
plusieurs fichiers ; la forme @ chemin (avec espace) est acceptée |
lis @'mon dossier/a b.txt' |
guillemets simples pour un chemin avec espaces (\' : guillemet littéral) |
résume @docs |
un dossier : la liste de ses entrées est injectée |
@/chemin/complet.txt · @~/notes.md |
chemin complet, ou depuis le dossier personnel |
- Chemins relatifs au dossier de lancement (voir « Dossier de base »). Aucune confirmation : c'est vous qui citez le fichier, les règles de permissions des outils ne s'appliquent pas.
- Un
@motqui ne désigne aucun fichier existant reste du texte ordinaire (@mention,@Override,jean@exemple.fr) ; un avertissement n'apparaît que s'il ressemble à un chemin (@src/x.tsintrouvable). - Limites : 2 Mo par fichier, 50 000 caractères injectés par fichier (milieu tronqué au-delà) et 150 000 par message ; les fichiers binaires sont ignorés ; un même fichier cité plusieurs fois n'est injecté qu'une fois.
- Le contenu est marqué comme donnée, pas comme instructions. Les
@fichierécrits dans le corps d'une commande personnalisée ne sont pas encore traités (seuls ceux de votre saisie le sont). - Pas d'autocomplétion des chemins à la saisie pour l'instant.
Sessions (./.maia/.sessions)
Au lancement, ./.maia et ./.maia/.sessions sont créés dans le dossier d'exécution s'ils n'existent pas. Les sessions sont une fonctionnalité de l'application.
- Sauvegarde automatique à la sortie (
/exit, Ctrl+D, SIGTERM/SIGHUP). Une session sans aucun message n'est pas enregistrée. - Nom par défaut :
session_{date}_{heure}_{minutes}_{secondes}(ex.session_2026-10-13_01_04_05). /rename <nom>: renomme la session courante ; ce nom sera utilisé à la sauvegarde./load: sans argument, liste les sessions enregistrées (sélection) ;/load <nom>recharge cette session (le modèle courant est conservé)./new: nouvelle session, nouveau contexte (MAIA.md/CLAUDE.mdet skills rechargés) ; l'utilisateur est averti que la session quittée est sauvegardée (sous son nom).
Chaque session est un fichier JSON : elle peut contenir des extraits de fichiers ou de sorties de commandes. .maia/ est ajouté au .gitignore.
Skills : appel direct avec §
§nom_du_skill [arguments] lance un skill de l'agent (ex. §sk_test) ; § seul liste les skills disponibles.
L'agent expose ses skills via ModeInitResult.skills (contrat AgentSkill : name, description, invoke(args)), ce qui
rend l'appel indépendant du format propre à l'agent. invoke retourne le prompt envoyé au LLM. Ce prompt contient
les instructions du SKILL.md (.maia/skills/<nom>/) (avec $ARGUMENTS / ARGUMENTS:), sans passer par l'outil Skill du modèle.
Accès aux LLMs : LiteLLM
MAIA ne parle qu'à un serveur LiteLLM : toutes les requêtes de tous les agents sont envoyées au format OpenAI
(chat/completions, en streaming, avec appels d'outils) vers le endpoint OpenAI de LiteLLM.
Au démarrage, le fichier ~/.maia/settings.json (dossier de l'utilisateur, pas celui de lancement) est chargé :
{
"LiteLLM_URL": "http://localhost:4000",
"OpenAI_EndPoint": "/v1/chat/completions",
"LiteLLM_Models": "/v1/models"
}
LiteLLM_URL: URL du serveur LiteLLM.OpenAI_EndPoint: suffixe ajouté àLiteLLM_URLpour envoyer une requête au format OpenAI.LiteLLM_Models: suffixe ajouté àLiteLLM_URLpour récupérer la liste des modèles disponibles.- Optionnel :
LiteLLM_API_Key(ou variable d'environnementLITELLM_API_KEY), envoyée enAuthorization: Bearer …. Ce fichier peut donc contenir un secret : ne pas le versionner. - Optionnel :
MaxIterations(entier, défaut 50) : étapes (appel du LLM puis des outils) par série avant que MAIA demande s'il faut continuer. - Optionnel :
MaxOutputTokens(entier, ex.16000) :max_tokensenvoyé à chaque requête. Sans lui, le serveur applique sa limite par défaut, parfois courte : un long fichier écrit par l'outilWritepeut être coupé (le paramètrecontentarrive alors absent). MAIA affiche un avertissement quand la réponse est coupée (finish_reasonlength, ou sortie qui tombe pile sur une limite usuelle 4096/8192… avec un appel d'outil : certains serveurs « réparent » alors l'appel et annoncenttool_calls). Cas constaté avecanthropic--claude-4.5-sonnet:completion_tokens: 4096etWritereçu avecpathseul → fixer par ex."MaxOutputTokens": 32000. - Diagnostic :
MAIA_DEBUG_LLM=1 maiaenregistre le flux brut renvoyé par LiteLLM dansmaia-llm-debug.log(dossier temporaire ;MAIA_DEBUG_LLM=/chemin/fichier.logpour choisir le fichier).
Si le fichier est absent ou incomplet, MAIA s'arrête avec un message qui en donne un exemple.
Choix du modèle : au démarrage (sauf si --model ou MAIA_MODEL le définit), MAIA récupère les modèles auprès de LiteLLM
et demande de choisir celui de la session. /model rouvre cette liste en cours de session (la conversation est conservée).
Spécification explicite : litellm:<nom du modèle> (ou le nom seul).
Agent MAIA : contexte et skills
Au lancement, le contexte de départ est chargé depuis le dossier de lancement : MAIA.md s'il existe, sinon CLAUDE.md (s'il existe).
Le fichier chargé est indiqué par un message INFO ; si aucun des deux n'existe, un WARN le signale. Seul ce dossier est regardé (pas les dossiers parents).
Les dossiers .claude ne sont plus lus (ni settings.json, ni commands/, ni skills/, ni .claude/CLAUDE.md, ni ~/.claude).
-
Skills :
.maia/skills/<nom_du_skill>/SKILL.md(ouskill.md), chargés depuis trois emplacements. Le plus spécifique l'emporte en cas de même nom :- le dossier utilisateur :
~/.maia/skills; - le dossier parent du dossier de lancement : ex.
/X/.maia/skillspour un lancement dans/X/Y; - le dossier de lancement : ex.
/X/Y/.maia/skills.
Le fichier commence par un en-tête YAML :
description(objectif du skill) etname(optionnel ; s'il est présent, il doit être identique au nom du dossier, qui fait foi). Seule la liste (nom + description) est mise dans le prompt ; le contenu est chargé à la demande par l'outilSkill, ou lancé directement avec§nom. - le dossier utilisateur :
---
name: sk_test # optionnel, = nom du dossier
description: Lance les tests du projet et résume les échecs
---
Instructions du skill… ($ARGUMENTS = arguments passés avec §sk_test)
Conséquence : il n'y a plus de variables d'environnement venant de la configuration de l'agent ni de slash-commandes personnalisées. Les permissions sont décrites dans la section suivante.
Permissions
Guide complet avec exemples (commande simple, commande avec tube, interdiction d'un
rmcaché) :GUIDE_PERMISSIONS.md.
Les permissions s'appliquent aux outils (intégrés ou non). Trois types de règles :
| Type | Effet |
|---|---|
allow |
l'outil est utilisé sans approbation manuelle |
ask |
l'utilisateur doit confirmer chaque utilisation (badge ASK, réponse o (oui), n (non) ou s (stop, voir ci-dessous)) |
deny |
l'outil ne peut pas être utilisé (l'agent reçoit un refus) |
Ordre d'évaluation : deny, puis ask, puis allow. La première famille qui correspond décide, quelle que soit la précision des règles : un allow étroit comme Bash(aws s3 ls) ne fait pas exception à un deny large comme Bash(aws *), ni à un ask plus large. Sans règle applicable, c'est ask qui s'applique (pour tous les outils, y compris Read, Glob, Grep…).
Où les définir : ~/.maia/settings.json (utilisateur) et ./.maia/settings.json (dossier de lancement). Les listes des deux fichiers s'additionnent. Le fichier ../.maia/settings.json du dossier parent n'est pas pris en compte. Dans chaque fichier, la propriété permissions contient une liste par type, tous facultatifs :
"permissions": {
"allow": ["Bash(allow stop *)"],
"ask": ["Bash(allow ask *)"],
"deny": ["Bash(allow deny *)", "Read(./.deny.md)"]
}
Format d'une règle : Outil (tous les appels de l'outil) ou Outil(motif) :
| Outil | Le motif porte sur | Exemples |
|---|---|---|
Bash |
la commande ; * = n'importe quelle suite ; cmd * couvre cmd avec ou sans arguments (mais pas cmdx) ; sans * : commande exacte |
Bash(allow stop *), Bash(git status), Bash(npm run test:*) |
Read, Write, Edit, Glob, Grep |
le chemin (relatif au dossier de lancement, motifs glob) | Read(./.deny.md), Edit(src/**) |
WebFetch |
le domaine | WebFetch(domain:docs.example.com), WebFetch(domain:*.example.org) |
Skill |
le nom du skill | Skill(pdf) |
| autres outils (ex. ajoutés plus tard) | une valeur texte de l'entrée de l'outil (* accepté) |
mcp__db__query(SELECT *) |
Précisions sur Bash :
- commande composée (
&&,;,|) : undenyou unasks'applique dès qu'une des parties correspond, y compris cachée dans$(…), des backticks ou des parenthèses ; unallowne s'applique que si toutes les parties sont couvertes ; - un
allowne couvre jamais une commande avec substitution ($(…), backticks) : elle repasse enask; une redirection de sortie (>,>>,2>,&>) repasse enasksauf si le fichier visé est autorisé par une règleRedirect(chemin)(2>&1n'écrit dans aucun fichier : sans effet).deny: ["Redirect(.env)"]interdit d'écrire dans ce fichier par redirection, même avecBashautorisé. Détails :GUIDE_PERMISSIONS.md§ 8.
Au démarrage, MAIA indique (INFO) les fichiers chargés et le nombre de règles, ou qu'aucune règle n'est définie ; un JSON invalide ou une règle mal formée donne un WARN et est ignoré. Les deny sont du « meilleur effort » : ils ne remplacent pas un vrai bac à sable. Les commandes ! et les références @fichier tapées par l'utilisateur ne passent pas par ces règles.
Attention : ./.maia/settings.json d'un dépôt tiers peut contenir des règles allow larges ; relisez-le avant de lancer MAIA dans un projet que vous ne connaissez pas.
Réponse « stop » : à une demande d'autorisation ASK … (o/n/s=stop), s signifie « je ne réponds pas à la question, je veux interrompre pour donner d'autres informations ou faire autre chose ». L'action demandée n'est pas exécutée (le modèle en est informé), les autres actions qui suivaient dans la même réponse ne sont pas lancées (celles déjà autorisées vont à leur terme), puis MAIA rend la main avec le message « Itération interrompue (stop) ». Rien n'est annulé : la conversation et les résultats déjà obtenus restent dans le contexte, et ce que vous tapez ensuite est ajouté à la suite (contrairement à Ctrl+C, qui restaure la conversation d'avant la demande). « stop » n'existe que pour les confirmations d'outils de l'agent principal (pas pour « Continuer ? » à la limite d'itérations, ni pour les confirmations d'un sous-agent, où s est ignoré).
La commande /permissions affiche les règles actives, fichier par fichier, avec un badge ALLOW (vert), ASK (jaune) et DENY (rouge) suivi des règles du type, une par ligne ; seuls les types non vides sont montrés. Après avoir modifié un settings.json, /clear recharge la configuration.
Exemple pour éviter les confirmations sur les outils en lecture seule : voir settings.example.json.
Outils TodoWrite, WebFetch et CurrentModel
- TodoWrite : liste de tâches de la session (
pending/in_progress/completed, formeactiveFormpour la tâche en cours). Chaque appel remplace la liste ; le résultat (☐ ▶ ☑) est affiché à l'utilisateur et renvoyé au modèle. Un avertissement est ajouté si plusieurs tâches sont « en cours ». - CurrentModel : renvoie le modèle LLM utilisé par la session (valeur à jour après un
/model). Sans paramètre, sans confirmation ; le modèle est lu au moment de l'appel. - WebFetch : télécharge une URL http(s) (HTML converti en Markdown avec
turndown, JSON mis en forme, texte brut ; 5 Mo et 30 s maximum,max_lengthcaractères renvoyés). Pas de JavaScript exécuté, pas de PDF ni d'images.- Confirmation demandée par défaut ; règle
allowpossible :WebFetch(domain:docs.example.com)(voir « Permissions »). - Une redirection vers un autre domaine n'est pas suivie : l'outil renvoie la nouvelle URL, que le modèle doit rappeler (ce qui repasse par la confirmation).
- Le contenu est signalé au modèle comme non fiable (risque d'injection de prompt) ; il ne passe par aucun second modèle de résumé.
- Confirmation demandée par défaut ; règle
Outils personnalisés (./.maia/tools)
L'utilisateur peut créer ses propres outils programmatiques : le LLM les appelle comme n'importe quel autre outil quand leur description correspond au besoin. Ils sont chargés au démarrage et rechargés par /clear et /new.
- Où :
./.maia/tools(dossier de lancement) et~/.maia/tools(utilisateur, valable pour tous les projets) ; jamais le dossier parent. À nom égal, l'outil du projet l'emporte. Un sous-dossier = un outil :./.maia/tools/addition/TOOL.json(tool.jsonaccepté aussi) et ses scripts. Un exemple complet est fourni dansexamples/tools/addition; exemple de sous-agent :examples/agents/code_reviewer(à copier dans./.maia/agents).(à copier dans./.maia/tools/). TOOL.json:
{
"name": "Addition",
"description": "Faire une addition avec 3 chiffres. Renvoie un chiffre, la somme",
"command": {
"parameters": [
{ "name": "param1", "type": "number", "default": "0" },
{ "name": "param2", "type": "number", "default": "0" },
{ "name": "param3", "type": "number", "default": "0" }
],
"run": "node ./test.js {{param1}} {{param2}} {{param3}}"
}
}
| Champ | Règle |
|---|---|
description |
Obligatoire : dit au LLM à quoi sert l'outil et quand l'appeler. |
name |
Facultatif, le nom du dossier par défaut. Lettres, chiffres, _ et - (64 caractères maximum). Ne peut pas reprendre le nom d'un outil intégré (Read, Bash…) ni d'un autre outil personnalisé. |
command.parameters |
Paramètres fournis par le LLM. type : "number" ou "string". default : valeur utilisée si le LLM n'en donne pas (ex. {{param2}} devient 0) ; sans default, le paramètre est obligatoire. description facultative par paramètre. |
command.run |
Ligne de commande ; chaque {{paramètre}} est remplacé par sa valeur. ./ désigne le dossier de l'outil (./test.js = ./.maia/tools/addition/test.js) et c'est le dossier d'exécution. |
- Sécurité : pas de shell. La ligne
runest découpée en arguments (guillemets simples ou doubles possibles) et exécutée directement : une valeur envoyée par le LLM reste toujours un seul argument (; rm -rf /ou$(cmd)sont reçus tels quels par le script, jamais interprétés). En contrepartie,runn'accepte ni tubes|, ni redirections>, ni variables$HOME; pour cela, appeler un script (sh ./script.sh {{p}}). Le programme (premier mot) ne peut pas être un{{paramètre}}. Sous Windows, les scripts.cmd/.batne sont pas lancés directement (utilisernode,python, oucmd /c ./script.cmd). - Exécution : timeout de 2 minutes, sortie limitée à 30 000 caractères ; un code de sortie non nul est renvoyé au LLM comme une erreur (avec la sortie d'erreur du script). Le script reçoit
MAIA_CWD(dossier de lancement de MAIA, pour retrouver le projet) etMAIA_TOOL_DIR. - Permissions : un outil personnalisé exécute un programme, donc il demande confirmation par défaut. Les règles de permissions s'appliquent avec son nom :
"Addition"dansallowl'autorise sans demander,"Addition(5)"cible une valeur de paramètre. - Erreurs : une définition invalide (description absente, type inconnu,
{{param}}non déclaré, JSON cassé…) est signalée par un avertissement (WARN) et l'outil est ignoré ; les autres restent chargés. Une virgule en trop avant]ou}est tolérée. Au chargement, les outils valides sont annoncés (INFO).
Exécution des outils : premier plan et parallèle
- Le raisonnement du LLM reste bloquant : l'utilisateur attend la réponse, ce qui préserve l'intégrité du contexte.
- 1 outil demandé : exécuté au premier plan (annonce, confirmation éventuelle, exécution, résultat).
- 2 outils ou plus dans la même réponse : chaque outil part dans sa propre tâche, en arrière-plan, dès qu'il est autorisé. L'agent attend la fin de toutes les tâches, puis renvoie les résultats au LLM dans l'ordre où il les a demandés. Une erreur ou un refus n'annule pas les autres outils.
- Le spinner indique « Outils en parallèle : n/total terminés (…) ».
- Limites : ce sont des tâches asynchrones Node (pas des threads système ; les processus lancés par Bash ou les outils personnalisés tournent réellement en parallèle) ; 8 outils au plus en même temps (
MAX_PARALLEL_TOOLS) ; les confirmations sont posées une par une ; les appels qui portent sur le même fichier (Read/Write/Edit) ou TodoWrite passent l'un après l'autre (Tool.lockKey). - Code :
packages/core/src/tool-runner.ts, teststool-runner.test.ts.
Compactage (/compact et automatique)
Le compactage résume toute la conversation de la session pour libérer du contexte.
- La conversation actuelle est sauvegardée telle quelle sous son nom.
- Le LLM reçoit la demande « Faire un résumé compacté de nos échanges… » (5 rubriques : Contexte du projet, Objectif actuel, Fichiers pertinents, Décisions/Conclusions, Prochaines étapes), limité à 4000 tokens. Les commandes
!et fichiers@pas encore envoyés font partie de ce qui est résumé. - Le contexte est supprimé comme avec
/clear, maisMAIA.md/CLAUDE.mdne sont pas rechargés : le résumé les remplace comme contexte de départ (dans le prompt système). Outils, skills et permissions sont rechargés. - La session continue sous le nom
{nom d'origine}__{n}:xxx→xxx__1→xxx__2… Après__1000, le suivant reprend à__1et écrase ce fichier. Le fichier d'avant compactage reste intact ; le fichierxxx__ncontient le résumé (startContext) et la suite de la conversation, et/load xxx__nle restaure. - Le nombre de tokens est recalculé et la barre de progression mise à jour.
Déclenchement automatique : dès que le contexte dépasse Seuil_max = Max_Token_Context - (4000 + 500) (4000 = tokens du résumé, 500 = marge d'erreur). La taille maximale est celle demandée au LLM au choix du modèle ; si elle est inconnue (barre masquée), seul /compact fonctionne. Le contrôle a lieu avant l'envoi de chaque demande et après chaque tour.
L'appel est bloquant (pas de tâche de fond). Ctrl+C l'annule sans rien modifier ; en cas d'erreur, la conversation reste inchangée.
Code : packages/core/src/compact.ts, sessions.ts (nextCompactedName, startCompacted), packages/agent/src/init.ts (startContext), orchestration dans packages/cli/src/index.ts.
Contexte refusé par le serveur : si LiteLLM répond « Input is too long » (ou équivalent), MAIA compacte automatiquement la conversation (résultats d'outils tronqués à 1 500 caractères dans la demande de résumé, pour qu'elle tienne), puis reprend la demande une seule fois. Si le compactage échoue aussi, l'erreur d'origine est affichée (utilisez alors /new). Les sous-agents n'ont pas ce mécanisme.
Sous-agents (./.maia/agents)
MAIA peut confier une tâche à un sous-agent : un processus Node autonome, avec sa propre conversation (vide) et sa propre définition. L'agent principal le lance avec l'outil Agent (agent = nom, task = tâche complète : le sous-agent ne voit rien de la conversation principale). Seul le résumé final du sous-agent revient dans la conversation principale. Plusieurs appels Agent dans une même réponse s'exécutent en parallèle.
Définition : un dossier par agent, avec un fichier AGENT.md, dans ./.maia/agents/<nom>/ (dossier de lancement) ou ~/.maia/agents/<nom>/ (utilisateur) ; le projet l'emporte sur l'utilisateur à nom égal. Créer un agent : /createAgent (ou /createAgent mon_agent) demande le niveau (Projet / Global) et le nom, puis copie le modèle .ressources/AGENT.md dans <dossier agents>/<nom>/AGENT.md en y mettant le bon name: (jamais d'écrasement d'un agent existant). Le modèle explique chaque propriété en commentaires (#) ; les propriétés facultatives y sont commentées, supprimez le # pour les activer. Tapez ensuite /clear pour charger le nouvel agent. Le dossier .ressources est celui de MAIA (racine du projet MAIA, ou MAIA_RESSOURCES), pas celui de votre projet. Les commentaires # … en fin de ligne de l'en-tête sont acceptés (mettez la valeur entre guillemets si elle contient #).
Les agents chargés sont listés au démarrage (« Agents chargés : ») et avec /agents.
---
name: code_reviewer # doit être le nom du dossier
description: Révise du code (sécurité, anti-patterns). Ne pas l'utiliser pour écrire du code.
model: sonnet # optionnel — défaut : modèle de l'agent principal
tools: Read, Grep, Write # optionnel — défaut : mêmes outils que l'agent principal (sans Agent)
permissions: # optionnel — s'ajoutent aux règles héritées
allow: [Read, Grep]
deny: [Bash]
effort: medium # low | medium | high — défaut medium
skills: [api-docs, testing-guide] # optionnel — défaut : tous les skills
omitMd: false # défaut false : MAIA.md / CLAUDE.md ajouté au contexte
trace: false # défaut false : true enregistre la conversation (voir plus bas)
---
# Instructions system
Tu es un expert en révision de code.
# Missions
- identifier les problèmes de sécurité
- détecter les anti-patterns
- suggérer des améliorations
# Constraints (optionnelle)
- ne modifie aucun fichier : lecture seule
- si une faille CRITICAL est trouvée, ne tente pas de la corriger : signale-la en premier pour que l'utilisateur décide
- si la demande sort du périmètre, arrête-toi et indique qui doit prendre le relais
# Output
Pour chaque problème : catégorie (CRITICAL / HIGH / MEDIUM / LOW), description, code concerné, solution recommandée.
- Les trois sections « Instructions system », « Missions » et « Output » sont obligatoires (titres Markdown de n'importe quel niveau). La section « Constraints » (ou « Contraintes »), à placer après Missions, est facultative : les limites de l'agent, c'est-à-dire ce qu'il n'a pas le droit de faire et dans quelles conditions il doit s'arrêter pour demander l'intervention de l'utilisateur ou d'un tiers. Un sous-agent ne pouvant pas poser de question, devant un de ces cas il n'agit pas et le signale dans son résumé (ce qui bloque, ce qui est attendu, de qui). C'est une consigne donnée à l'agent ; les interdits techniques se garantissent avec
toolsetpermissions; un agent invalide est ignoré avec un avertissement qui dit pourquoi. - Héritage : sans
tools,skillsoupermissions, le sous-agent a les mêmes outils (y compris les outils personnalisés), skills et règles que l'agent principal. Les règlespermissionsde l'agent s'ajoutent aux règles héritées :deny > ask > allowreste vrai, undenyouaskhérité n'est jamais levé. Un sous-agent ne peut pas lancer de sous-agent. - Résumé : la dernière réponse du sous-agent suit la section
Output; s'il épuise son budget d'étapes sans conclure, un appel supplémentaire lui demande ce résumé. - Effort : borne le nombre d'étapes du sous-agent (low 10, medium 25, high 50) et module son prompt.
- Modèle : identifiant exact ou fragment (
sonnet→ premier modèle LiteLLM dont le nom contient « sonnet ») ; introuvable → modèle de l'agent principal, avec avertissement. - Autorisations : si le sous-agent doit demander une confirmation, la question s'affiche dans l'interface principale, préfixée
[sous-agent nom](une question à la fois). Ctrl+C interrompt aussi les sous-agents en cours. - Lancement par l'utilisateur : une saisie qui commence par
&lance un sous-agent directement :&code_reviewer relis src/a.ts(le nom est reconnu sans tenir compte de la casse ; sans tâche, l'agent exécute sa mission telle que décrite).&seul liste les agents. Aucune confirmation de l'outilAgentn'est demandée (c'est vous qui lancez), mais les autorisations des outils du sous-agent suivent les règles habituelles. Le résumé s'affiche et est joint à votre prochaine demande, comme le résultat d'une commande!. Ctrl+C interrompt le sous-agent. - Fin de mission : un message INFO est affiché dans l'interface principale (durée, nombre d'appels d'outils) ; un échec est signalé par un avertissement et renvoyé à l'agent principal comme erreur d'outil. Délai maximal : 30 minutes.
- Trace (
trace: true) : la conversation du sous-agent est enregistrée dans./.maia/.sessions/sous le nom{session de l'agent principal}-{agent}, ex.session_2026-10-20_00_01_53-agent_1.json(_2,_3… si le nom est pris). Sanstrace, rien n'est enregistré.
Serveurs MCP (distants)
MAIA peut utiliser les outils de serveurs MCP accessibles par une URL (Streamable HTTP, ou l'ancien transport SSE). Pas d'OAuth ; seuls les outils MCP sont utilisés (pas les ressources ni les prompts).
1. Déclarer les serveurs (global uniquement) : ~/.maia/mcp.json. La clé est le nom du serveur.
{
"mcpServers": {
"github": {
"type": "http",
"url": "https://api.githubcopilot.com/mcp/",
"headers": { "Authorization": "Bearer ${GITHUB_TOKEN}" },
"excludeTools": ["delete_repository"]
},
"docs": { "url": "https://docs.example.com/mcp", "timeout": 60 }
}
}
| Propriété | Rôle |
|---|---|
url |
obligatoire (http:// ou https://) |
type |
http (défaut, Streamable HTTP) ou sse (ancien transport) |
headers |
en-têtes HTTP (jetons, clés d'API) |
timeout |
secondes, défaut 30 : connexion et chaque appel |
includeTools / excludeTools |
liste de noms d'outils (* accepté) retenus / retirés |
enabled |
false : le serveur est ignoré |
description |
commentaire libre |
${VARIABLE} est remplacé par la variable d'environnement (dans url et headers) ; une variable absente donne un avertissement et une valeur vide.
2. Activer par projet : aucun serveur n'est actif par défaut. Il faut l'autoriser dans le settings.json du projet :
{ "permissions": { "allow": ["Mcp(github)"], "deny": ["Mcp(github,delete_repository)"] } }
Mcp(serveur): active le serveur, tous ses outils sont autorisés sans confirmation ;Mcp(serveur,outil): un outil (*accepté, ex.Mcp(github,delete_*)) ; endenyl'outil n'est jamais proposé au modèle ; enaskil demande confirmation ;Mcp(*)(=Mcp(*,*)) : tous les serveurs déclarés et tous leurs outils ;Mcp(*,outil): cet outil sur tous les serveurs ;allow: ["Mcp(github)"]+deny: ["Mcp(github,*)"]: serveur désactivé, MAIA ne cherche pas à s'y connecter.
Les permissions sont traitées avant toute connexion ; seuls les serveurs activés sont ensuite contactés (en parallèle). Un serveur en échec n'empêche pas MAIA de démarrer : avertissement, état FAIL.
3. Utilisation : les outils s'appellent mcp__serveur__outil. /mcp liste les serveurs activés : bandeau vert OK s'il est connecté, bandeau rouge FAIL (avec la cause en grisé) sinon. Les connexions sont coupées à la sortie de MAIA (/exit, Ctrl+D, SIGTERM/SIGHUP) et refaites à chaque /clear, /new, /load, /model et /compact. Les sous-agents n'utilisent pas les outils MCP.
Reste à faire
- Provider LiteLLM : validation sur un vrai serveur (streaming SSE et appels d'outils testés seulement sur des flux simulés) ; compatibilité de
@inkjs/uiavec Ink 7 à confirmer à l'usage (l'affichage des badges a été vu fonctionner par l'utilisateur). - Agent : hooks ; MCP : OAuth, ressources/prompts, outils MCP pour les sous-agents ; sous-agents :
reasoning_effortdu modèle non utilisé (l'effort borne les étapes), pas de sous-agent imbriqué. - Éventuellement rétablir sous
.maiace que supprimait l'abandon de.claude: variables d'environnement et slash-commandes personnalisées (le code des commandes existe encore danspackages/agent/src/commands.ts, il n'est plus appelé). @fichierdans le corps d'une commande personnalisée ; autocomplétion des chemins@à la saisie.- Liste à cocher (choix multiples) : n'existe pas encore ; elle recevrait le badge
ASK. - Taille de contexte : lue en interrogeant le LLM (demande de l'utilisateur) ; la route
/model/infode LiteLLM (max_input_tokens) serait plus fiable, et un réglage manuel danssettings.jsonpourrait corriger une réponse erronée. - Nerd Fonts (icônes) : proposé comme réglage optionnel dans
settings.json, non réalisé. - Tests : les suites vitest et le typecheck sont à lancer après
pnpm install(écrits sans avoir pu être exécutés dans l'environnement de développement).
Travaux réalisés
Journal des évolutions, dans l'ordre :
- Squelette : monorepo pnpm (TypeScript strict, tsup, vitest), contrats
LLMProvider/Tool/AgentUI, boucle agent avec appels d'outils, agent par défaut de type « Claude Code » (outils Read, Write, Edit, Bash). - Provider SAP AI Core puis choix du modèle au démarrage et commande
/model. (Remplacé ensuite par LiteLLM, voir 12.) - Outils Glob et Grep (JavaScript pur, sans ripgrep).
- Configuration de l'agent (lot 2) : fichiers de contexte, permissions, slash-commandes, skills. (Abandonné avec les dossiers
.claude, voir 13.) §skill: appel direct d'un skill de l'agent.- Dossier de lancement : tous les chemins relatifs partent du dossier où MAIA est lancé (
MAIA_CWD>INIT_CWD>cwd). - Interface Ink (remplace Clack.js) : bannière « MAIA » avec masque pixel-art et titre arc-en-ciel, messages en bleu clair, zone de saisie en pointillés
greenBrightavec spinnerarrow3, attente « En réflexion » avecdots10enyellowBright. - Outils TodoWrite et WebFetch (lot 3, première partie).
!commande(avec la syntaxe!'commande avec paramètres') et@fichier.- Renommage en MAIA (titre « Maia ») et sessions :
./.maia/.sessions, sauvegarde automatique,/rename,/load(avec réaffichage du contenu),/new. - Corrections : import manquant de
normalizeSessionName,session.toolsest uneMap, en-tête de skill avec espace avant les deux-points (description : …). - LiteLLM : un seul provider, requêtes au format OpenAI en streaming vers le endpoint OpenAI de LiteLLM ; configuration
~/.maia/settings.json(LiteLLM_URL,OpenAI_EndPoint,LiteLLM_Models,LiteLLM_API_Keyoptionnel). Suppression de SAP AI Core, Anthropic, OpenAI et Ollama. - Renommage technique
magiccode→maia(packages@maia/*, commandemaia, variablesMAIA_*, dossier.maia) ; textes « MagicCode » → « MAIA » ; bannière « Un seul agent, plusieurs modèles ». - Skills dans
.maia/skills(dossier de lancement, dossier parent, dossier utilisateur) ; les dossiers.claudene sont plus lus. - Badges
@inkjs/ui:INFO,WARN,FAIL(texte d'erreur#DC8ADD) etASKdevant les questions. - Suppression des modes et des extensions : un seul agent MAIA (
packages/agent), contexte de départMAIA.mdsinonCLAUDE.md(messageINFO, ouWARNsi aucun). - Outil
CurrentModel: indique au modèle quel LLM est utilisé dans la session. /clear: supprime le contexte et rechargeMAIA.md/CLAUDE.md(et les skills) comme au démarrage ;/newfait de même.- Permissions granulaires : règles
allow/ask/denypar outil (OutilouOutil(motif)), évaluées dans l'ordre deny > ask > allow,askpar défaut ; définies dans~/.maia/settings.jsonet./.maia/settings.json(pas le dossier parent). /permissions: affiche les permissions chargées, par fichier et par type (badges ALLOW / ASK / DENY) ; les fichiers sont listés l'un sous l'autre au démarrage.- Rendu markdown des réponses de l'agent via
markdansi, dans un seul composant isolé (packages/ui/src/markdown.tsx), désactivable avecMAIA_NO_MARKDOWN=1. - Image de la bannière : nouvelle image (visage à couronne), fond retiré, réduite à 32 × 32 en couleurs réelles (sans pixelisation ni réduction de palette), centrée verticalement à côté du titre ; données générées par
packages/ui/scripts/make-mask.py. - Barre de progression du contexte sous la zone de saisie (1 carré = 10 000 tokens) : taille maximale demandée au LLM après la sélection du modèle, contexte utilisé mis à jour après chaque réponse (consommation renvoyée par LiteLLM, sinon estimation) ; correction d'un import de type manquant dans
ink-ui.tsx. Valeur alignée sur le contexte réel de la session : contexte en attente compté, tokens de raisonnement exclus, estimation calibrée sur le serveur et signalée par~. - Outils personnalisés :
./.maia/tools/<nom>/TOOL.json(dossier de lancement uniquement) ; paramètresnumber/stringavec valeurs par défaut,runavec{{paramètre}}exécuté sans shell dans le dossier du tool (aucune injection possible), permissionaskpar défaut ; modulepackages/agent/src/custom-tools.ts, exempleexamples/tools/addition; exemple de sous-agent :examples/agents/code_reviewer(à copier dans./.maia/agents). - Threading async des outils : 1 outil au premier plan ; dès 2 outils, une tâche de fond par outil, attente de toutes les fins avant de répondre au LLM (résultats dans l'ordre demandé, 8 en parallèle au plus, verrou par fichier).
- Compactage :
/compactet compactage automatique au seuilMax_Token_Context - (4000 + 500); résumé en 5 rubriques comme contexte de départ (à la place de MAIA.md / CLAUDE.md), session renomméexxx__n(cycle 1..1000), barre de contexte recalculée. - Correction de la barre de contexte : comptage local avec js-tiktoken (
o200k_base) de tout le contexte (système, outils, messages, contexte en attente), recalculé avant chaque saisie ; fin du mélange consommation du serveur / estimation calibrée (cause des variations) et du marqueur~. - Documentation :
SPECS_TECH.md, spécifications techniques de toutes les fonctionnalités implémentées. - Outil Write plus tolérant : message d'erreur qui liste les paramètres reçus (tous les outils), alias de
contentacceptés, description du paramètre précisée. - Appels d'outils coupés ou mal assemblés : paramètre optionnel
MaxOutputTokens, avertissement quand la réponse est coupée (max_tokens), assemblage plus tolérant des fragments d'arguments d'appels d'outils du flux SSE, journal de diagnosticMAIA_DEBUG_LLM. - Coupure à 4096 tokens diagnostiquée (log
MAIA_DEBUG_LLM) : détection d'une sortie tombant sur une limite usuelle avec un appel d'outil, message d'erreur deWritequi conseille d'écrire en plusieurs morceaux. - Outil Bash via
runShell: timeout et Ctrl+C arrêtent toute l'arborescence de processus (groupe de processus, SIGTERM puis SIGKILL) ; stdout et stderr fusionnés dans l'ordre d'arrivée, dernière ligne[code de sortie : N]ou état (délai dépassé, interrompu). - Sous-agents : définitions
./.maia/agents/<nom>/AGENT.mdet~/.maia/agents(en-tête YAML : name, description, model, tools, permissions, effort, skills, omitMd, trace ; sections Instructions system / Missions / Output), outilAgent, processus autonome (fork+ IPC), conversation vide, résumé final seul renvoyé au parent, confirmations relayées à l'interface principale, notification INFO de fin, trace optionnellesession_…-agent_1.json, commande/agents. - Compactage sur « Input is too long » : réponse du serveur reconnue (
isContextTooLongError), compactage d'urgence (résultats d'outils tronqués dans la demande de résumé), reprise de la demande une fois. - Barre de contexte pendant le travail de l'agent : taille recomptée à chaque itération (après la demande, chaque réponse du LLM et chaque série de résultats d'outils) et barre affichée pendant les itérations, plus seulement à la saisie.
- Barre de contexte toujours affichée : en bas de l'écran en permanence (saisie, itérations, outils, confirmations, listes) ; si la taille maximale du contexte est inconnue, seul le nombre de tokens utilisés est montré.
- Zone de saisie toujours visible : grisée pendant le travail de l'agent (« Traitement en cours — Ctrl+C : interrompre »), et c'est elle qui porte les demandes d'autorisation (
ASK … (o/n)), dans le même cadre en pointillés. - Ligne d'état réservée au-dessus de la zone de saisie (« En réflexion… », outils en cours) : toujours présente, vide hors traitement, pour que la zone de saisie ne remonte ni ne descende.
- Limite d'itérations : 50 par défaut, réglable avec
MaxIterationsdans~/.maia/settings.json; à la limite, MAIA demande s'il faut continuer (o/n) pour une nouvelle série ; une demande tapée après un arrêt est fusionnée dans le dernier message utilisateur (alternance respectée). - Guide des permissions :
GUIDE_PERMISSIONS.md(syntaxe, exemples simples et complexes, interdiction d'une commande cachée dans un tube, limites). - Signature dans la bannière : « Arnaud VINCENT (avec ClaudeCode) » sous « Un seul agent, plusieurs modèles » (ligne séparée par un espace ; nom en #EEF527, ClaudeCode en #F57D27).
- Lancement direct d'un sous-agent : une saisie qui commence par
&(&Agent_1 tâche…) exécute le sous-agent sans passer par le LLM ; son résumé est affiché et ajouté au contexte. /createAgent(ex-/newAgent) : création guidée d'un sous-agent (niveau projet ou global) à partir du modèle commenté.ressources/AGENT.md; commentaires de fin de ligne acceptés dans l'en-tête deAGENT.md, commentaires HTML des sections ignorés.- Règles
Redirect: autoriser, demander ou interdire les redirections de sortie (>,>>,2>,&>) fichier par fichier ; sans règle, elles demandent confirmation ;2>&1et les>entre guillemets ne comptent pas ; guide mis à jour (§ 8). - Serveurs MCP distants : déclaration globale
~/.maia/mcp.json(expansion${VAR},typehttp/sse,headers,timeout,includeTools/excludeTools), aucun serveur actif par défaut, activation parMcp(serveur)dans lesettings.json,Mcp(serveur,outil)pour retirer/confirmer un outil,Mcp(serveur,*)endenydésactive sans connexion ; connexion parallèle après traitement des permissions ; outilsmcp__serveur__outil; commande/mcp(bandeauxOKvert /FAILrouge). - Outils personnalisés utilisateur : chargés aussi depuis
~/.maia/tools; à nom égal l'outil du projet (./.maia/tools) l'emporte, sans avertissement ; pris en compte par les sous-agents. - Réponse « stop » aux demandes d'autorisation d'outil (
o/n/s) : l'action n'est pas exécutée, l'itération s'arrête sans rollback (le contexte est conservé), puis la saisie suivante est ajoutée à la conversation ; sert à donner une information oubliée ou à changer de sujet. - Section
# Constraintsdes agents (facultative, après# Missions) : limites et conditions d'intervention de l'utilisateur ou d'un tiers ; incluse dans le prompt du sous-agent avec la consigne de s'arrêter et de le signaler dans son résumé ; modèle.ressources/AGENT.mdet exemplecode_reviewermis à jour.
