Crux
GuidesSkills

Registries

Load skills from skills.sh, from disk, or from your own private registry using the .well-known/agent-skills protocol.

Skills can come from three places: inline in your code, a file on disk, or a remote registry. Registries are how you share skills across repos, teams, or the public community.

skills.sh — The Built-in Community Registry

skills.sh is the community registry for SKILL.md files. Crux exports it as the skillsSh registry value:

import { skill, skillsSh } from '@crux/core/skill'

const seo = skill.fromRegistry(skillsSh, 'mattpocock/skills/seo-analysis')
const research = skill.fromRegistry(skillsSh, 'owner/repo/skill-name')

Registry skills use the same shape everywhere: pass a registry value and the skill path inside that registry.

Content is fetched lazily on the first prompt.resolve() and cached in process memory for the lifetime of the runtime — subsequent resolves do not re-fetch.

Custom Registries

Host your own skills on any HTTPS server that implements the .well-known/agent-skills/ protocol. Useful for shared internal skills — brand voice, legal review, compliance checks — that you don't want to publish publicly.

import { skill, registry } from '@crux/core/skill'

const internal = registry({
  name: 'internal',
  baseUrl: 'https://skills.mycompany.com',
  auth: () => process.env.SKILLS_TOKEN,
})

const brand = skill.fromRegistry(internal, 'brand-guidelines')
const legal = skill.fromRegistry(internal, 'legal/disclosure-language')

Registry configuration

FieldTypeDescription
namestringStable registry name used in skill ids and local Project Model output
baseUrlstringRegistry base URL — requests go to <baseUrl>/.well-known/agent-skills/<path>
auth() => string | undefinedOptional callback returning a bearer token. Called per request, so it can read fresh env vars or refreshed tokens.

registry() returns a registry value. Pass that value to skill.fromRegistry(internal, 'brand-guidelines'); do not repeat registries in crux.config.ts. Crux does not scan project files at runtime to find custom registries, so create registry-backed skills from the registry value in the module that uses them.

Loading from Disk

For skills you own and version-control inside your repo, use skill.fromFile():

const seo = skill.fromFile('./skills/seo-analysis/SKILL.md')

This reads synchronously at import time and automatically picks up any sibling references/*.md files. See Writing skills for the directory layout.

Disk-loaded skills don't need a registry — they're already available the moment the module imports.

Error Handling

All three loaders throw SkillLoadError (a tagged error) when something goes wrong:

CauseWhen
File not foundskill.fromFile() with a bad path
Invalid frontmatterMissing name or description, or malformed YAML
Missing pathskill.fromRegistry(internal) without a registry-local skill path
Unknown registry valuePassing a value that is not a Registry
Network failure / 404Registry fetch fails at resolve time

For fromFile and inline, errors surface at import time. For fromRegistry, remote fetch errors surface at the first prompt.resolve() because registry content is loaded lazily.

import { SkillLoadError } from '@crux/core/skill'

try {
  await myPrompt.resolve({ input })
} catch (err) {
  if (err instanceof SkillLoadError) {
    console.error(`Skill ${err.skillId} failed: ${err.reason}`)
  }
  throw err
}

Caching

Registry fetches are cached in memory for the lifetime of the process, keyed by the full identifier. There is no TTL — if you publish a new version of a skill, restart the runtime to pick it up. The instrumentation hooks onSkillCacheHit and onSkillCacheMiss let you observe cache behavior in production (see the API reference).

On this page