ハーネスアダプターの抽象化
HarnessAdapter プロトコル — Apothem のアダプターが共有プロファイルをどのように変換するか。
Apothem は、スキーマ検証済みの単一の YAML プロファイルを意味論上の信頼できる情報源として用い、
HarnessAdapter プロトコルを実装するアダプタークラスを通じて、各ハーネス固有のインストール面を
すべて派生させます。各アダプターは、そのハーネスがドキュメント化したユーザー位置または
プロジェクト位置に書き込み、そのハーネスに対応するネイティブのプリミティブが存在しない場合に限り、
Apothem 所有のサポートパスを使用します。
なぜアダプター抽象化なのか
ハーネスはそれぞれ異なる設定方言を話します。あるものは JSON 設定ファイルを求め、
別のものは .mdc ルールファイルのディレクトリを求め、3 つめのものは単一の Markdown 指示
ファイルを求めます。もし Apothem がその知識を CLI にハードコードしていたら、各ハーネスは
その前提をコアに漏れ出させてしまい、ハーネスを 1 つ追加するたびに、インストール・更新・
アンインストール・検証のロジックを足並みをそろえて変更しなければならなくなります。
アダプターパターンは、その構図を反転させます。コアが知っているのは HarnessAdapter
プロトコル(ライフサイクルメソッドからなる小さな契約)だけです。各ハーネスの方言は、
その契約を実装する 1 つの自己完結したサブパッケージの内部に収まります。コアはアダプターに
インストールするよう求めますが、それが JSON
ファイルのレンダリングを意味するのか、ルールディレクトリの書き込みを意味するのか、
サポートツリーの敷設を意味するのかを知りません。新しいハーネスは、プロトコルを満たし、
src/apothem/lib/harness_registry_data.py に HARNESS_REGISTRY のエントリーを追加し
(そこで SUPPORTED_HARNESS_COUNT を更新し)、pyproject.toml の apothem.harnesses
エントリーポイントに同じものを反映させ、src/apothem/schemas/profile.schema.json の
harnessId 列挙に ID を加えることで参加します。
これこそ Apothem がホスト非依存であり続ける構造的な理由です。この抽象化は、
ハーネス固有の知識が広がるのを食い止める境界なのです。
アダプターの形
アダプターは 2 つの物質化形態に分かれ、どちらもレジストリで確認できます。
- 単一ファイルレンダラーは、プロファイルのフィールドを 1 つのハーネスネイティブな 設定ファイルへ変換します。たとえば OpenCode アダプターは、マテリアライザーを通じて、 共有プロファイルからネイティブの設定ファイルをレンダリングします。
- ルール面およびサポートツリーのアダプターは、Apothem のペイロードのコホートを、
そのハーネスがドキュメント化しているルールの配置先へ伝播させます。ネイティブ形式が
存在するコホートはその形式へ変換し、残りはネイティブアンカーから参照される
Apothem 所有のサポートサブツリーの下に置きます。Cursor アダプターは、指定された
プロジェクトルートに単一の
.cursor/rules/apothem-rules.mdcを書き込みます。そのファイルの 前に、ユーザーホームに 2 つの共有フォルダーを書き込みます: スキルと、スキルとしてラップした コマンドは~/.agents/skills/へ、それらのスキルが引用するルールとテンプレートは~/.config/apothem/へ配置されます。Claude Code アダプターは、プロファイルからsettings.jsonをレンダリングしません。 同梱の設定テンプレートを、フックインタープリターを埋めた状態で~/.claude/settings.jsonに コピーし、その隣に Apothem の規約ディレクトリ(agents/、rules/、skills/、…)を 平坦化して配置し、プロファイルをセンチネルで区切られた管理ブロックとしてCLAUDE.mdに 書き込みます。
ある特定のアダプターは、ユーザースコープ(ホームディレクトリの下に書き込む)か、
プロジェクトスコープ(--project <path> を要求し、そのプロジェクトファイルをそのツリー内部に
書き込む)のいずれかで、レジストリがそのどちらであるかを記録します。7 つのプロジェクトスコープの
アダプター(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"]中心から端への扇形展開は、プロジェクト名がエンコードしているアーキテクチャそのものです。 1 つのプロファイルが核にあり、すべてのハーネスがそれぞれのアダプターに沿って等距離の外側にあります。
プロトコル定義
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 を受け取るアダプターにのみ、それを渡します。
エントリーポイント登録
アダプターは、apothem.harnesses という setuptools エントリーポイントグループ経由で登録します。
[project.entry-points."apothem.harnesses"]
cursor = "apothem.harnesses.cursor:CursorAdapter"src/apothem/lib/harness_registry.py にある中央レジストリは、正確に
17 個の公開 ID、エントリーポイント、ドキュメントパス、ターゲットパス、パッケージデータ
要件、能力状態、標準規約ピンのパスを記録します。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未対応および発見保留中の能力セルは、書き込みの前に構造化された警告になります。 それらは、臆造されたベンダー面へ静かに投影されることはありません。
新しいアダプターの追加
ステップバイステップのガイドは、ハーネスアダプターの作成 を参照してください。