ADR-012: Skills are a plugin capability; "Ever Works Skills" is the first-party plugin shipping the default catalog
Status
Accepted — 2026-05-25. Operator instruction during round 6 review of PR #1017. Supersedes the relevant part of ADR-006 (Skills are no longer "core not plugin").
Date
2026-05-25
Context
ADR-006 (round 1) declared that Agents, Skills, and Tasks are all core domain concepts that USE plugins but ARE NOT plugins. Round 6 review by the operator reversed two of those three:
"For Skills, I think that's a good idea to implement it as plugin too — 'Ever Works Skills' — and later other community members can provide own 'Skills' plugins that integrate other popular skills catalogs. So yes, let's do it as plugin too!"
This ADR captures the new direction for Skills specifically. (Task tracking gets its own parallel ADR-013; Agents stay core per the corrected ADR-006.)
Decision
Skills are a plugin capability. The platform defines a new plugin category skills-provider with the contract ISkillsProviderPlugin. The first-party plugin "Ever Works Skills" is the default skills-provider, shipping the platform's curated catalog from the ever-works/skills repo (per ADR-014).
Future community plugins can implement the same capability — e.g.:
- An "Anthropic Skills" plugin loading from Anthropic's catalog format.
- An "AWS Q Developer Skills" plugin loading from AWS's catalog.
- A "Custom Skill Library" plugin loading from a self-hosted Git URL.
A tenant can have one or more skills-provider plugins enabled at once; resolved skills come from the union of all enabled providers, deduplicated by slug with priority by plugin install order.
What stays core
The following remain core domain concerns and do not move into plugin packages:
- Skill DB schema —
skillsandskill_bindingstables stay in the platformpackages/agent/src/entities/. The plugin returns Skills; the platform stores user-installed copies + bindings. - Skill resolution + injection logic —
SkillBindingRepository.resolveActive(),AiFacadeService.assembleSystemMessage(). Plugin-agnostic. - The Skills UI —
/skillspage, the Skills tab on Agents/Works/Missions/Ideas. Plugin-agnostic. - The
skills:array inworks.yml/mission.yml— schema stays in the platform's Zod definitions.
The plugin's job is narrow: source the catalog. Read entries from somewhere (a Git repo, an API, a local folder) and return them in the contract shape.
ISkillsProviderPlugin contract
Defined in packages/plugin/src/contracts/capabilities/skills-provider.interface.ts:
export interface SkillCatalogEntry {
slug: string;
title: string;
description: string;
frontmatter: SkillFrontmatter;
body: string;
version: string;
tags: string[];
sourceUrl?: string; // link to the canonical source
}
export interface ISkillsProviderPlugin extends IPlugin {
readonly providerName: string;
/** List all available catalog entries (paginated). */
listEntries(options: { limit: number; offset: number; tags?: string[]; search?: string }): Promise<{
entries: SkillCatalogEntry[];
total: number;
}>;
/** Fetch one entry by slug. */
getEntry(slug: string): Promise<SkillCatalogEntry | null>;
/** Optional: signal that the catalog has updated. */
checkForUpdates?(installedVersions: Record<string, string>): Promise<{
updated: { slug: string; oldVersion: string; newVersion: string }[];
}>;
}
SkillsFacadeService (new)
Mirrors the AiFacadeService shape. Resolves enabled skills-provider plugins for the user/work scope, calls their methods, dedupes, returns. Skill UI + injection always go through this facade — never directly into a specific plugin.
"Ever Works Skills" — the first-party plugin
Lives at packages/plugins/everworks-skills/. package.json everworks.plugin block:
{
"id": "everworks-skills",
"name": "Ever Works Skills",
"category": "skills-provider",
"capabilities": ["skills-provider"],
"defaultForCapabilities": ["skills-provider"],
"visibility": "public",
"settingsSchema": {
/* repo ref, local path override, refresh interval */
}
}
Implementation:
- On
onLoad(), clonesever-works/skills(--depth 1) to a tmp dir; reads all.mdfiles; parses frontmatter; caches the parsed result. - Settings (per
settings-system.md):repoRef(defaultlatest),localPath(optional, for self-hosted/offline),cacheTtlSeconds(default 3600). listEntries,getEntry,checkForUpdatesread from the cache, refreshing on TTL expiry.
Consequences
Positive
- Symmetric with existing plugin architecture. Skills join
ai-provider,git-provider,search, etc. as a real capability — no special-case "core not plugin" carve-out. - Community contribution surface. Anyone can ship an alternative
skills-providerplugin pointing at their own catalog. - Self-hosted users can fully detach by enabling only the local-path override (via plugin settings) — no monorepo dependency on the catalog content.
- Multiple-catalog union. A tenant can enable "Ever Works Skills" AND "Awesome AI Skills" plugins simultaneously; resolved set is the union.
- Aligns with ADR-014 (catalogs in separate repos) — the plugin is the consumer of the repo.
Negative
- One more plugin to maintain. Counts toward
built-in-plugins.mdConstitution VIII inventory. Mitigated by: this is the cost of generality. - Indirection cost. A read like "list installed skills" now goes through
SkillsFacadeService → ISkillsProviderPlugin. Mitigated by: caching at the plugin layer means the per-call cost is process-memory only. ISkillsProviderPluginis a new public contract. Once shipped, breaking changes require deprecation cycles per Constitution X.
Mitigations
- Plugin SDK contract test ensures every
skills-providerplugin honors the contract (per existingpackages/plugin/src/contracts/__tests__/pattern). - Default-on for new tenants — "Ever Works Skills" is
defaultForCapabilities: ['skills-provider']so users get a working catalog immediately. - Fallback when no provider enabled —
SkillsFacadeService.listEntries()returns empty; UI shows "No skills providers enabled — install one from /plugins to see the catalog."
What changes in the Skills feature spec
The Skills product behavior described in features/skills/spec.md is unchanged. What changes is the implementation packaging:
- Was:
apps/api/src/skills/catalog/(in-monorepo) +SkillCatalogServiceinapps/api/. - Now:
ever-works/skillsrepo +packages/plugins/everworks-skills/plugin +SkillsFacadeServiceinpackages/agent/.
User-installed skills, custom skills, agent-bound skills, the resolution hierarchy, injection rules, frontmatter shape — all unchanged.
Alternatives Considered
1. Keep Skills as core (status quo of ADR-006)
Rejected per operator instruction. Misses the community-extension opportunity.
2. Single plugin only (no capability + no facade)
Rejected. Locks users to "Ever Works Skills"; no extension story.
3. Plugin for catalog source, but no facade indirection
Rejected. Means every consumer (Agents, generators, Skills page) has to know which plugin is enabled. Facade is the cleaner pattern, matching AiFacadeService precedent.
Related
- ADR-006 (partially superseded — Skills no longer "core not plugin").
- ADR-007 (Skill catalog in-monorepo — superseded).
- ADR-013 (Task tracking as plugin — parallel decision).
- ADR-014 (No hardcoded catalogs —
ever-works/skillsrepo). features/skills/spec.md,features/skills/plan.md— product behavior unchanged; implementation packaging updated.- Constitution Principle I (Plugin-First) and Principle II (Capability-Driven Resolution) — this ADR brings Skills under both.