<< All versions

Skill v1.0.0

currentLLM-judged scan90/100
adyen/agent-marketplace/registry-authoring
──Details
PublishedOctober 22, 2026 at 05:48 AM
Content Hashsha256:9efeccfb92765c21...
Git SHAd58ebea87c1a
──Files
Files (1 file, 16.5 KB)
SKILL.md16.5 KBactive
SKILL.md · 376 lines · 16.5 KB

version: "1.0.0" name: registry-authoring description: > How to create and maintain a registry that agent-marketplace can discover, ingest, and sync into harness-native layouts. Use when creating a registry repo, adding skills, commands, subagents, hooks, MCP servers, or collections to one, writing registry CI validation, releasing a registry, or troubleshooting resources that are missing or malformed. metadata: tags:

  • registry
  • skills
  • authoring
  • agent-marketplace

Registry Authoring

A registry is a plain Git repository (or local directory) whose resources are discovered from the native layout of one source harness. There is no manifest: the CLI reads the layout's well-known paths. The authoritative references are registry layout, resource metadata, and collections.

Minimal registry (Claude layout)

my-registry/
├── skills/
│ ├── deploy-conventions/
│ │ ├── SKILL.md
│ │ └── references/checklist.md ← ships with the skill
│ └── incident-response/
│ └── SKILL.md
├── commands/
│ └── release.md
├── collections/
│ └── platform-onboarding/
│ └── COLLECTION.yaml
├── CODEOWNERS
└── README.md

A consumer selects from it with a v4 config:

yaml
# .agents/config.yaml
apiVersion: agent-marketplace/v4
sources:
platform:
git: git@github.com:example-org/my-registry.git
ref: v1.4.0
source_harness: claude # state the layout instead of relying on detection
resources:
platform:
include:
- skill/deploy-conventions
- command/release
- collection/platform-onboarding

Consumers can also select by tags: [...] or take everything with all: true. Set the source path: when resources live under a subdirectory: path: resources discovers resources/skills/..., resources/commands/..., and so on.

Layout by source harness

Bundle and root-form paths are relative to the registry root, or to the source's path.

HarnessSkillCommandSubagentHookMCP server
claudeskills/<name>/SKILL.mdcommands/<name>.mdagents/<name>.mdhooks/hooks.json.mcp.json at the bundle root
factoryskills/<name>/SKILL.mdcommands/<name>.mddroids/<name>.mdhooks/hooks.jsonmcp.json
cursorskills/<name>/SKILL.mdcommands/<name>.mdagents/<name>.mdhooks/hooks.jsonmcp.json
windsurfskills/<name>/SKILL.mdworkflows/<name>.mdnot supportedhooks.jsonmcp_config.json
gemini.gemini/skills/<name>/SKILL.md or .agents/skills/…commands/<name>.toml.gemini/agents/<name>.md.gemini/settings.json#hooks.gemini/settings.json#mcpServers
devinskills/<name>/SKILL.mdskill dir + x-agent-marketplace-kind: commandagents/<name>.mdhooks.v1.jsonmcp_config.json
codex.agents/skills/<name>/SKILL.mdskill dir + x-agent-marketplace-kind: command.codex/agents/<name>.toml.codex/hooks.json#hooks.codex/config.toml#mcp_servers
opencode.opencode/skills/<name>/SKILL.md.opencode/commands/<name>.md.opencode/agents/<name>.md.opencode/hooks/agent-marketplace/<name>.jsonopencode.json[c]#mcp

There are no generic subagents/ or mcp/ roots: a Claude subagent ships as agents/<name>.md, and Claude MCP servers ship in a root .mcp.json. Files in directories the layout does not read are silently undiscovered. Explicit workspace roots keep their resources inside the matching harness prefix; their hook files are listed below.

Hooks

A registry may instead be an explicit native workspace root. Its hook file is:

HarnessNative workspace hook path
claude.claude/settings.json#hooks
factory.factory/hooks.json
cursor.cursor/hooks.json#hooks
windsurf.devin/hooks.json#hooks
gemini.gemini/settings.json#hooks
devin.devin/hooks.v1.json
codex.codex/hooks.json#hooks
opencode.opencode/hooks/agent-marketplace/<name>.json beside the fixed adapter

Use the source harness's documented workspace directories for its other resources. A Claude workspace source uses .claude/skills/, .claude/commands/, .claude/agents/, and .claude/settings.json together; it does not mix root-level bundle directories or a .claude-plugin manifest with that workspace hook file.

For example, a Claude PreToolUse hook that runs only for Bash can ship in .claude/settings.json:

json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"${CLAUDE_PLUGIN_ROOT}/.claude/scripts/check.sh\""
}
]
}
]
}
}

