Introduce the skills catalog: the second half of the two-layer agent
model (skills teach agents HOW to think about a problem; MCP servers
give them the ABILITY to act). Delivery via MCP resources lands in
Slice 3.5b; this slice ships the data model + API surface.
Migration 0049 extends the legacy `skills` table (from 0001_init.sql,
originally a workspace catalog of markdown snippets) with the richer
typing we need — name, when_to_use, tags, source_kind, current_version
— rather than duplicating tables. Also adds:
- skill_versions (version history for level-up promotions +
rollback; back-pointer via promoted_from
JSONB records agent_id / research artifact /
brain memory that produced it)
- template_role_skills (m2m binding skills to team-template roles
with pin_in_context + order_idx)
- agent_skills_ext (per-agent overlay: include=true adds a skill
to the bundle; include=false prunes a
template default for this specific agent)
Rust surface:
- cm_db::repo::skills_catalog with typed Skill/SkillVersion/
AgentSkillBinding structs + upsert_builtin (idempotent — bumps
version + appends to skill_versions ONLY when body changes) +
list_visible/get/get_by_name reads + template + agent binding
helpers + effective_for_agent (merges template defaults with
agent overrides, applies exclude precedence, batch-fetches skill
bodies)
- cm_api::routes::skills_catalog with:
GET /api/skills — list visible
GET /api/skills/{id} — detail
GET /api/claws/{id}/skills — effective binding (accepts
template_id + role_slot as query args to merge in template
defaults)
Follow-ups:
- Slice 3.5b: clawmates_skills MCP server exposes catalog as MCP
resources, honoring pin_in_context for auto-injection
- Slice 3.5c: seed ~40-60 builtin skills across the 6 stacks
Co-Authored-By: Claude Opus 4.7 <[email protected]>
101 lines
4.9 KiB
SQL
101 lines
4.9 KiB
SQL
-- Slice 3.5a — skills catalog (extends the legacy skills tables from
|
|
-- 0001_init.sql rather than duplicating them).
|
|
--
|
|
-- The two-layer model:
|
|
-- Skills teach agents HOW to think about a problem (static, curated
|
|
-- markdown, no auth, no observability of the call itself).
|
|
-- MCP servers give agents the ABILITY to act (live, governed, audited).
|
|
--
|
|
-- Delivery: `clawmates_skills` MCP server (Slice 3.5b) exposes skills as
|
|
-- MCP resources. Some skills are pinned (always injected into context);
|
|
-- the rest are discovered on-demand via resources/list + read lazily.
|
|
--
|
|
-- This migration keeps the legacy `skills` + `installed_skills` tables
|
|
-- intact (they already ship a workspace-scoped catalog with body_md-
|
|
-- shaped content) and adds the missing structural pieces:
|
|
-- - richer typing columns on `skills` (name, when_to_use, tags,
|
|
-- source_kind, current_version, updated_at)
|
|
-- - `skill_versions` for level-up promotions + rollback
|
|
-- - `template_role_skills` binding skills to team-template roles
|
|
-- with an optional pin_in_context flag
|
|
-- - `agent_skills_ext` for per-agent overrides (level-up adds
|
|
-- personal learnings that don't yet merit the template default set)
|
|
--
|
|
-- `agent_skills_ext` sits alongside `installed_skills` — the latter
|
|
-- is the "user manually installed this" tracker; the new table is the
|
|
-- level-up overlay (added, pinned, promoted). Slice 9 folds them.
|
|
|
|
-- ── Extend `skills` ──────────────────────────────────────────────
|
|
|
|
ALTER TABLE skills
|
|
ADD COLUMN name TEXT,
|
|
ADD COLUMN when_to_use TEXT,
|
|
ADD COLUMN tags TEXT[] NOT NULL DEFAULT '{}',
|
|
ADD COLUMN source_kind TEXT NOT NULL DEFAULT 'hand_authored',
|
|
ADD COLUMN current_version INT NOT NULL DEFAULT 1,
|
|
ADD COLUMN updated_at TIMESTAMPTZ NOT NULL DEFAULT now();
|
|
|
|
-- Backfill `name` from `title` for existing rows so the new UNIQUE
|
|
-- constraint doesn't reject them.
|
|
UPDATE skills SET name = title WHERE name IS NULL;
|
|
ALTER TABLE skills ALTER COLUMN name SET NOT NULL;
|
|
|
|
-- One skill per (workspace scope, name). NULL workspace = builtin/global.
|
|
CREATE UNIQUE INDEX skills_name_uniq
|
|
ON skills (COALESCE(workspace_id, '00000000-0000-0000-0000-000000000000'::uuid), name);
|
|
CREATE INDEX skills_source_idx ON skills (source_kind);
|
|
CREATE INDEX skills_tags_gin ON skills USING gin (tags);
|
|
|
|
-- ── Version history ─────────────────────────────────────────────
|
|
|
|
CREATE TABLE skill_versions (
|
|
skill_id UUID NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
version INT NOT NULL,
|
|
body_md TEXT NOT NULL,
|
|
description TEXT NOT NULL,
|
|
when_to_use TEXT,
|
|
-- Back-pointer to the source that produced this version:
|
|
-- {"kind":"agent_brain","agent_id":"..."} etc.
|
|
promoted_from JSONB,
|
|
author_id UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
approved_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (skill_id, version)
|
|
);
|
|
|
|
-- ── Template-role bindings ──────────────────────────────────────
|
|
|
|
CREATE TABLE template_role_skills (
|
|
template_id UUID NOT NULL REFERENCES team_templates(id) ON DELETE CASCADE,
|
|
slot TEXT NOT NULL,
|
|
skill_id UUID NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
-- true → injected at turn start; false → discoverable via MCP
|
|
pin_in_context BOOLEAN NOT NULL DEFAULT false,
|
|
order_idx INT NOT NULL DEFAULT 0,
|
|
PRIMARY KEY (template_id, slot, skill_id),
|
|
FOREIGN KEY (template_id, slot)
|
|
REFERENCES template_roles(template_id, slot) ON DELETE CASCADE
|
|
);
|
|
CREATE INDEX template_role_skills_lookup_idx
|
|
ON template_role_skills (template_id, slot, order_idx);
|
|
|
|
-- ── Per-agent overrides (level-up) ──────────────────────────────
|
|
--
|
|
-- Semantics:
|
|
-- included = true → add this skill to the agent's bundle even if
|
|
-- the template didn't include it
|
|
-- included = false → remove a template-provided skill for THIS
|
|
-- agent (level-up "prune what never got used")
|
|
|
|
CREATE TABLE agent_skills_ext (
|
|
agent_id UUID NOT NULL REFERENCES agents(id) ON DELETE CASCADE,
|
|
skill_id UUID NOT NULL REFERENCES skills(id) ON DELETE CASCADE,
|
|
included BOOLEAN NOT NULL DEFAULT true,
|
|
pin_in_context BOOLEAN NOT NULL DEFAULT false,
|
|
added_by UUID REFERENCES users(id) ON DELETE SET NULL,
|
|
added_reason TEXT,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
PRIMARY KEY (agent_id, skill_id)
|
|
);
|
|
CREATE INDEX agent_skills_ext_agent_idx ON agent_skills_ext (agent_id);
|