Coverage for src / openenv / core / skills.py: 95.83%
56 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 13:36 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-25 13:36 +0000
1"""Shared skill defaults and normalization helpers."""
3from __future__ import annotations
5from collections.abc import Iterable
7from openenv.core.models import SkillConfig
8from openenv.core.utils import slugify_name
11FREERIDE_SKILL_SOURCE = "freeride"
12AGENT_BROWSER_SKILL_SOURCE = "agent-browser-clawdbot"
13SKILL_SOURCE_NAME_OVERRIDES: dict[str, str] = {
14 FREERIDE_SKILL_SOURCE: "free-ride",
15}
16FREERIDE_SKILL_NAME = SKILL_SOURCE_NAME_OVERRIDES[FREERIDE_SKILL_SOURCE]
17MANDATORY_SKILL_SOURCES: tuple[str, ...] = (
18 "deus-context-engine",
19 "self-improving-agent",
20 "skill-security-review",
21 FREERIDE_SKILL_SOURCE,
22 AGENT_BROWSER_SKILL_SOURCE,
23)
26def mandatory_skill_names() -> tuple[str, ...]:
27 """Return the canonical skill directory names for mandatory skill sources."""
28 return tuple(skill_name_for_source(source) for source in MANDATORY_SKILL_SOURCES)
31def catalog_install_dir_name(source: str) -> str:
32 """Return the default directory name created by ClawHub for a catalog source."""
33 source_name = source.rsplit("/", 1)[-1]
34 return slugify_name(source_name)
37def skill_name_for_source(source: str) -> str:
38 """Convert a catalog source into the local skill directory name."""
39 source_name = source.rsplit("/", 1)[-1]
40 return SKILL_SOURCE_NAME_OVERRIDES.get(source_name, catalog_install_dir_name(source))
43def build_catalog_skill(source: str, *, mandatory: bool = False) -> SkillConfig:
44 """Build a manifest skill entry for an externally referenced catalog skill."""
45 descriptor = "Always-installed skill" if mandatory else "Skill"
46 return SkillConfig(
47 name=skill_name_for_source(source),
48 description=f"{descriptor} referenced from catalog source {source}",
49 source=source,
50 )
53def merge_mandatory_skill_sources(skill_sources: Iterable[str]) -> list[str]:
54 """Merge extra skill sources with the mandatory skill set without duplicates."""
55 ordered_sources = list(MANDATORY_SKILL_SOURCES)
56 seen = set(ordered_sources)
57 for source in skill_sources:
58 if source not in seen:
59 seen.add(source)
60 ordered_sources.append(source)
61 return ordered_sources
64def ensure_mandatory_skills(skills: Iterable[SkillConfig]) -> list[SkillConfig]:
65 """Append mandatory skills when they are missing from the manifest."""
66 normalized = list(skills)
67 present_sources = {skill.source for skill in normalized if skill.source is not None}
68 present_names = {skill.name for skill in normalized}
69 for source in MANDATORY_SKILL_SOURCES:
70 skill_name = skill_name_for_source(source)
71 if source in present_sources or skill_name in present_names:
72 continue
73 normalized.append(build_catalog_skill(source, mandatory=True))
74 return normalized
77def catalog_skill_specs(skills: Iterable[SkillConfig]) -> list[tuple[str, str]]:
78 """Return ordered `(name, source)` pairs for skills referenced from an external catalog."""
79 ordered: list[tuple[str, str]] = []
80 seen: set[tuple[str, str]] = set()
81 for skill in skills:
82 if skill.source is None:
83 continue
84 spec = (skill.name, skill.source)
85 if spec in seen: 85 ↛ 86line 85 didn't jump to line 86 because the condition on line 85 was never true
86 continue
87 seen.add(spec)
88 ordered.append(spec)
89 return ordered
92def is_mandatory_skill_reference(reference: str) -> bool:
93 """Return whether a source or local skill name belongs to the mandatory set."""
94 return reference in MANDATORY_SKILL_SOURCES or reference in mandatory_skill_names()
97def is_mandatory_skill(skill: SkillConfig) -> bool:
98 """Return whether a skill entry belongs to the mandatory skill set."""
99 if skill.source is not None and skill.source in MANDATORY_SKILL_SOURCES:
100 return True
101 return skill.name in mandatory_skill_names()