${CLAUDE_PLUGIN_ROOT} is the bundle root, the folder that contains .claude/, so the path after the token includes .claude/. Keep scripts/check.sh below .claude/ beside the hook definition. Sync copies regular support files byte-for-byte into hook-support/<source>/<hook>/.claude/scripts/check.sh for each selected target and rewrites the plugin-root token to that installed location. A command that omits .claude/, such as ${CLAUDE_PLUGIN_ROOT}/scripts/check.sh, still installs for one minor release with a hook.support-reference-legacy warning; change it to the bundle-root path. Repository-scope commands use the target's documented project-directory variable or working-directory base; user-scope commands use the absolute installed path. The definition JSON itself is not copied into support output.

Hook matchers must cover the same source tool concepts exactly, including anchoring and MCP naming. A partial or unknown mapping suppresses that hook with an error, while valid sibling hooks still install. The shipped hook and tool catalog lists the exact target events, types, names, and timeout rules. Do not expect every resource with an explicit tool list to convert to every target; an allow-list can only select tools the target actually has, and it cannot use a combined target tool to cover only part of that tool's scope.

A hook command or prompt is trusted executable content. Review it as registry code, and note that sync prints changed command and prompt values to stdout for review. That stdout listing is intentionally exact; the application's logs and telemetry do not contain those values.

Autodetection

Without source_harness, the CLI detects the layout from markers: .devin/, then plugin manifests (.claude-plugin/plugin.json, .factory-plugin/, .cursor-plugin/, gemini-extension.json), then .codex/, then Claude-shaped roots, and so on. Root-level Cursor, Windsurf, Factory, and Gemini layouts share folder names with Claude, and a root-level Devin layout (no .devin/) is never detected. .devin/hooks.json is Windsurf's workspace hook file, while .devin/hooks.v1.json is Devin CLI's; set source_harness explicitly when that context could be ambiguous. An .opencode/ folder with OpenCode resources or opencode.json selects OpenCode only when the otherwise-detected layout has no workspace folder of its own (.codex/, .claude/, .factory/, .cursor/, .gemini/, .windsurf/); .opencode/package.json alone is not a marker. When both layouts are present the catalog warns ingest.autodetect.ambiguous, and the unselected layout is not read; a container of bundles with a top-level .opencode/ stays a container. Tell consumers to set source_harness for these and the other ambiguous registries. An undetected registry falls back to Claude with catalog.autodetect.fallback.

Root shapes

A registry root (or the path target) may be one bundle in a layout above, a single SKILL.md (which must declare name), a folder of skill folders, or a pure container of bundles. A container's subfolders are searched for bundles, skipping .git, .github, node_modules, .cache, test, tests, __tests__, harness folders, and resource folders. A root that has its own resources and also sub-bundles is read as the root only, with catalog.discover.sub-bundles-ignored; move root resources into a subfolder such as common/.

Everything inside a skill folder ships with the skill; a nested SKILL.md is a support file, not a separate skill. Keep skills one level under skills/.

SKILL.md frontmatter

Follow the Agent Skills specification:

markdown
---
name: vm-lifecycle
description: >
Create, update, and decommission VMs. Use when working with OpenStack VMs,
Puppet node definitions, or DNS records for a host.
license: Apache-2.0
metadata:
tags: [infra, openstack, puppet]
requires:
-mcp:service-catalog
-name: incident-response
kind: skill
registry: git@github.com:example-org/operations-skills.git
path: skills
---
FieldNotes
nameLowercase letters, digits, hyphens; starts with a letter. Wins over the folder name; the folder name is the fallback, except for a root-level SKILL.md. Keep them equal.
descriptionRequired by the spec. Write it as a when-to-use trigger naming symptoms, tools, and error conditions.
licenseOptional SPDX identifier. Hosted Marketplaces use it for license policy.
x-agent-marketplace-kindcommand marks a skill-shaped command in Devin and Codex layouts only.
metadata.tagsStrings used by consumer tags: and install --tag. Matching is exact and case-sensitive.
metadata.requiresAdvisory prerequisites.

Invalid YAML or an invalid name makes the skill malformed (for example ingest.claude.load-skill-failed): list shows malformed: <reason>, sync counts it under Malformed, and it is not installed. Harness-specific fields such as allowed-tools and model are translated per target; other unknown fields pass through.

Commands and subagents written as Markdown with frontmatter read metadata.tags and metadata.requires from the same place. A Claude command's name comes from its file name.

Tags

Tags group resources for bulk selection. Use a small set (3–6) of lowercase nouns: one domain (infra, security, platform) plus technologies (k8s, puppet). A tag that repeats the resource name selects nothing new.

Prerequisites

