Skip to content

Managing registries

Registries are catalogs of skills and plugins beyond a single owner/repo URL. Each registry is an adapter capa stores in its database and materializes under ~/.capa/registries-managed/<slug>/. You browse and install from the CLI or the Web UI.

Supported source types: github, gitlab, url, and claude-marketplace.

A fresh install comes with three first-party registries, seeded on the first server start:

SlugCatalogCapabilities
skills-shskills.shSkills
claude-pluginsClaude PluginsPlugins
cursor-marketplaceCursor MarketplacePlugins

Their adapters are bundled in the capa binary and pinned by SHA-256. capa never fetches them from GitHub, not when seeding, not on capa registry refresh, and not when you re-add one with capa registry add infragate/capa@<slug> (use the original slug). A copy on disk that doesn’t match the pin is refused.

  1. Add a registry once (capa registry add …).
  2. Search / browse items (capa registry search or Web UI).
  3. Install with capa add <slug>:<itemId>, then capa install (unless you used --passthrough / --install).

The adapter decides whether an item is a skill or a plugin. See Registries for the resource-focused reference and CLI: registry for every flag.

Terminal window
capa registry # same as list
capa registry list
capa registry path # ~/.capa/registries-managed/
capa registry add <source> [slug] [--type <type>] [--no-cache] [-y]
capa registry approve <slug> [-y]
capa registry refresh <slug> [--no-cache]
capa registry enable <slug>
capa registry disable <slug>
capa registry remove <slug>
capa registry search [slug] [query]
capa registry search -q design --capability skills
ActionEffect
addFetch and stage the adapter, show its SHA-256 and source preview, and install it after you confirm; derive or set a slug
approveReview and execute a staged adapter that has not been approved yet
refreshRe-fetch from the stored source (updates resolved ref and pinned hash)
disable / enableHide or restore without deleting
removeDelete the DB row and materialized adapter files
searchQuery one registry or all enabled registries
  1. Pick a source form

    FormExample
    GitHub searchowner/repo@my-registry
    GitHub exact pathowner/repo::registries/internal
    GitLabowner/repo::path --type gitlab (or a gitlab.com/… style source)
    HTTPS adapter URLhttps://example.com/adapter.ts (public HTTPS only)
    Claude marketplaceowner/repo (bare owner/repo is detected as a marketplace), or --type claude-marketplace
  2. Install the adapter

    Terminal window
    capa registry add my-org/internal-tools@acme-skills
    capa registry add anthropics/claude-plugins-official marketplace --type claude-marketplace

    Pass a second argument when you want an explicit slug. capa prints the source, the content SHA-256, and a preview, then asks before executing the adapter. In CI, pass --yes.

  3. Verify

    Terminal window
    capa registry list

    Confirm status is healthy and the registry is enabled.

  4. Install an item

    Terminal window
    capa add skills-sh:vercel-labs/skills/find-skills
    capa install

Claude marketplace registries point at a repo that publishes marketplace.json. Set the type explicitly when auto-detect is ambiguous:

Terminal window
capa registry add anthropics/claude-plugins-official my-marketplace --type claude-marketplace

Then install with the usual slug:itemId form. You can also manage marketplaces from Registries → Manage registries in the Web UI.

An adapter is a single self-contained adapter.ts (or .js / .mjs) file. capa fetches it into ~/.capa/registries-managed/<slug>/ and records its SHA-256. Only after you confirm does capa dynamic-import it to validate the shape; every later load re-checks the file against the recorded hash. There is no compile step: Bun transpiles TypeScript on the fly inside the server process.

interface RegistryAdapter {
manifest: RegistryManifest;
search(args: RegistrySearchArgs): Promise<RegistrySearchResult>;
view(args: RegistryViewArgs): Promise<RegistryItemDetail>;
}
PieceRole
manifestid, name, optional description / homepage / icon, and capabilities: ['skills' | 'plugins', …]
searchFast, lightweight listings (debounced UI search / capa registry search)
viewFull detail for one item: markdown preview plus installSnippet pasted into capabilities.yaml

installSnippet for a skill typically looks like:

{
id: 'my-skill',
type: 'github',
def: { repo: 'owner/repo@my-skill' },
}

Keep type definitions inline in the file so the adapter has no external imports.

