feat: restore zhihu browser skills

Reconnect the recovered Zhihu skill flows to the live browser runtime and resolve their resources relative to the executable so they work outside the repo root.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
木炎
2026-03-27 14:29:38 +08:00
parent b87968632a
commit 6aad2ce48e
32 changed files with 7607 additions and 146 deletions

85
src/skill/mod.rs Normal file
View File

@@ -0,0 +1,85 @@
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<T: Transport>(
transport: &T,
browser_tool: &BrowserPipeTool<T>,
instruction: &str,
) -> Result<Option<String>, 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")
);
}
}