No description
Find a file
2026-10-13 01:23:59 +02:00
packages gestion des sessions et @fichier 2026-10-13 01:23:59 +02:00
.gitignore gestion des sessions et @fichier 2026-10-13 01:23:59 +02:00
btp_AI_tpl.json Initial commit 2026-10-11 02:11:20 +02:00
package.json Initial commit 2026-10-11 02:11:20 +02:00
pnpm-lock.yaml gestion des sessions et @fichier 2026-10-13 01:23:59 +02:00
pnpm-workspace.yaml Initial commit 2026-10-11 02:11:20 +02:00
README.md gestion des sessions et @fichier 2026-10-13 01:23:59 +02:00
screenshot.png gestion des sessions et @fichier 2026-10-13 01:23:59 +02:00
tsconfig.base.json Migration Ink 2026-10-12 00:41:17 +02:00
tsconfig.json Migration Ink 2026-10-12 00:41:17 +02:00

MagicCode

MagicCode

Assistant IA en ligne de commande. Le mode (comportement de l'agent) vient d'une extension, le LLM se choisit indépendamment, l'interface utilise Ink (React pour CLI).

Démarrer

pnpm install
cp btp_AI_tpl.json btp_AI.json   # puis renseigner url, basic, serviceurls.AI_API_URL
pnpm dev                                  # mode claudecode ; le modèle est choisi dans la liste au démarrage
pnpm dev -- --model sapaicore:anthropic--claude-4.5-sonnet
pnpm dev -- --model anthropic:opus       # API Anthropic directe (ANTHROPIC_API_KEY)
pnpm test                                 # test de la boucle agent (provider factice)
pnpm build && node packages/cli/dist/index.js

Lancer MagicCode depuis un autre dossier

pnpm dev s'exécute depuis la racine du dépôt : le dossier de travail est alors cette racine (./btp_AI.json, ./CLAUDE.md… y sont cherchés). Pour utiliser MagicCode sur un autre projet ./X, lancez-le depuis ./X :

pnpm build
cd packages/cli && pnpm link --global     # une fois : installe la commande `magiccode`
cd ~/X && magiccode                       # le dossier de travail est ~/X
# ou sans installer :  cd ~/X && node /chemin/vers/magiccode/packages/cli/dist/index.js
# ou en développement : MAGICCODE_CWD=~/X pnpm --dir /chemin/vers/magiccode 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. MagicCode tient compte de MAGICCODE_CWD, puis de INIT_CWD (dossier où pnpm/npm a été lancé), puis de process.cwd().

Architecture

Paquet Rôle
core Contrats (LLMProvider, AgentMode, Extension, Tool, AgentUI), boucle agent, registre, chargement des extensions
providers Adaptateurs LLM : sapaicore (premier provider, voir ci-dessous), anthropic (direct), openai et ollama (à faire)
ui Interface Ink/React (journal, saisie, listes de choix, confirmations, spinner)
ext-claudecode Mode par défaut : CLAUDE.md, dossiers .claude (settings, commandes, skills) + outils Read/Glob/Grep/Write/Edit/Bash/TodoWrite/WebFetch
cli Point d'entrée : configuration, extensions, boucle de conversation

Résolution du mode et du modèle (le modèle n'a pas de défaut : s'il n'est pas défini, il est demandé) : flags CLI > variables MAGICCODE_MODE / MAGICCODE_MODEL

.magiccode/config.json (projet) > ~/.magiccode/config.json > défauts.

Extensions tierces : ~/.magiccode/extensions/<nom>/index.js (export default d'un objet Extension).

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 sans bibliothèque tierce, pour ne dépendre que d'ink et de react. Un terminal interactif (TTY) est requis.

Présentation : la bannière d'accueil reprend le style « MAIA » : masque pixel-art à gauche (demi-blocs ▀▄█, 20 × 20 pixels), titre « Maia » en gros caractères pixel avec ombre et dégradé arc-en-ciel à droite, puis deux textes (vert, rose). Terminal d'au moins 46 colonnes pour la version complète ; entre 25 et 45 : sans le masque ; en dessous : cadre à double bordure compact, titre centré ; les messages d'information sont en bleu clair (LIGHT_BLUE dans packages/ui/src/colors.ts) ; une ligne vide sépare chaque message.

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, /mode, 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ù MagicCode 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), ./btp_AI.json, .magiccode/config.json, CLAUDE.md et .claude/. 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. Seuls ~/.claude et ~/.magiccode (config et extensions utilisateur) sont lus depuis le dossier personnel.

Commande ! : exécuter une commande shell

Quel que soit le mode chargé (ClaudeCode, Copilot…), !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 ») avec les variables env du mode ; un cd n'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é. /clear l'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

Quel que soit le mode chargé (ClaudeCode, Copilot…), @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 @mot qui 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.ts introuvable).
  • 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 .claude/commands/*.md 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, valable pour tous les agents (claudecode, copilot…).

  • 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 mode d'origine est rétabli, le modèle courant est conservé).
  • /new : nouvelle session, nouveau contexte ; l'utilisateur est averti que la session quittée est sauvegardée (sous son nom). /mode démarre aussi une nouvelle session.

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 §

Quel que soit le mode chargé, §nom_du_skill [arguments] lance un skill de l'agent (ex. §sk_test) ; § seul liste les skills disponibles. Chaque mode 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. En mode ClaudeCode, ce prompt contient les instructions du SKILL.md (avec $ARGUMENTS / ARGUMENTS:), sans passer par l'outil Skill du modèle. Les extensions Copilot, opencode, etc. n'ont qu'à renvoyer leurs propres skills sous cette forme.

Provider SAP AI Core / Generative AI Hub

Configuration : ./btp_AI.json (ou $MAGICCODE_BTP_CONFIG), modèle fourni dans btp_AI_tpl.json. Ne jamais versionner ce fichier (il est dans .gitignore).

  1. OAuth client_credentials : GET {url}/oauth/token avec Authorization: Basic {basic} (token mis en cache, renouvelé sur expiration ou HTTP 401).
  2. Découverte : GET {AI_API_URL}/v2/lm/deployments (header AI-Resource-Group, default sauf resourceGroup dans le JSON).
  3. Appel du modèle sur son deploymentUrl selon la famille : Claude → /converse (Bedrock), GPT → /chat/completions (Azure OpenAI). Les autres familles (Gemini, embeddings…) sont ignorées.

Choix du modèle : au démarrage (sauf si --model, MAGICCODE_MODEL ou une config le définit), MagicCode interroge tous les providers (déploiements SAP AI Core, et API Anthropic si ANTHROPIC_API_KEY est définie) et demande de choisir le modèle de la session. /model rouvre cette liste en cours de session (la conversation est conservée). Un provider injoignable donne un avertissement sans bloquer les autres. Spécification explicite : sapaicore:<nom du déploiement>.

Extension ClaudeCode : dossiers .claude (lot 2)

Les dossiers .claude sont lus dans cet ordre (le plus spécifique l'emporte) : ~/.claude, puis ceux de la racine jusqu'au dossier courant.

  • settings.json / settings.local.json : permissions.allow|ask|deny au format Claude Code (Bash(npm run test:*), Edit(src/**), Read(./.env), Write) et env (transmis à Bash). Priorité deny > ask > allow. Pour Bash, une règle allow ne couvre une commande composée (&&, ;, |) que si chaque partie est autorisée, et jamais une commande avec redirection ou substitution (>, $(…), backticks). Les règles deny sont du « meilleur effort » : elles ne remplacent pas un vrai bac à sable.
  • commands/**/*.md : slash-commandes. $ARGUMENTS, $1…$9 ; en-tête description et argument-hint. Un sous-dossier préfixe le nom (commands/git/commit.md → /git:commit). Les noms help, clear, mode, model, exit sont réservés.
  • skills/<nom>/SKILL.md : en-tête name et description. Seule la liste est mise dans le prompt ; le contenu est chargé à la demande par l'outil Skill.