Save as my-registry/adapter.ts (folder name becomes the search basename when you use owner/repo@my-registry):

type RegistryCapability = 'skills' | 'plugins';
interface RegistryManifest {
id: string;
name: string;
description?: string;
homepage?: string;
icon?: string;
capabilities: RegistryCapability[];
}
interface RegistryItemSummary {
id: string;
capability: RegistryCapability;
title: string;
description?: string;
author?: string;
version?: string;
icon?: string;
tags?: string[];
homepage?: string;
}
interface RegistryItemDetail extends RegistryItemSummary {
preview: string;
installSnippet: Record<string, unknown>;
files?: string[];
}
interface RegistryAdapter {
manifest: RegistryManifest;
search(args: {
capability: RegistryCapability;
query?: string;
limit?: number;
}): Promise<{ items: RegistryItemSummary[]; total?: number }>;
view(args: {
capability: RegistryCapability;
id: string;
}): Promise<RegistryItemDetail>;
}
const adapter: RegistryAdapter = {
manifest: {
id: 'acme-skills',
name: 'Acme Skills',
description: 'Internal skill catalog',
homepage: 'https://registry.example.com',
capabilities: ['skills'],
},
async search({ capability, query, limit }) {
if (capability !== 'skills') return { items: [] };
const url = new URL('https://registry.example.com/api/search');
if (query) url.searchParams.set('q', query);
url.searchParams.set('limit', String(limit ?? 20));
const res = await fetch(url);
if (!res.ok) throw new Error(`search failed: ${res.status}`);
const data = await res.json();
return {
items: (data.results ?? []).map((r: any) => ({
id: r.id,
capability: 'skills' as const,
title: r.name,
description: r.summary,
author: r.author,
})),
total: data.total,
};
},
async view({ capability, id }) {
if (capability !== 'skills') {
throw new Error(`Unsupported capability: ${capability}`);
}
const res = await fetch(`https://registry.example.com/api/items/${id}`);
if (!res.ok) throw new Error(`view failed: ${res.status}`);
const data = await res.json();
return {
id,
capability: 'skills',
title: data.name,
description: data.summary,
author: data.author,
preview: data.readme, // markdown shown in the Web UI
installSnippet: {
id: data.slug,
type: 'github',
def: { repo: `${data.owner}/${data.repo}@${data.slug}` },
},
};
},
};
export default adapter;
  1. Host the file in a git repo (recommended) or at an HTTPS URL.

    my-org/internal-tools/
    └── registries/
    └── acme-skills/
    └── adapter.ts
  2. Add it to capa

    Terminal window
    # Exact path in a GitHub repo
    capa registry add my-org/internal-tools::registries/acme-skills
    # Or search by folder basename
    capa registry add my-org/internal-tools@acme-skills
    # Or a direct public HTTPS URL (localhost and private addresses are rejected)
    capa registry add https://raw.example.com/acme-skills/adapter.ts acme-skills
  3. Verify and use

    Terminal window
    capa registry list
    capa registry search acme-skills "checkout"
    capa add acme-skills:<itemId>
    capa install
  • manifest.id must be unique across loaded adapters. Collisions skip the second adapter and show as failed on capa registry list.
  • Return early for unsupported capabilities ({ items: [] } from search, throw from view).
  • search / view time out after 15 seconds. Cache slow upstream responses in memory when needed.
  • preview is Markdown (sanitized in the Web UI). Returning full SKILL.md content works well.
  • Refresh after edits: capa registry refresh <slug> re-fetches the source and re-pins its hash; restart the server for a fully clean reload. Editing the materialized copy under ~/.capa/registries-managed/ directly makes capa refuse to load it.
  • Test over HTTPS or git. URL sources must be public HTTPS, so localhost servers won’t work; host the adapter in a git repo and add it with owner/repo::path instead.
  • Reference adapters in the capa repo: registries/skills-sh, registries/cursor-marketplace, registries/claude-plugins.

With the server running, open the local UI:

  • Browse enabled registries and install items: items already in the project show an Installed check mark instead of Add
  • Manage registries (/ui/registries/settings) to add, preview, refresh, enable/disable, or remove adapters. Adding an adapter requires previewing its source and confirming that you trust it

See Web UI.