use axum::extract::{Query, State}; use axum::http::StatusCode; use axum::Json; use cm_db::repo::skills::Skill; use cm_domain::AgentId; use serde::Deserialize; use uuid::Uuid; use crate::routes::claws::workspace_agent; use crate::{ApiError, AppState, Authed}; #[derive(Deserialize)] pub struct SkillsQuery { #[serde(rename = "clawId")] claw_id: Option, } /// GET /api/skills — the Skill Library (§8.1); with ?clawId= the skills /// installed on that agent (§7.5). pub async fn list( State(state): State, Authed(user): Authed, Query(query): Query, ) -> Result>, ApiError> { match query.claw_id { Some(claw_id) => { let agent = workspace_agent(&state, &user, claw_id).await?; Ok(Json( cm_db::repo::skills::installed(&state.pool, agent.id).await?, )) } None => Ok(Json( cm_db::repo::skills::library(&state.pool, user.workspace_id).await?, )), } } #[derive(Deserialize)] pub struct InstallRequest { #[serde(rename = "clawId")] claw_id: AgentId, #[serde(rename = "skillId")] skill_id: Uuid, } /// POST /api/skills/install pub async fn install( State(state): State, Authed(user): Authed, Json(body): Json, ) -> Result { let agent = workspace_agent(&state, &user, body.claw_id).await?; cm_db::repo::skills::install(&state.pool, agent.id, body.skill_id, user.user_id).await?; Ok(StatusCode::NO_CONTENT) } /// POST /api/skills/uninstall pub async fn uninstall( State(state): State, Authed(user): Authed, Json(body): Json, ) -> Result { let agent = workspace_agent(&state, &user, body.claw_id).await?; cm_db::repo::skills::uninstall(&state.pool, agent.id, body.skill_id).await?; Ok(StatusCode::NO_CONTENT) }