Allow all spec-compliant names

This commit is contained in:
Yiorgis Gozadinos 2026-03-24 17:27:56 +02:00
parent 7d50edea86
commit 705ce80b4f
No known key found for this signature in database
3 changed files with 29 additions and 47 deletions

View file

@ -36,9 +36,6 @@ def _get_env() -> Environment:
def validate_metadata(name: str, description: str) -> None:
if not name.isidentifier():
raise ValueError(f"{name!r} is not a valid Python identifier")
from haiku.skills import SkillMetadata
SkillMetadata(name=name, description=description)
@ -80,16 +77,18 @@ def render_templates(
if preamble is None:
preamble = DEFAULT_PREAMBLE
pkg_name = name.replace("-", "_")
env = _get_env()
context = {
"name": name,
"pkg_name": pkg_name,
"description": description,
"tool_names": tool_names,
"preamble": preamble,
}
result_dir = output_dir / f"{name}-skill"
pkg_dir = result_dir / f"{name}_skill"
pkg_dir = result_dir / f"{pkg_name}_skill"
assets_dir = pkg_dir / "assets"
assets_dir.mkdir(parents=True)
@ -130,7 +129,8 @@ def generate_skill(
preamble=preamble,
)
assets_dir = result / f"{name}_skill" / "assets"
pkg_name = name.replace("-", "_")
assets_dir = result / f"{pkg_name}_skill" / "assets"
shutil.copytree(db_path, assets_dir / f"{name}.lancedb")
if config_path is not None:

View file

@ -13,4 +13,4 @@ dependencies = [
]
[project.entry-points."haiku.skills"]
{{ name }} = "{{ name }}_skill:create_skill"
{{ name }} = "{{ pkg_name }}_skill:create_skill"

View file

@ -25,46 +25,11 @@ class TestAvailableTools:
class TestValidateMetadata:
def test_valid(self):
validate_metadata("recipes", "A skill.")
validate_metadata("my-recipes", "A skill.")
def test_valid_with_numbers(self):
validate_metadata("recipes123", "A skill.")
def test_rejects_underscores(self):
with pytest.raises(ValueError, match="name"):
validate_metadata("my_recipes", "A skill.")
def test_rejects_hyphens(self):
with pytest.raises(ValueError, match="identifier"):
validate_metadata("my-recipes", "A skill.")
def test_rejects_uppercase(self):
with pytest.raises(ValueError, match="lowercase"):
validate_metadata("Recipes", "A skill.")
def test_rejects_empty_name(self):
with pytest.raises(ValueError, match="identifier"):
validate_metadata("", "A skill.")
def test_rejects_not_identifier(self):
with pytest.raises(ValueError, match="identifier"):
validate_metadata("123abc", "A skill.")
def test_rejects_spaces_in_name(self):
with pytest.raises(ValueError, match="identifier"):
validate_metadata("my recipes", "A skill.")
def test_rejects_special_chars(self):
with pytest.raises(ValueError, match="identifier"):
validate_metadata("my@recipes", "A skill.")
def test_rejects_empty_description(self):
with pytest.raises(ValueError, match="description"):
validate_metadata("recipes", "")
def test_rejects_too_long_description(self):
with pytest.raises(ValueError, match="description"):
validate_metadata("recipes", "x" * 1025)
def test_rejects_invalid_name(self):
with pytest.raises(ValueError):
validate_metadata("Bad_Name!", "A skill.")
class TestValidateTools:
@ -135,6 +100,23 @@ class TestRenderTemplates:
assert (pkg / "SKILL.md").is_file()
assert (pkg / "assets").is_dir()
def test_dashed_name_uses_underscores_for_python(self, tmp_path):
result = render_templates(
output_dir=tmp_path,
name="my-recipes",
description="A recipe skill.",
tool_names=["search", "ask"],
)
assert result == tmp_path / "my-recipes-skill"
pkg = result / "my_recipes_skill"
assert (pkg / "__init__.py").is_file()
init = (pkg / "__init__.py").read_text()
assert '"my-recipes.lancedb"' in init
assert 'state_namespace="my-recipes"' in init
toml = (result / "pyproject.toml").read_text()
assert 'name = "my-recipes-skill"' in toml
assert 'my-recipes = "my_recipes_skill:create_skill"' in toml
def test_tool_names_list_matches_selection(self, tmp_path):
render_templates(
output_dir=tmp_path,
@ -313,11 +295,11 @@ class TestGenerateSkill:
def test_rejects_invalid_name(self, tmp_path):
db_path = _make_fake_lancedb(tmp_path / "test.lancedb")
with pytest.raises(ValueError, match="identifier"):
with pytest.raises(ValueError):
generate_skill(
db_path=db_path,
output_dir=tmp_path,
name="Bad-Name",
name="Bad_Name!",
description="A skill.",
tool_names=["search"],
)