OpenCode : l’agent de code open source, installé puis démonté

OpenCode s'installe sans compte et répond dès la première commande avec un modèle gratuit. Il charge aussi, sans le dire, les skills déjà installés pour d'autres outils si vous oubliez d'isoler votre répertoire personnel.

OpenCode : l’agent de code open source, installé puis démonté
Réponse rapide

OpenCode est un agent de code open source (Anomaly, licence MIT) qui tourne en terminal, en extension d'éditeur ou en application de bureau. Installé sans rien poser sur le système, il fonctionne dès la première commande grâce à un modèle gratuit et prend en charge les serveurs MCP et le format de skills SKILL.md, y compris ceux déjà installés pour d'autres agents s'ils ne sont pas isolés. Les modèles locaux passent par un fournisseur générique compatible OpenAI, pas par une intégration Ollama dédiée.

Un agent de code open source promet de ne pas vous enfermer chez un seul éditeur. Le modèle se choisit, le code reste sur votre machine, la licence est permissive. J’ai installé OpenCode sans toucher au système ni saisir un seul identifiant, avant de découvrir qu’un export de variable oublié suffit à lui faire charger les skills déjà installés pour d’autres outils.

OpenCode, trois interfaces sous licence MIT

OpenCode est développé par Anomaly et publié sous licence MIT dans le dépôt anomalyco/opencode, qui affichait environ 206 000 étoiles le 7 septembre 2026. L’outil se présente comme « an open source agent that helps you write code in your terminal, IDE, or desktop ». Il se décline en trois interfaces : un client terminal (TUI), une extension pour éditeur et une application de bureau en bêta pour macOS, Windows et Linux.

Un LSP se charge en principe automatiquement selon le langage détecté, et plus de 75 fournisseurs de modèles sont pris en charge via Models.dev et le AI SDK, de l’API payante au modèle local. Dans mon mini-projet de test, le journal a pourtant noté « all LSPs are disabled » : la détection du langage ne suffit pas, le serveur correspondant doit être présent pour que la fonctionnalité s’active. Le site revendique par ailleurs « OpenCode does not store any of your code or context data ». Rien ne part vers les serveurs d’Anomaly par défaut, mais tout reste dans une base SQLite locale, comme le montre la section suivante.

OpenCode rejoint une liste déjà longue d’agents en ligne de commande : notre panorama des agents de code les compare tous. Cet article se concentre sur ce qu’on découvre en installant et en démontant celui-ci, pièce par pièce.

Installer OpenCode sans rien poser sur le système

Le paquet npm opencode-ai installe un binaire compilé (opencode-darwin-arm64 sur ce Mac) derrière un exécutable opencode.exe. Pour ne rien laisser dans les emplacements globaux, direction un préfixe temporaire du labo, avec un cache npm local et un HOME isolé pour toutes les commandes qui suivent :

bash
mkdir -p docker/articles/2026-09-07/lab/opencode-agent-code-open-source/{prefix,home,projet-demo}
cd docker/articles/2026-09-07/lab/opencode-agent-code-open-source

npm install opencode-ai --prefix ./prefix --cache ./prefix/.npm-cache

export HOME="$PWD/home"
./prefix/node_modules/.bin/opencode --version

La commande renvoie 1.18.29, publiée le 4 septembre 2026 à 23h47 selon les releases GitHub, trois jours avant ce test. Cinquième version en huit jours : 1.18.25 à 1.18.29 entre le 28 août et le 4 septembre. Le registre npm confirme qu’il s’agit de la dernière version disponible. La documentation propose aussi un script curl -fsSL https://opencode.ai/install | bash et des paquets Homebrew, Scoop ou pacman, j’ai gardé npm pour que toute l’installation tienne dans un dossier qu’on supprime à la fin.

L’installation ne s’arrête pas forcément là. Une fois quelques commandes lancées depuis le projet, OpenCode a installé de lui-même 54 Mo de dépendances dans .opencode/node_modules, sans le demander : son propre SDK de plugin, la bibliothèque effect, et une présence plus surprenante, kubernetes-types. Un .opencode/.gitignore les attendait déjà pour les exclure du suivi Git.

Où OpenCode lit sa configuration

Une fois HOME isolé, la commande opencode debug paths, un sous-outil de diagnostic absent du menu principal, affiche la structure façon XDG qu’OpenCode s’est construite dans ce faux répertoire personnel (chemins raccourcis pour la lecture) :

bash
opencode debug paths

home    .../lab/opencode-agent-code-open-source/home
config  .../home/.config/opencode
data    .../home/.local/share/opencode
cache   .../home/.cache/opencode
state   .../home/.local/state/opencode
tmp     /var/folders/.../T/opencode

