pub mod router; pub mod zhihu; pub mod zhihu_hotlist; pub mod zhihu_hotlist_store; pub mod zhihu_navigation; use std::path::PathBuf; use crate::pipe::{BrowserPipeTool, PipeError, Transport}; fn relative_skill_resource_path(resource_name: &str) -> PathBuf { PathBuf::from("resources") .join("skills") .join(resource_name) } pub(crate) fn skill_resource_path_from_executable( executable_path: PathBuf, resource_name: &str, ) -> PathBuf { executable_path .parent() .map(|dir| dir.join("resources").join("skills").join(resource_name)) .unwrap_or_else(|| relative_skill_resource_path(resource_name)) } pub(crate) fn default_skill_resource_path(resource_name: &str) -> PathBuf { std::env::current_exe() .ok() .map(|path| skill_resource_path_from_executable(path, resource_name)) .filter(|path| path.exists()) .unwrap_or_else(|| relative_skill_resource_path(resource_name)) } pub fn try_execute_skill( transport: &T, browser_tool: &BrowserPipeTool, instruction: &str, ) -> Result, PipeError> { match router::route_instruction(instruction) .map_err(|err| PipeError::Protocol(err.to_string()))? { Some(router::RoutedSkill::ZhihuWrite(req)) => { let result = zhihu::execute(transport, browser_tool, req) .map_err(|err| PipeError::Protocol(err.to_string()))?; Ok(Some(result.summary)) } Some(router::RoutedSkill::ZhihuHotlistCollect(req)) => { let result = zhihu_hotlist::execute_collect(transport, browser_tool, req) .map_err(|err| PipeError::Protocol(err.to_string()))?; Ok(Some(result.summary)) } Some(router::RoutedSkill::ZhihuHotlistReport(req)) => { let result = zhihu_hotlist::execute_report(req) .map_err(|err| PipeError::Protocol(err.to_string()))?; Ok(Some(result.summary)) } Some(router::RoutedSkill::ZhihuNavigate(req)) => { let result = zhihu_navigation::execute(transport, browser_tool, req) .map_err(|err| PipeError::Protocol(err.to_string()))?; Ok(Some(result.summary)) } None => Ok(None), } } #[cfg(test)] mod tests { use std::path::PathBuf; use super::skill_resource_path_from_executable; #[test] fn skill_resource_path_uses_executable_directory_instead_of_cwd() { let executable_path = PathBuf::from("/tmp/out/KylinRelease/sgclaw"); let resolved = skill_resource_path_from_executable(executable_path, "zhihu_navigation_pages.json"); assert_eq!( resolved, PathBuf::from("/tmp/out/KylinRelease/resources/skills/zhihu_navigation_pages.json") ); } }