requires items are "<kind>:<name>" strings (a bare name means skill) or maps with name and optional kind, registry, path. Kinds are skill, command, subagent, hook, mcp; collection is not accepted. A prerequisite is met by any selected resource of that kind and name, from any source; registry and path only improve the suggested install command. Unmet prerequisites warn and never install anything. Items with an unknown kind or no name are skipped with a log warning.

Collections

A collection bundles resources for one-line selection (collection/<name> in a consumer's include). It lives at collections/<name>/COLLECTION.yaml; flat collections/<name>.yaml files are ignored, and consumers match on the name field, not the folder.

yaml
name: platform-onboarding
description: Common starter resources for platform teams
resources:
- kind: skill
name: deploy-conventions
- kind: command
name: release
- kind: mcp
name: service-catalog
registry: https://github.com/example-org/shared-mcp.git # must already be a consumer source
includes:
- name: security-baseline
  • Member kind defaults to skill; collection is not a member kind (use

includes to nest). The legacy skills: list still works; write resources:.

  • A member without registry comes from the collection's own source, ref, and

path. A collection cannot add a registry to the consumer's config; a member from another registry fails unless the consumer already declares it.

  • includes may name a collection from any configured registry. Shared nested

collections are fine; including an ancestor is a cycle.

  • A missing member warns during sync and fails sync --strict; list does

not check members.

  • The collection itself writes no files; removing one member records an

exclude and keeps the collection selected.

Writing the body

The body is what the agent reads after the description fires. Put the ordered steps first, then decision rules, reference tables, and gotchas. Name exact files, fields, and commands:

markdown
## Step 1: Network configuration (network-configurations repo)
Add the VM's IP allocation in `networks/<dc>/hosts.yaml` under the
datacenter section, with `hostname`, `ip`, and `netmask`.

Reference support files from the body (See references/repo-map.md) so the agent knows to open them.

Check locally

bash
agent-marketplace list --from . # every resource should be `available`

A clean conversion reports Diagnostics 0. Look up ingest.* codes in the diagnostics reference.

CI validation

Sync a throwaway consumer against the checkout with a pinned CLI (v4.0.9 or newer) and every harness your consumers target:

Warning: Hook review stdout includes exact command and prompt values.
This script deliberately captures that stdout in sync.log. Treat the log as
sensitive and do not publish it as a CI artifact.
bash
set -euo pipefail # run under bash
REGISTRY_PATH=$PWD
CONSUMER=$(mktemp -d)
mkdir -p "$CONSUMER/.agents"
cat > "$CONSUMER/.agents/config.yaml" << EOF
apiVersion: agent-marketplace/v4
harnesses: [claude, factory, cursor, windsurf, gemini, devin, codex]
sources:
checkout:
directory: ${REGISTRY_PATH}
resources:
checkout:
all: true
required: true
EOF
cd "$CONSUMER"
agent-marketplace sync --strict -v | tee sync.log
grep -q 'Malformed 0 Errors' sync.log # --strict alone does not fail on malformed resources under all: true
agent-marketplace sync --check
for kind in skill command subagent hook mcp collection; do agent-marketplace list --kind "$kind"; done

Compare the list output with a checked-in expectation so a resource in an unread directory fails CI. The CI how-to explains each step and the download command for a pinned release.

Access control

CODEOWNERS plus branch protection is the trust boundary: whoever can merge into the registry ships resources to every consumer. Require review for skills/ and stricter owners for sensitive paths.

Versioning and releases

  • Tag releases with semantic versions and keep a CHANGELOG.md. Renaming or

removing a resource is a major release.

  • Consumers pinned to a tag stay on it until they edit ref: and run sync.

Consumers on a branch stay on their locked commit until update.

  • update --bump rewrites eligible refs in v4 sources: to tags that differ

only in the final number. Refs declared by included fragments are reported and left unchanged; consumers edit those fragments deliberately.

  • Document the consumer v4 snippet and contribution rules in the registry

README.

Common mistakes

MistakeSymptomFix
Resource in a directory the layout does not read (subagents/, mcp/)Missing from listUse the source harness's native path
Root-level Cursor/Windsurf/Factory/Devin layout without source_harnessRead as Claude; resources missingConsumers set source_harness
Skill nested inside another skillInner skill not discoveredKeep skills one level under skills/
name with capitals, spaces, or underscoresSkill reported malformedUse kebab-case matching the folder
Root resources plus sub-bundlescatalog.discover.sub-bundles-ignoredMove root resources into a subfolder
Collection as collections/<name>.yamlCollection not foundUse collections/<name>/COLLECTION.yaml
Collection member from an unconfigured registryExpansion errorTell consumers to add that source first
Same kind and name in two registriesConsumer warns and picks one winnerRename, or consumers narrow selections
Vague descriptionAgents never activate the skillName triggers, tools, and error text
No CODEOWNERSUnreviewed changes reach consumersAdd CODEOWNERS and branch protection
All versions