Le fichier opencode.log écrit dans data/log détaille l’ordre de lecture exact au démarrage : d’abord le dossier de config globale (opencode.json, .jsonc), puis, en partant du dossier courant, opencode.json et .opencode/opencode.json du projet. Le skill intégré au binaire, lisible avec opencode debug skill, documente la même hiérarchie, avec un tableau qui vaut la peine d’être recopié :

Portée Chemin
Config projet ./opencode.json, ./opencode.jsonc ou .opencode/opencode.json (recherché en remontant du dossier courant à la racine du dépôt Git)
Config globale ~/.config/opencode/opencode.json ou .jsonc, jamais ~/.opencode/
Skills du projet .opencode/skill(s)/<nom>/SKILL.md
Skills globaux ~/.config/opencode/skill(s)/<nom>/SKILL.md
Skills externes (chargés automatiquement) ~/.claude/skills/<nom>/SKILL.md, ~/.agents/skills/<nom>/SKILL.md

Les configurations de chaque portée sont fusionnées (deep-merge), le projet l’emportant sur le global. La dernière ligne du tableau porte le plus loin. OpenCode lit, sans déclaration supplémentaire, les skills déjà installés pour Claude Code et pour d’autres agents compatibles avec le standard ouvert Agent Skills, décrit dans notre guide du SKILL.md.

Les skills : jusqu’où va la découverte automatique ?

Pour vérifier ce que dit la documentation embarquée, j’ai ajouté deux skills-leurres directement dans le faux HOME, à l’endroit exact où OpenCode dit les chercher, avant de relancer le même diagnostic :

bash
mkdir -p home/.claude/skills/decoy-claude home/.agents/skills/decoy-agents
# un SKILL.md minimal (frontmatter name + description) dans chaque dossier
opencode debug skill

Résultat : quatre skills détectés, le skill intégré, le skill du projet, et les deux leurres, chacun avec son chemin complet en home/.claude/skills/… et home/.agents/skills/…. OpenCode respecte simplement la variable HOME qu’on lui donne, il regarde seulement plus large que son propre dossier de configuration.

Sans HOME réexporté dans la commande, la même liste retombe sur le vrai répertoire personnel de la machine et remonte 120 skills, ceux réellement installés pour Claude Code et pour d’autres agents. Aucune évasion de sandbox là-dedans. Une seule commande sans HOME explicite suffit à recoller un test qu’on croit isolé à votre répertoire personnel réel, et donc aux skills déjà installés pour d’autres outils, jamais déclarés dans le projet. La commande elle-même est révélatrice : aucun opencode skill list n’existe au premier niveau, il faut passer par opencode debug skill, un outil de diagnostic.

MCP : configurer et interroger un serveur local

Le fichier de configuration du projet déclare un serveur MCP local, les permissions associées, et restreint l’accès à l’outil pour l’agent plan :

json
{
  "$schema": "https://opencode.ai/config.json",
  "mcp": {
    "slug": {
      "type": "local",
      "command": ["node", "./mcp/serveur.mjs"],
      "enabled": true
    }
  },
  "permission": {
    "edit": { "*": "ask", "docs/**": "allow" },
    "bash": { "*": "ask", "git status": "allow", "git diff *": "allow", "rm *": "deny" },
    "webfetch": "deny",
    "skill": { "*": "allow" }
  },
  "agent": {
    "plan": { "tools": { "slug_*": false } }
  }
}

Le serveur, mcp/serveur.mjs, est un script Node autonome d’une quarantaine de lignes qui parle JSON-RPC 2.0 sur l’entrée standard et expose un seul outil, slug_fr :

javascript
const TOOLS = [{
  name: "slug_fr",
  description: "Transforme un titre francais en slug d'URL (minuscules, sans accent).",
  inputSchema: {
    type: "object",
    properties: { titre: { type: "string", description: "Le titre a convertir" } },
    required: ["titre"]
  }
}];

function slug(titre) {
  return titre.normalize("NFD").replace(/[\u0300-\u036f]/g, "")
    .toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
}

opencode mcp list confirme la connexion :

bash
opencode mcp list

MCP Servers
  slug   connected
    node ./mcp/serveur.mjs

1 server(s)

Un skill de projet, slug-gekkode, explique la règle métier (cinq mots maximum, sans article) et renvoie vers l’outil. Un aller-retour avec opencode run déclenche les deux à la suite (sortie nettoyée des codes couleur et des icônes, contenu inchangé) :

bash
opencode run "Utilise la regle de slug du projet pour convertir ce titre : Les secrets d'une bonne architecture PHP"

