Abstraction de l'adaptateur d'environnement
Le protocole HarnessAdapter — comment les adaptateurs d'Apothem traduisent le profil partagé.
Apothem utilise un unique profil YAML validé par schéma comme source sémantique
de vérité et dérive chaque surface d'installation propre à un environnement via
des classes adaptatrices qui implémentent le protocole HarnessAdapter. Chaque
adaptateur écrit dans l'emplacement utilisateur ou projet documenté par
l'environnement et n'utilise des chemins de support propres à Apothem que lorsque
l'environnement n'a pas de primitive native équivalente.
Pourquoi une abstraction d'adaptateur
Chaque environnement parle un dialecte de configuration différent : l'un veut un
fichier de paramètres JSON, un autre un répertoire de fichiers de règles .mdc,
un troisième un unique fichier d'instructions Markdown. Si Apothem codait ces
connaissances en dur dans la CLI, chaque environnement laisserait fuiter ses
hypothèses dans le cœur, et ajouter un environnement signifierait toucher la
logique d'installation, de mise à jour, de désinstallation et de vérification à
l'unisson.
Le motif adaptateur inverse cela. Le cœur ne connaît que le protocole
HarnessAdapter — un petit contrat de méthodes de cycle de vie. Le dialecte de
chaque environnement vit à l'intérieur d'un sous-paquet autonome qui implémente
ce contrat. Le cœur demande à un adaptateur d'installer sans savoir si cela
signifie rendre un fichier JSON, écrire un répertoire de règles ou poser une
arborescence de support. Les nouveaux environnements s'intègrent en satisfaisant
le protocole et en ajoutant une entrée HARNESS_REGISTRY dans
src/apothem/lib/harness_registry_data.py (en incrémentant SUPPORTED_HARNESS_COUNT
à cet endroit), en la reflétant dans les points d'entrée apothem.harnesses de
pyproject.toml, et en ajoutant l'identifiant à l'énumération harnessId de
src/apothem/schemas/profile.schema.json. C'est la raison structurelle pour
laquelle Apothem reste agnostique vis-à-vis de l'hôte : l'abstraction est la
frontière qui empêche les connaissances propres à un environnement de se
propager.
La forme d'un adaptateur
Les adaptateurs se répartissent en deux formes de matérialisation, toutes deux visibles dans le registre :
- Les moteurs de rendu de fichier unique traduisent les champs du profil en un unique fichier de configuration natif de l'environnement. L'adaptateur OpenCode, par exemple, rend son fichier de configuration natif à partir du profil partagé, par l'intermédiaire d'un materializer.
- Les adaptateurs de surface de règles et de sous-arborescence de support
propagent la cohorte de charge utile d'Apothem vers l'emplacement de règles
documenté par l'environnement, en convertissant les cohortes au format natif
lorsqu'il en existe un, et en rangeant le reste sous une sous-arborescence de
support détenue par Apothem, référencée depuis l'ancre native. L'adaptateur
Cursor écrit un unique
.cursor/rules/apothem-rules.mdcdans la racine de projet fournie. Avant ce fichier, il écrit deux dossiers partagés dans le répertoire personnel : les skills, ainsi que les commandes enveloppées en skills, vont dans~/.agents/skills/, et les règles et templates que citent ces skills vont dans~/.config/apothem/. L'adaptateur Claude Code ne rend passettings.jsonà partir du profil. Il copie le template de settings fourni vers~/.claude/settings.jsonavec l'interpréteur de hooks renseigné, aplatit à côté les répertoires de convention d'Apothem (agents/,rules/,skills/, …), et écrit le profil dans un bloc géré délimité par des sentinelles dansCLAUDE.md.
Un adaptateur donné est de portée utilisateur (écrit sous le répertoire
personnel) ou de portée projet (requiert --project <path> et écrit son
fichier de projet à l'intérieur de cet arbre) ; le registre indique lequel. Sept
adaptateurs de portée projet (cursor, github-copilot, windsurf, zed, codebuddy,
kiro et trae) écrivent aussi des dossiers partagés dans le répertoire personnel,
comme ~/.agents/skills/ et ~/.config/apothem/, et --project ne change pas
l'emplacement de ceux-ci.
Topologie actuelle des adaptateurs
%% provenance: hand-authored %%
%% verified: 2026-10-25 %%
%% cross-reference: src/apothem/harnesses/ (adapter sub-packages) %%
flowchart TD
accTitle: Apothem adapter topology
accDescr: The single shared profile at the center feeds the adapter layer, which fans out to one adapter per supported harness; each adapter writes to a user-scope or project-scope native surface, rendering a single config file or propagating a rules and support-tree cohort.
P["Shared profile (YAML)<br/>~/.config/apothem/profile.yaml<br/>+ packaged payload cohorts"]
P --> AL["Adapter layer<br/>HarnessAdapter protocol<br/>(17 registered adapters)"]
AL --> CC["claude-code adapter<br/>(user scope)"]
AL --> CX["codex adapter<br/>(user scope)"]
AL --> CU["cursor adapter<br/>(project scope)"]
AL --> CO["github-copilot adapter<br/>(project scope)"]
AL --> WS["windsurf adapter<br/>(project scope)"]
AL --> ETC["… 12 more registered adapters<br/>(antigravity, gemini-cli, glm, hermes,<br/>kimi-code, open-claw, opencode, qwen-code,<br/>codebuddy, kiro, trae, zed)"]
CC --> CCN["~/.claude/settings.json<br/>+ flattened convention dirs<br/>(template copy)"]
CX --> CXN["~/.codex/AGENTS.md + hooks.json<br/>+ converted TOML agents<br/>(render + support tree)"]
CU --> CUN["<project>/.cursor/rules/apothem-rules.mdc<br/>+ ~/.agents/skills/ and ~/.config/apothem/<br/>(project rules file + user-home folders)"]
CO --> CON["<project>/.github/copilot-instructions.md<br/>+ ~/.agents/skills/ and ~/.config/apothem/<br/>(project rules file + user-home folders)"]
WS --> WSN["<project>/.devin/rules/apothem-rules.md<br/>+ ~/.agents/skills/ and ~/.config/apothem/<br/>(project rules file + user-home folders)"]
ETC --> ETCN["each harness's documented<br/>native surface or Apothem-owned<br/>support subtree"]L'éventail du centre vers les bords est l'architecture que le nom du projet encode : un profil au cœur, chaque environnement à une distance égale le long de son propre adaptateur.
Définition du protocole
from pathlib import Path
from typing import Protocol
class HarnessAdapter(Protocol):
name: str
output_path: Path
def install(self, profile: dict[str, object]) -> object:
...
def update(self, profile: dict[str, object]) -> object:
...
def uninstall(self) -> None:
...
def is_installed(self) -> bool:
...
def verify(self) -> bool:
...Les adaptateurs de portée projet peuvent en outre accepter project: Path | None
sur les méthodes de cycle de vie et exposer resolve_output_path(project). La CLI
ne passe --project PATH qu'aux adaptateurs dont la signature de méthode
l'accepte.
Enregistrement du point d'entrée
Les adaptateurs s'enregistrent via le groupe de points d'entrée setuptools
apothem.harnesses :
[project.entry-points."apothem.harnesses"]
cursor = "apothem.harnesses.cursor:CursorAdapter"Le registre central dans src/apothem/lib/harness_registry.py consigne les dix-sept
ID publics exacts, les points d'entrée, les chemins de documentation, les chemins
cibles, les exigences de données de paquet, les états de capacité et les chemins
d'épinglage de convention standard. La CLI charge les adaptateurs via ce registre
au lieu de redécouvrir des faits produit dans chaque commande.
Mappage du profil vers le natif
Chaque adaptateur implémente la logique de traduction appropriée à son environnement :
YAML profile + payload cohort Harness-native surface
----------------------------- ----------------------
identity / preferences --> native config fields or rendered instruction context
commands/*.md --> native commands or SKILL.md folders
rules/*.md --> native rules where supported, otherwise support trees
hooks/messages/*.md --> native hooks where supported, otherwise support trees
agents/*.md --> native agent formats where supported
skills/*/SKILL.md --> native skill folders where supportedLes cellules de capacité non prises en charge et en attente de découverte deviennent des avertissements structurés avant les écritures. Elles ne sont pas projetées en silence sur des surfaces de fournisseur inventées.
Ajouter un nouvel adaptateur
Voir Écrire un adaptateur d'environnement pour un guide pas à pas.
Schéma du profil partagé
Schéma du YAML du profil partagé d'Apothem.
Contrat d'empaquetage des cohortes
Le contrat stable, consommable en aval, pour l'empaquetage des cohortes et les manifestes de plugin — énumérer les cohortes et résoudre leurs cibles natives par environnement sans redériver la correspondance.