blob: bc870ddfd5425b8ba2ea5fa64d8e511430c2106e [file] [view]
<!-- SPDX-License-Identifier: Apache-2.0
https://www.apache.org/legal/release-policy.html -->
# agents — the agent-target registry (where framework-skill symlinks land)
Framework skills are **vendor-neutral content**: every supported
agent reads the *same* `SKILL.md` (the open Agent Skills format —
plain Markdown + a small YAML frontmatter). The skill body is
byte-identical no matter which agent loads it; there is **no
per-agent compile, adapter, or content transform**. The only thing
that genuinely differs between agents is **where on disk each one
looks for skills**. This file is the registry of those locations —
the single source of truth `adopt`, `upgrade`, `verify`,
`unadopt`, and `worktree-init` consult to decide *which directories*
to wire, refresh, health-check, and tear down.
It is the magpie analogue of a package manager's per-agent path
table: keep all vendor-specific knowledge here as *"where files
go"*, never as *"what files contain"*.
## The registry
| Target id | Project skills dir | Kind | Reads it |
|---|---|---|---|
| `universal` | `.agents/skills/` | universal **(canonical)** | Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode, Cline, Zed, Warp, Amp, and the rest of the cluster that converged on the shared path |
| `claude-code` | `.claude/skills/` | native (relay) | Claude Code |
| `github` | `.github/skills/` | native (relay) | GitHub's skill loader |
| `windsurf` | `.windsurf/skills/` | native (relay) | Windsurf |
| `goose` | `.goose/skills/` | native (relay) | Goose |
| `kiro` | `.kiro/skills/` | native (relay) | Kiro CLI |
The table is **extensible**: a new agent that wants framework
skills is one new row (`id`, project dir, kind), nothing else —
the same way a path-registry-driven installer adds an agent. Do
not invent per-agent *content*; if an agent needs a different
directory, add a row, never a forked skill.
## The canonical directory — `.agents/skills/`
`.agents/skills/` is the **one canonical home** for every framework
skill. Its `magpie-<skill>` entries are the links that resolve to
the actual skill source — the gitignored snapshot
(`.apache-magpie/skills/<skill>/`) for a normal adopter, or the
in-repo `../../skills/<skill>/` source for the framework's own
[local self-adoption](adopt.md#local-self-adoption-methodlocal).
This is the load-bearing move for neutrality on two fronts:
1. **One placement covers the whole shared-path cluster.** A large
set of agents (Codex, Cursor, Gemini CLI, GitHub Copilot,
OpenCode, Cline, Zed, Warp, …) all read `.agents/skills/` as
their project-scope skills path, so a single
`.agents/skills/magpie-<skill>` link is seen by all of them:
```text
.agents/skills/magpie-pr-management-triage → the canonical link
├─ Codex picks it up
├─ Cursor picks it up
├─ Gemini CLI picks it up
└─ Copilot … picks it up
```
2. **Every other target is a thin relay into it.** Agents with a
bespoke folder (`claude-code` → `.claude/skills/`, `github` →
`.github/skills/`, `windsurf`, `goose`, …) do **not** link into
the snapshot independently. Each one gets a per-skill relay
symlink that points back at the canonical entry:
```text
.claude/skills/magpie-<skill> → ../../.agents/skills/magpie-<skill>
.github/skills/magpie-<skill> → ../../.agents/skills/magpie-<skill>
```
The snapshot path appears exactly **once** — in
`.agents/skills/`. Re-pointing the framework at a new snapshot,
or repairing a broken link, is a single-source operation; the
relays follow automatically. Adopters keep their own native
(non-`magpie-`) skills in `.claude/skills/` / `.github/skills/`
untouched — only the `magpie-*` entries are relayed.
(Global / per-user skill paths diverge across agents — e.g.
`~/.cursor/skills/`, `~/.codex/skills/`, `~/.gemini/skills/`. The
framework's adoption is **project-scope** — it writes inside the
adopter repo — so it only ever cares about the project columns
above. Global installs are the operator's concern, out of scope
for `setup`.)
## Active-target selection — which dirs `adopt` wires
On every `adopt` / `upgrade` / `worktree-init`, the **active
target set** is computed as the union of:
1. **The always-on neutral targets** — `universal`
(`.agents/skills/`, canonical) **plus** the `claude-code` +
`github` relay pair. These three are wired unconditionally; the
relays are cheap relative symlinks, harmless to an agent that
never reads them, and dropping them is not a supported
configuration.
2. **Any other registry target already present in the repo** —
if `.windsurf/skills/` or `.goose/skills/` (etc.) already
exists as a real directory, it is added to the active set so
that agent sees the framework skills too (as a relay).
3. **Explicit opt-in** via the `agents:<list>` flag (see
[`SKILL.md` Inputs](SKILL.md#inputs)) — a comma-separated list
of registry ids. When passed it **replaces** the auto-detected
set (1)+(2) for that run; `universal` is always retained even
if omitted, because it is the canonical home every relay points
at — dropping it would leave the relays dangling.
The flow **never** removes or rewrites an adopter's own
non-`magpie-` skill content in any target dir. It only adds /
repairs `magpie-*` symlinks. Whatever layout an adopter's
`.claude/` / `.github/` directories were in before, the framework
always wires the `magpie-*` set the same way: canonical in
`.agents/skills/`, relayed everywhere else.
## How the framework's rules generalise across targets
Every adoption rule is "canonical link, then relays", not
"per-target independent link":
- **`magpie-` prefix** ([`SKILL.md` Golden rule 6](SKILL.md#golden-rules))
— unchanged. Every framework skill is `magpie-<skill>` in
*every* active target dir, so it never collides with an
adopter's own skills regardless of agent.
- **`.gitignore`** — one **uniform** block per active target dir,
with no per-layout variation: `/<dir>/magpie-*` ignored plus
`!/<dir>/magpie-setup` un-ignored. The negation keeps the one
committed bootstrap (`magpie-setup`) tracked; the glob ignores
the rest (the canonical links target the gitignored snapshot, so
the relays that follow them dangle on a fresh clone). See
[`adopt.md` Step 7](adopt.md#step-7--gitignore-entries-fresh-only).
- **Symlink wiring** — the canonical `magpie-<n>` →
snapshot/source link is created once in `.agents/skills/`; every
other active target (`claude-code`, `github`, `windsurf`,
`goose`, …) gets a per-skill relay `magpie-<n>` →
`../../.agents/skills/magpie-<n>`. See
[`adopt.md` Step 8](adopt.md#step-8--wire-up-the-framework-skill-symlinks).
- **Committed bootstrap** ([`SKILL.md` Golden rule 6](SKILL.md#golden-rules))
— the one committed framework artefact, `magpie-setup`, lives at
the **canonical** `.agents/skills/magpie-setup/` (a committed
copy for adopters; a committed symlink under self-adoption).
`.claude`/`.github` carry a committed relay symlink to it.
- **Local self-adoption** (framework checkout) — canonical
committed symlinks into `../../skills/<skill>/` in
`.agents/skills/`, plus committed relays into
`../../.agents/skills/magpie-<skill>` in every other active
target. See
[`adopt.md` → Local self-adoption](adopt.md#local-self-adoption-methodlocal).
- **`unadopt` / `worktree-init`** — every active target dir is
torn down / propagated uniformly. Removing only `.claude` +
`.github` would orphan the canonical `.agents/skills/magpie-*`
links; removing only `.agents` would leave every relay dangling.
## SKILL.md format portability
The same `SKILL.md` is valid in every target with no
per-agent edit:
| Frontmatter field | Cross-agent behaviour |
|---|---|
| `name`, `description` | Universal — discovery works everywhere. |
| `when_to_use` | Claude-family routing hint; other agents may ignore it → discovery still works off `description`, only routing precision degrades. |
| `argument-hint`, `capability` | magpie / Claude extensions; non-supporting agents silently ignore them. |
| `license` | Inert metadata. |
Unknown frontmatter is ignored by each agent (graceful
degradation), so there is **no compile step and no per-agent
file**. The gitignored snapshot stays the single source of truth;
`.agents/skills/` links into it, and every other target dir
resolves into it through the `.agents/skills/` relay.
## The Claude-Code-only layer (not wired for other targets)
Some of what `adopt` installs is **genuinely Claude-Code-specific
and is wired only when the `claude-code` target is active**:
- `.claude/settings.json` — the sandbox (`network.allowedDomains`
allowlist, `filesystem.denyRead`), the MCP-tool permission
allowlist, and the hooks. Schema:
`claude-code-settings.json`.
- `.claude/settings.local.json` — per-machine sandbox-allowlist
entries.
- The `setup-isolated-setup-*` skill family — sandbox / pinned-
tools / hooks installer.
Other agents adopt the **skills** (the neutral content) **without**
this layer.
> **Security caveat — this layer is a control, not cosmetics.**
> For a security framework the sandbox is a *confidentiality
> control* (it blocks exfiltration of non-public vulnerability
> data and reading `~/`). Running a security-class skill on an
> agent that lacks an equivalent control is a **policy decision**,
> not graceful degradation. Adopting the skills onto a non-Claude
> agent is supported; *executing confidential workflows there*
> requires the project to either declare that agent unsupported
> for those workflows or provide an equivalent control. `adopt`
> itself only places files — it does not grant that approval.