feat(skills): add metadata and branch fields to skill packs and marketplace skills

This commit is contained in:
Abhimanyu Saharan
2026-02-14 12:26:45 +05:30
parent 5b9e81aa6d
commit 40dcf50f4b
17 changed files with 1049 additions and 51 deletions

View File

@@ -485,6 +485,61 @@ async def test_create_skill_pack_rejects_localhost_source_url() -> None:
await engine.dispose()
@pytest.mark.asyncio
async def test_create_skill_pack_is_unique_by_normalized_source_url() -> None:
engine = await _make_engine()
session_maker = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
try:
async with session_maker() as session:
organization, _gateway = await _seed_base(session)
await session.commit()
app = _build_test_app(session_maker, organization=organization)
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://testserver",
) as client:
first = await client.post(
"/api/v1/skills/packs",
json={"source_url": "https://github.com/org/repo"},
)
spaced = await client.post(
"/api/v1/skills/packs",
json={"source_url": " https://github.com/org/repo.git "},
)
second = await client.post(
"/api/v1/skills/packs",
json={"source_url": "https://github.com/org/repo/"},
)
third = await client.post(
"/api/v1/skills/packs",
json={"source_url": "https://github.com/org/repo.git"},
)
packs = await client.get("/api/v1/skills/packs")
assert first.status_code == 200
assert spaced.status_code == 200
assert second.status_code == 200
assert third.status_code == 200
assert spaced.json()["id"] == first.json()["id"]
assert spaced.json()["source_url"] == first.json()["source_url"]
assert second.json()["id"] == first.json()["id"]
assert second.json()["source_url"] == first.json()["source_url"]
assert third.json()["id"] == first.json()["id"]
assert third.json()["source_url"] == first.json()["source_url"]
assert packs.status_code == 200
pack_items = packs.json()
assert len(pack_items) == 1
assert pack_items[0]["source_url"] == "https://github.com/org/repo"
finally:
await engine.dispose()
@pytest.mark.asyncio
async def test_list_skill_packs_includes_skill_count() -> None:
engine = await _make_engine()
@@ -548,6 +603,109 @@ async def test_list_skill_packs_includes_skill_count() -> None:
await engine.dispose()
@pytest.mark.asyncio
async def test_update_skill_pack_rejects_duplicate_normalized_source_url() -> None:
engine = await _make_engine()
session_maker = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
try:
async with session_maker() as session:
organization, _gateway = await _seed_base(session)
pack_a = SkillPack(
organization_id=organization.id,
source_url="https://github.com/org/repo",
name="Pack A",
)
pack_b = SkillPack(
organization_id=organization.id,
source_url="https://github.com/org/other-repo",
name="Pack B",
)
session.add(pack_a)
session.add(pack_b)
await session.commit()
await session.refresh(pack_a)
await session.refresh(pack_b)
app = _build_test_app(session_maker, organization=organization)
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://testserver",
) as client:
response = await client.patch(
f"/api/v1/skills/packs/{pack_b.id}",
json={"source_url": "https://github.com/org/repo/"},
)
assert response.status_code == 409
assert "already exists" in response.json()["detail"].lower()
async with session_maker() as session:
pack_rows = (
await session.exec(
select(SkillPack)
.where(col(SkillPack.organization_id) == organization.id)
.order_by(col(SkillPack.created_at).asc())
)
).all()
assert len(pack_rows) == 2
assert {str(pack.source_url) for pack in pack_rows} == {
"https://github.com/org/repo",
"https://github.com/org/other-repo",
}
finally:
await engine.dispose()
@pytest.mark.asyncio
async def test_update_skill_pack_normalizes_source_url_on_update() -> None:
engine = await _make_engine()
session_maker = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
try:
async with session_maker() as session:
organization, _gateway = await _seed_base(session)
pack = SkillPack(
organization_id=organization.id,
source_url="https://github.com/org/old",
name="Initial",
)
session.add(pack)
await session.commit()
await session.refresh(pack)
app = _build_test_app(session_maker, organization=organization)
async with AsyncClient(
transport=ASGITransport(app=app),
base_url="http://testserver",
) as client:
response = await client.patch(
f"/api/v1/skills/packs/{pack.id}",
json={"source_url": " https://github.com/org/new.git/ "},
)
assert response.status_code == 200
assert response.json()["source_url"] == "https://github.com/org/new"
async with session_maker() as session:
updated = (
await session.exec(
select(SkillPack).where(col(SkillPack.id) == pack.id),
)
).one()
assert str(updated.source_url) == "https://github.com/org/new"
finally:
await engine.dispose()
def test_collect_pack_skills_from_repo_uses_root_index_when_present(tmp_path: Path) -> None:
repo_dir = tmp_path / "repo"
repo_dir.mkdir()
@@ -672,3 +830,39 @@ def test_collect_pack_skills_from_repo_supports_top_level_skill_folders(
"https://github.com/BrianRWagner/ai-marketing-skills/tree/main/homepage-audit"
in by_source
)
def test_collect_pack_skills_from_repo_streams_large_index(tmp_path: Path) -> None:
repo_dir = tmp_path / "repo"
repo_dir.mkdir()
(repo_dir / "SKILL.md").write_text("# Fallback Skill\n", encoding="utf-8")
huge_description = "x" * (300 * 1024)
(repo_dir / "skills_index.json").write_text(
json.dumps(
{
"skills": [
{
"id": "oversized",
"name": "Huge Index Skill",
"description": huge_description,
"path": "skills/ignored",
},
],
}
),
encoding="utf-8",
)
skills = _collect_pack_skills_from_repo(
repo_dir=repo_dir,
source_url="https://github.com/example/oversized-pack",
branch="main",
)
assert len(skills) == 1
assert (
skills[0].source_url
== "https://github.com/example/oversized-pack/tree/main/skills/ignored"
)
assert skills[0].name == "Huge Index Skill"