agent-Specialization/test/test_skills_manager.py
JOJO 398ee6e760 feat(memory): refactor proactive memory and conversation recall
Refactor conversation persistence into workspace-scoped storage with host workspace legacy migration, current workspace filtering, and active host workspace state protection.

Add proactive recent-conversation prompt injection with personal-space controls, configurable item count, empty-conversation filtering, and frozen per-conversation prompt metadata to keep request caches stable.

Introduce conversation_search and conversation_review tools for current-workspace history: multi-keyword search, list mode, current conversation exclusion, message/tool counts, formatted model output, read/save review modes, long-review fallback, and .agents/review storage.

Move internal workspace artifacts under .agents, including uploads, skills, compact results, and reviews; update prompt file-tree visibility, compression output, upload paths, and hidden versioning exclusions.

Add create_skill workflow and a Skills tool category, move read_skill into Skills, validate/归档 skill folders, support host global skills and Docker/API private skills, and sync private skills into .agents/skills and skill prompts.

Update frontend tool icons, statuses, and expanded renderers for conversation recall and skills workflows; add workspace conversation and skills-manager tests.
2026-05-27 03:09:42 +08:00

98 lines
3.9 KiB
Python

from __future__ import annotations
import tempfile
import unittest
from pathlib import Path
from modules.skills_manager import (
archive_skill_directory,
get_skills_catalog,
infer_private_skills_dir,
sync_workspace_skills,
validate_skill_directory,
)
class SkillsManagerTest(unittest.TestCase):
def test_validate_skill_directory_requires_skill_md_and_frontmatter_fields(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
skill_dir = root / "sample-skill"
skill_dir.mkdir()
missing_file = validate_skill_directory(skill_dir)
self.assertFalse(missing_file.get("success"))
self.assertEqual(missing_file.get("error"), "缺少 SKILL.md")
(skill_dir / "SKILL.md").write_text("---\nname: Sample\n---\n", encoding="utf-8")
missing_description = validate_skill_directory(skill_dir)
self.assertFalse(missing_description.get("success"))
self.assertEqual(missing_description.get("error"), "缺少 description:")
(skill_dir / "SKILL.md").write_text(
"---\nname: Sample\ndescription: Demo skill\n---\n",
encoding="utf-8",
)
valid = validate_skill_directory(skill_dir)
self.assertTrue(valid.get("success"))
self.assertEqual(valid.get("skill_name"), "sample-skill")
def test_archive_skill_directory_moves_without_overwriting(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
skill_dir = root / "sample-skill"
target_root = root / "agentskills"
skill_dir.mkdir()
(skill_dir / "SKILL.md").write_text(
"---\nname: Sample\ndescription: Demo skill\n---\n",
encoding="utf-8",
)
archived = archive_skill_directory(skill_dir, target_root)
self.assertTrue(archived.get("success"))
self.assertFalse(skill_dir.exists())
self.assertTrue((target_root / "sample-skill" / "SKILL.md").exists())
duplicate_src = root / "sample-skill"
duplicate_src.mkdir()
(duplicate_src / "SKILL.md").write_text(
"---\nname: Sample\ndescription: Demo skill\n---\n",
encoding="utf-8",
)
duplicate = archive_skill_directory(duplicate_src, target_root)
self.assertFalse(duplicate.get("success"))
self.assertEqual(duplicate.get("error"), "目标 skill 已存在")
def test_private_skills_are_cataloged_and_synced(self):
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
global_root = root / "global"
private_root = root / "users" / "jojo" / "agentskills"
data_dir = root / "users" / "jojo" / "data"
project = root / "project"
private_skill = private_root / "private-skill"
private_skill.mkdir(parents=True)
data_dir.mkdir(parents=True)
project.mkdir(parents=True)
(private_skill / "SKILL.md").write_text(
"---\nname: Private Skill\ndescription: Private demo\n---\n",
encoding="utf-8",
)
self.assertEqual(infer_private_skills_dir(data_dir), private_root.resolve())
catalog = get_skills_catalog(base_dir=str(global_root), private_dir=private_root)
self.assertEqual([item["id"] for item in catalog], ["private-skill"])
synced = sync_workspace_skills(
project,
enabled_skills=["private-skill"],
base_dir=str(global_root),
private_dir=private_root,
)
self.assertTrue(synced.get("success"))
self.assertTrue((project / ".agents" / "skills" / "private-skill" / "SKILL.md").exists())
if __name__ == "__main__":
unittest.main()