//! `/api/skills/*` — the skills catalog surface (Slice 3.5a). //! Slice 3.5b's `clawmates_skills` MCP server layers on top. use axum::{ extract::{Path, Query, State}, Json, }; use cm_db::repo::skills_catalog::{AgentSkillBinding, Skill}; use serde::Deserialize; use uuid::Uuid; use crate::{ApiError, AppState, Authed}; pub async fn list( State(state): State, Authed(user): Authed, ) -> Result>, ApiError> { let rows = cm_db::repo::skills_catalog::list_visible(&state.pool, user.workspace_id.as_uuid()).await?; Ok(Json(rows)) } pub async fn get( State(state): State, Authed(_user): Authed, Path(id): Path, ) -> Result, ApiError> { let skill = cm_db::repo::skills_catalog::get(&state.pool, id) .await? .ok_or(ApiError::NotFound)?; Ok(Json(skill)) } #[derive(Debug, Deserialize)] pub struct AgentBindingQuery { pub template_id: Option, pub role_slot: Option, } /// Effective skill bundle for a specific agent. When `template_id` + /// `role_slot` are provided, template defaults are merged with the /// agent's per-agent overrides; otherwise only overrides are returned. pub async fn effective_for_agent( State(state): State, Authed(_user): Authed, Path(agent_id): Path, Query(q): Query, ) -> Result>, ApiError> { let rows = cm_db::repo::skills_catalog::effective_for_agent( &state.pool, agent_id, q.template_id, q.role_slot.as_deref(), ) .await?; Ok(Json(rows)) }