diff --git a/server/tasks/skills.py b/server/tasks/skills.py index b50c227e..4413e2af 100644 --- a/server/tasks/skills.py +++ b/server/tasks/skills.py @@ -35,8 +35,19 @@ def _parse_skill_metadata(content: str, fallback_name: str) -> Dict[str, str]: metadata["name"] = metadata.get("name") or fallback_name return metadata +def _workspace_project_root(workspace) -> Path: + """工作区根目录(已 resolve 的绝对路径)。""" + return Path(workspace.project_path).expanduser().resolve() + def _workspace_skills_dir(workspace) -> Path: - return (Path(workspace.project_path).expanduser().resolve() / WORKSPACE_SKILLS_DIRNAME).resolve() + return _workspace_project_root(workspace) / WORKSPACE_SKILLS_DIRNAME + +def _rel_skill_path(skill_file: Path, workspace) -> str: + """把 skill 文件的绝对路径转成相对工作区根的路径(如 `.astrion/skills/xxx/SKILL.md`)。 + + 前端 / 菜单插入、对话历史、任务快照均使用该相对路径,避免暴露服务器绝对路径。 + """ + return str(skill_file.relative_to(_workspace_project_root(workspace)).as_posix()) def _resolve_workspace_skill_path(workspace, raw_path: str) -> Path: try: @@ -83,7 +94,7 @@ def _list_workspace_skills(workspace) -> List[Dict[str, str]]: result.append({ "name": metadata.get("name") or skill_file.parent.name, "description": metadata.get("description") or "", - "path": str(skill_file.resolve()), + "path": _rel_skill_path(skill_file, workspace), }) return result @@ -102,7 +113,7 @@ def _build_skill_context_messages(workspace, raw_refs: Any) -> List[Dict[str, st if not raw_path: continue skill_file = _resolve_workspace_skill_path(workspace, raw_path) - path_key = str(skill_file) + path_key = _rel_skill_path(skill_file, workspace) if path_key in seen_paths: debug_log(f"[SkillsAPI] build_skill_context duplicate path skipped: {path_key}") continue