feat(gateway): GET /api/skills

This commit is contained in:
xiaoxixi 2026-07-26 21:24:14 +08:00
parent 119df57b49
commit 5e2771c538
4 changed files with 67 additions and 0 deletions

View File

@ -844,6 +844,23 @@ pub async fn get_tools(State(state): State<Arc<GatewayState>>) -> Result<Json<Va
Ok(Json(json!({ "tools": tools })))
}
pub async fn get_skills(State(state): State<Arc<GatewayState>>) -> Result<Json<Value>, ApiError> {
let loader = state.session_manager.skills_loader();
let skills: Vec<Value> = loader
.get_loaded_skills()
.iter()
.map(|s| {
json!({
"name": s.name,
"description": s.description,
"always": s.always,
"source": loader.source_of(s.path.as_deref()),
})
})
.collect();
Ok(Json(json!({ "skills": skills })))
}
pub async fn get_tasks(
State(state): State<Arc<GatewayState>>,
Query(query): Query<LimitQuery>,

View File

@ -595,6 +595,7 @@ fn build_router(state: Arc<GatewayState>) -> Router {
.route("/api/tasks", routing::get(http::get_tasks))
.route("/api/status", routing::get(http::get_status))
.route("/api/tools", routing::get(http::get_tools))
.route("/api/skills", routing::get(http::get_skills))
.route("/api/jobs", routing::get(http::get_jobs))
.route("/api/jobs/{id}/runs", routing::get(http::get_job_runs))
.route("/api/memories", routing::get(http::get_memories))

View File

@ -1708,6 +1708,10 @@ impl SessionManager {
self.tools.clone()
}
pub fn skills_loader(&self) -> Arc<SkillsLoader> {
self.skills_loader.clone()
}
pub fn work_manager(&self) -> Arc<crate::work::WorkManager> {
self.work_manager.clone()
}

View File

@ -248,6 +248,24 @@ impl SkillsLoader {
max_mtime
}
pub fn source_of(&self, path: Option<&Path>) -> &'static str {
let Some(path) = path else {
return "unknown";
};
if let Some(ws) = &self.workspace_skills_dir
&& path.starts_with(ws)
{
return "workspace";
}
if path.starts_with(&self.picobot_skills_dir) {
return "picobot";
}
if path.starts_with(&self.agent_skills_dir) {
return "agent";
}
"other"
}
/// Get a copy of loaded skills (checks for changes first)
pub fn get_loaded_skills(&self) -> Vec<Skill> {
self.reload_if_changed();
@ -586,4 +604,31 @@ This is the content.
let beta_pos = prompt.find("**beta**").unwrap();
assert!(alpha_pos < beta_pos);
}
#[test]
fn test_source_of() {
let mut loader = SkillsLoader::new_for_testing(
PathBuf::from("/home/user/.picobot/skills"),
PathBuf::from("/home/user/.agents/skills"),
);
loader.set_workspace_skills_dir(PathBuf::from("/workspace"));
assert_eq!(loader.source_of(None), "unknown");
assert_eq!(
loader.source_of(Some(Path::new("/workspace/skills/my-skill"))),
"workspace"
);
assert_eq!(
loader.source_of(Some(Path::new("/home/user/.picobot/skills/foo"))),
"picobot"
);
assert_eq!(
loader.source_of(Some(Path::new("/home/user/.agents/skills/bar"))),
"agent"
);
assert_eq!(
loader.source_of(Some(Path::new("/opt/other/skill"))),
"other"
);
}
}