> build - big-pickle
Skill "slug-gekkode"
slug_slug_fr {"titre":"Les secrets d'une bonne architecture PHP"}

Le slug genere par l'outil MCP est : les-secrets-d-une-bonne-architecture-php

Mais selon les regles du skill (max 5 mots, sans article ni preposition),
le slug correct est : secrets-bonne-architecture-php

Le nom d’outil affiché, slug_slug_fr, montre la convention de nommage. OpenCode préfixe chaque outil MCP par le nom du serveur déclaré dans la config (slug), accolé au nom de l’outil exposé (slug_fr), d’où la répétition. Le modèle a choisi le skill sur sa description, appelé l’outil, puis recalculé le résultat à la main pour respecter la règle des cinq mots. Un skill qui complète un outil au lieu de le remplacer se comporte exactement ainsi. Pour un serveur distant, la documentation décrit un bloc "type": "remote" avec url et headers ou oauth, plus opencode mcp auth pour le flux OAuth. Le sujet est creusé dans notre article sur la création d’un serveur MCP en PHP.

Agents et permissions, règle par règle

opencode agent list affiche huit agents dans ce projet : build (agent principal par défaut, accès large), plan (principal, lecture et planification), compaction, summary et title (internes), explore et general (sous-agents), et relecteur, déclaré dans .opencode/agents/relecteur.md :

markdown
---
description: Relit un article et signale les fautes, sans jamais modifier de fichier.
mode: subagent
temperature: 0.1
permission:
  edit: deny
  bash: deny
---
Vous relisez un article de blog en francais. Vous signalez les erreurs, vous ne corrigez rien.

