Add a 'platforms' field to SKILL.md frontmatter that restricts skills to specific operating systems. Skills with platforms: [macos] only appear in the system prompt, skills_list(), and slash commands on macOS. Skills without the field load everywhere (backward compatible). Implementation: - skill_matches_platform() in tools/skills_tool.py — core filter - Wired into all 3 discovery paths: prompt_builder.py, skills_tool.py, skill_commands.py - 28 new tests across 3 test files New bundled Apple/macOS skills (all platforms: [macos]): - imessage — Send/receive iMessages via imsg CLI - apple-reminders — Manage Reminders via remindctl CLI - apple-notes — Manage Notes via memo CLI - findmy — Track devices/AirTags via AppleScript + screen capture Docs updated: CONTRIBUTING.md, AGENTS.md, creating-skills.md, skills.md (user guide)
88 lines
3.3 KiB
Python
88 lines
3.3 KiB
Python
"""Tests for agent/skill_commands.py — skill slash command scanning and platform filtering."""
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from agent.skill_commands import scan_skill_commands, build_skill_invocation_message
|
|
|
|
|
|
def _make_skill(skills_dir, name, frontmatter_extra="", body="Do the thing.", category=None):
|
|
"""Helper to create a minimal skill directory with SKILL.md."""
|
|
if category:
|
|
skill_dir = skills_dir / category / name
|
|
else:
|
|
skill_dir = skills_dir / name
|
|
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
content = f"""\
|
|
---
|
|
name: {name}
|
|
description: Description for {name}.
|
|
{frontmatter_extra}---
|
|
|
|
# {name}
|
|
|
|
{body}
|
|
"""
|
|
(skill_dir / "SKILL.md").write_text(content)
|
|
return skill_dir
|
|
|
|
|
|
class TestScanSkillCommands:
|
|
def test_finds_skills(self, tmp_path):
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path):
|
|
_make_skill(tmp_path, "my-skill")
|
|
result = scan_skill_commands()
|
|
assert "/my-skill" in result
|
|
assert result["/my-skill"]["name"] == "my-skill"
|
|
|
|
def test_empty_dir(self, tmp_path):
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path):
|
|
result = scan_skill_commands()
|
|
assert result == {}
|
|
|
|
def test_excludes_incompatible_platform(self, tmp_path):
|
|
"""macOS-only skills should not register slash commands on Linux."""
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path), \
|
|
patch("tools.skills_tool.sys") as mock_sys:
|
|
mock_sys.platform = "linux"
|
|
_make_skill(tmp_path, "imessage", frontmatter_extra="platforms: [macos]\n")
|
|
_make_skill(tmp_path, "web-search")
|
|
result = scan_skill_commands()
|
|
assert "/web-search" in result
|
|
assert "/imessage" not in result
|
|
|
|
def test_includes_matching_platform(self, tmp_path):
|
|
"""macOS-only skills should register slash commands on macOS."""
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path), \
|
|
patch("tools.skills_tool.sys") as mock_sys:
|
|
mock_sys.platform = "darwin"
|
|
_make_skill(tmp_path, "imessage", frontmatter_extra="platforms: [macos]\n")
|
|
result = scan_skill_commands()
|
|
assert "/imessage" in result
|
|
|
|
def test_universal_skill_on_any_platform(self, tmp_path):
|
|
"""Skills without platforms field should register on any platform."""
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path), \
|
|
patch("tools.skills_tool.sys") as mock_sys:
|
|
mock_sys.platform = "win32"
|
|
_make_skill(tmp_path, "generic-tool")
|
|
result = scan_skill_commands()
|
|
assert "/generic-tool" in result
|
|
|
|
|
|
class TestBuildSkillInvocationMessage:
|
|
def test_builds_message(self, tmp_path):
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path):
|
|
_make_skill(tmp_path, "test-skill")
|
|
scan_skill_commands()
|
|
msg = build_skill_invocation_message("/test-skill", "do stuff")
|
|
assert msg is not None
|
|
assert "test-skill" in msg
|
|
assert "do stuff" in msg
|
|
|
|
def test_returns_none_for_unknown(self, tmp_path):
|
|
with patch("tools.skills_tool.SKILLS_DIR", tmp_path):
|
|
scan_skill_commands()
|
|
msg = build_skill_invocation_message("/nonexistent")
|
|
assert msg is None
|