Outils TodoWrite et WebFetch (lot 3, première partie)

  • TodoWrite : liste de tâches de la session (pending / in_progress / completed, forme activeForm pour 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 ».
  • 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_length caractères renvoyés). Pas de JavaScript exécuté, pas de PDF ni d'images.
    • Confirmation demandée à chaque appel, sauf règle allow dans settings.json : "WebFetch(domain:docs.example.com)" (nom d'hôte exact, ou motif *.example.com) ; deny et ask fonctionnent de même.
    • 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é.

Reste à faire

  • Provider SAP AI Core : streaming (actuellement une réponse complète par appel), autres familles (Gemini), validation sur un vrai service

  • ext-claudecode : lots 1 et 2 terminés, lot 3 : TodoWrite et WebFetch faits (reste : sous-agents agents/ + outil Task, hooks, serveurs MCP) (Glob/Grep en JS pur sans ripgrep ; à l'initialisation, ce qui est trouvé et chargé est listé, sinon un avertissement). Pas encore gérés dans les commandes : allowed-tools, model, @fichier, !commande``

  • ext-claudecode lot 3 : TodoWrite, WebFetch, sous-agents, hooks, MCP

  • Providers OpenAI et Ollama

  • Sessions (reprise), compaction du contexte, annulation (AbortSignal) depuis l'UI