Chaque permission accepte allow, ask ou deny, par motif glob, la règle la plus spécifique l’emportant. Le réglage vaut pour edit, bash, webfetch, skill, et même pour un outil MCP précis comme slug_*. L’agent plan du projet illustre le principe : sa configuration désactive l’outil slug_* et n’autorise l’écriture que dans .opencode/plans/*.md, jamais dans le code. En mode non interactif, sans terminal pour répondre à une question, une permission ask n’est pas laissée en attente : elle est automatiquement rejetée :

bash
opencode run "Execute la commande shell : echo test-permission"

> build - big-pickle
! permission requested: bash (echo test-permission); auto-rejecting
Error: The user rejected permission to use this specific tool call.

$ echo $?
0

Le comportement par défaut est donc fermé : sans surveillance humaine, OpenCode échoue plutôt que d’exécuter une commande non explicitement autorisée. Le code de sortie reste pourtant à 0 malgré l’échec, un piège pour qui scripte une vérification dessus. Le sujet des permissions et de la sandbox, commun à tous ces agents, est creusé dans notre article sur les agents de code, sandbox et permissions.

Les modèles : un essai gratuit, et le cas des modèles locaux

Sans compte ni clé, opencode run "dis bonjour" fonctionne dès la première commande. Le modèle utilisé par défaut, big-pickle (« stealth model » selon sa fiche), fait partie des six modèles gratuits d’OpenCode Zen, la passerelle maison. Les cinq autres sont MiMo-V2.5 Free, Ling 3.0 Flash Fin Free, deux variantes de Nemotron 3 et Muse Spark 1.3 Contributor Free. La base SQLite locale (table message) garde la trace de l’échange. Les tables credential et account restent à zéro ligne : aucun identifiant demandé ni stocké pour ce modèle-là, et un coût nul dans le journal de session.

Un modèle de marque du même catalogue (GPT, Claude, Gemini, Grok, DeepSeek, Qwen…) demande un compte OpenCode Zen et un moyen de paiement. La facturation se fait au token, de 0,20 $ à plusieurs dollars le million selon le modèle. Le solde se recharge automatiquement de 20 $ dès qu’il passe sous 5 $.

Pour un modèle local, la vérification est simple :

bash
which ollama
# ollama not found

Le catalogue de modèles mis en cache par OpenCode ne propose qu’un fournisseur cloud, ollama-cloud (clé OLLAMA_API_KEY), pas d’entrée dédiée pour un Ollama local. Pour un serveur local, Ollama, llama.cpp ou LM Studio, la documentation décrit un fournisseur générique compatible OpenAI à déclarer soi-même :

json
{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "ollama-local": {
      "npm": "@ai-sdk/openai-compatible",
      "name": "Ollama (local)",
      "options": { "baseURL": "http://127.0.0.1:11434/v1" },
      "models": { "mon-modele": { "name": "Mon modele local" } }
    }
  }
}

Le port 11434 et l’API native (/api/generate, entre autres) sont documentés sur docs.ollama.com. La compatibilité OpenAI qu’exige @ai-sdk/openai-compatible tient, elle, en deux appels, vérifiés sur un Ollama 0.34.0 lancé en conteneur :

bash
curl -s http://127.0.0.1:11434/v1/models
# {"object":"list","data":[{"id":"qwen2.5:0.5b","object":"model",
#   "created":1789038968,"owned_by":"library"}]}

curl -s http://127.0.0.1:11434/v1/chat/completions \
  -H 'Content-Type: application/json' \
  -d '{"model":"qwen2.5:0.5b","messages":[{"role":"user",
       "content":"Réponds par le seul mot OK."}],"max_tokens":5}'
# {"id":"chatcmpl-704","object":"chat.completion","model":"qwen2.5:0.5b",
#  "choices":[{"index":0,"message":{"role":"assistant","content":"OK."},
#   "finish_reason":"stop"}],
#  "usage":{"prompt_tokens":38,"completion_tokens":3,"total_tokens":41}}

La forme est bien celle qu’attend le SDK : object: "list" sur le catalogue, objet chat.completion complet avec choices, finish_reason et usage sur la complétion. Un modèle inconnu répond 404 avec l’objet d’erreur OpenAI, et l’en-tête Authorization est accepté quelle que soit sa valeur : le champ apiKey du fournisseur peut contenir n’importe quoi.

Ce qui manque, vu d’ici

Le panorama complet des agents de code en ligne de commande, forces et faiblesses face à Claude Code, Codex, Gemini CLI et les autres, est traité à part dans notre comparatif dédié. La liste des skills et la configuration résolue ne sont accessibles que par des commandes de diagnostic, debug skill et debug config. Toutes deux sont absentes du menu principal : utiles une fois qu’on les connaît, mais pas franchement découvrables. Le stockage local est bien réel : session, messages et appels d’outils vivent dans une base SQLite sur votre disque, donc « aucune donnée envoyée aux serveurs d’Anomaly » n’équivaut pas à « rien n’est écrit ». Et l’accès gratuit s’arrête au modèle maison : au premier modèle de marque, on retrouve le compte et la carte bancaire qu’on retrouverait ailleurs.

Ce qu’il faut retenir

  • OpenCode (Anomaly, licence MIT) s’installe sans rien poser sur le système via npm install opencode-ai --prefix et un HOME isolé, version testée : 1.18.29.
  • La configuration se lit à trois niveaux fusionnés : projet (en remontant jusqu’à la racine Git), globale (~/.config/opencode, jamais ~/.opencode), et des skills externes chargés automatiquement depuis ~/.claude/skills et ~/.agents/skills.
  • Isoler HOME fonctionne, à condition de l’exporter dans chaque commande. Un oubli reconnecte OpenCode à votre répertoire personnel réel, skills d’autres outils compris : deux skills-leurres le confirment, et une liste sans HOME en remonte 120.
  • Un skill de projet et un outil MCP local fonctionnent ensemble dès le premier essai, sans identifiant, avec le modèle gratuit big-pickle d’OpenCode Zen.
  • Les permissions ask sont automatiquement rejetées en mode non interactif, mais le code de sortie reste 0 : à surveiller si vous scriptez des vérifications autour d’opencode run.
  • Les modèles locaux (Ollama, llama.cpp, LM Studio) passent par un fournisseur générique compatible OpenAI à déclarer à la main, pas par une intégration Ollama dédiée.

Erreurs fréquentes

Isoler HOME ne protège qu'à moitié Si une seule commande oublie d'exporter la variable, OpenCode retombe sur votre répertoire personnel réel et charge les skills déjà installés pour d'autres outils.
~/.claude/skills et ~/.agents/skills sont lus automatiquement Ce n'est pas un défaut d'isolation : la documentation embarquée le confirme (external skills, auto-loaded). Sur une machine déjà équipée pour Claude Code, ses skills s'y retrouvent sans déclaration explicite.
Pas de commande skill visible La liste des skills n'apparaît pas dans opencode --help : elle est cachée sous opencode debug skill, un outil de diagnostic qui peut changer sans préavis.
~/.config/opencode, pas ~/.opencode La documentation embarquée précise explicitement ce piège de nommage, le second chemin est ignoré en silence.
Un code de sortie 0 malgré un refus de permission En mode non interactif, une commande bash refusée fait échouer l'outil mais opencode run se termine tout de même avec le code 0.

MCPOllamaOpenCodeSkills

Damien Flandrin Développeur web depuis 2010, créateur de Gekkode et d’Email Impact. Chaque article est testé sur un projet réel avant publication. Contact
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.