Абстракция адаптера окружения
Протокол HarnessAdapter — как адаптеры Apothem транслируют общий профиль.
Apothem использует единый профиль YAML, проверенный по схеме, как семантический
источник истины и выводит каждую специфичную для окружения поверхность установки
через классы-адаптеры, реализующие протокол HarnessAdapter. Каждый адаптер
пишет в задокументированное окружением расположение пользователя или проекта и
использует собственные пути поддержки Apothem только тогда, когда у окружения нет
соответствующего нативного примитива.
Зачем нужна абстракция адаптера
Каждое окружение говорит на своём диалекте конфигурации: одно хочет файл настроек
JSON, другое — каталог файлов правил .mdc, третье — единый файл инструкций
Markdown. Если бы Apothem жёстко закодировал эти знания в CLI, каждое окружение
протекало бы своими допущениями в ядро, и добавление окружения означало бы
синхронное изменение логики установки, обновления, удаления и проверки.
Шаблон адаптера работает наоборот. Ядро знает только протокол HarnessAdapter —
небольшой контракт из методов жизненного цикла. Диалект каждого окружения живёт
внутри одного самодостаточного подпакета, который реализует этот контракт. Ядро
просит адаптер установить, не зная, означает ли это рендеринг файла JSON, запись
каталога правил или создание поддерева поддержки. Новые окружения подключаются, реализуя
протокол и добавляя запись HARNESS_REGISTRY в src/apothem/lib/harness_registry_data.py
(с увеличением там SUPPORTED_HARNESS_COUNT), отражая её в точках входа
apothem.harnesses в pyproject.toml и добавляя идентификатор в перечисление
harnessId в src/apothem/schemas/profile.schema.json. Именно это структурно
обеспечивает независимость Apothem от хоста: абстракция служит границей, которая не
даёт знаниям, специфичным для окружения, распространяться.
Форма адаптера
Адаптеры делятся на две формы материализации, обе видны в реестре:
- Рендереры одного файла преобразуют поля профиля в один нативный для окружения файл конфигурации. Адаптер OpenCode, например, рендерит свой нативный файл конфигурации из общего профиля через материализатор.
- Адаптеры поверхности правил и поддерева поддержки распространяют группу полезной
нагрузки Apothem в задокументированное окружением расположение правил, преобразуя
группы в нативный формат там, где он существует, а остальное помещая в поддерево
поддержки, принадлежащее Apothem и указанное в нативном якоре. Адаптер Cursor
записывает единственный файл
.cursor/rules/apothem-rules.mdcв указанный корень проекта. До этого файла он записывает две общие папки в домашнем каталоге пользователя: навыки и команды, обёрнутые как навыки, попадают в~/.agents/skills/, а правила и шаблоны, на которые ссылаются эти навыки, — в~/.config/apothem/. Адаптер Claude Code не рендеритsettings.jsonиз профиля. Он копирует поставляемый шаблон настроек в~/.claude/settings.json, подставляя интерпретатор хуков, уплощает каталоги конвенций Apothem (agents/,rules/,skills/, …) рядом с ним и записывает профиль в управляемый блок, ограниченный служебными маркерами, вCLAUDE.md.
Данный адаптер относится к области пользователя (пишет под домашним каталогом)
или к области проекта (требует --project <path> и пишет свой файл проекта
внутри этого дерева); реестр фиксирует, какой именно. Семь адаптеров с областью
проекта (cursor, github-copilot, windsurf, zed, codebuddy, kiro и trae) также
записывают общие папки в домашнем каталоге пользователя, такие как
~/.agents/skills/ и ~/.config/apothem/, и --project не меняет, куда они
попадают.
Текущая топология адаптеров
%% 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"]Разворачивание веером от центра к краям — это архитектура, которую кодирует имя проекта: один профиль в ядре, каждое окружение на равном расстоянии вдоль своего собственного адаптера.
Определение протокола
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:
...Адаптеры области проекта могут дополнительно принимать project: Path | None в
методах жизненного цикла и предоставлять resolve_output_path(project). CLI
передаёт --project PATH только тем адаптерам, чья сигнатура метода это
принимает.
Регистрация точки входа
Адаптеры регистрируются через группу точек входа setuptools apothem.harnesses:
[project.entry-points."apothem.harnesses"]
cursor = "apothem.harnesses.cursor:CursorAdapter"Центральный реестр в src/apothem/lib/harness_registry.py фиксирует точные
семнадцать публичных идентификаторов, точки входа, пути документации, целевые
пути, требования к данным пакета, состояния возможностей и пути закрепления
стандартной конвенции. CLI загружает адаптеры через этот реестр вместо повторного
обнаружения фактов о продукте в каждой команде.
Сопоставление профиля с нативным форматом
Каждый адаптер реализует логику трансляции, подходящую для его окружения:
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 supportedНеподдерживаемые и ожидающие обнаружения ячейки возможностей становятся структурированными предупреждениями перед записью. Они не проецируются молча на вымышленные поверхности поставщиков.
Добавление нового адаптера
См. Написание адаптера окружения для пошагового руководства.