Metadata-Version: 2.3
Name: dais-skills
Version: 0.1.3
Summary: A Python package for scanning and downloading agent skills from specified GitHub repositories.
Author: BHznJNs
Author-email: BHznJNs <bhznjns@outlook.com>
Requires-Dist: httpx>=0.28.1
Requires-Dist: python-frontmatter>=1.1.0
Requires-Dist: binaryornot>=0.6.0
Requires-Python: >=3.14
Description-Content-Type: text/markdown

# dais-skills

A Python package for programmatically discovering, downloading, and parsing [Agent Skills](https://agentskills.io/home.md).

Currently supports GitHub as a skill source. GitLab, arbitrary Git repositories, and custom sources (e.g. S3) are planned.

> This project is in early development. The API may change at any time and backward compatibility is not guaranteed yet.

[中文文档](./README_zh.md)

## Features

- **Scan a repository**: given a GitHub repo URL, list every skill in it along with its path, name, and description
- **Download a skill**: given a repo URL and a skill path, package that skill directory as a zip byte stream
- **Parse a skill archive**: extract `SKILL.md` metadata (with frontmatter) and every resource file (auto-classified as text or binary) from a zip

## Installation

Requires Python 3.14+.

```bash
uv add dais-skills
# or
pip install dais-skills
```

## Quick Start

### Scan a repository for skills

```python
import asyncio
from dais_skills import scan_repo

async def main():
    skills = await scan_repo("https://github.com/<owner>/<repo>")
    for s in skills:
        print(s.path, "-", s.name)
        print(" ", s.description)

asyncio.run(main())
```

`scan_repo` returns `list[ScannedSkill]`, where each item has:

- `path`: the skill directory path inside the repo
- `name`: the `name` field from the `SKILL.md` frontmatter
- `description`: the `description` field from the `SKILL.md` frontmatter

The scanner walks the priority directories defined by the [Agent Skills specification](https://agentskills.io/home.md) (such as `skills/`, `.claude/skills/`, `.github/skills/`, and so on) and skips irrelevant directories like `node_modules`, `.git`, `dist`, `build`, and `__pycache__`.

### Download a single skill as a zip

```python
import asyncio
from dais_skills import download_skill_zip

async def main():
    data: bytes = await download_skill_zip(
        "https://github.com/<owner>/<repo>",
        "skills/my-skill",
    )
    with open("my-skill.zip", "wb") as f:
        f.write(data)

asyncio.run(main())
```

`skill_path` may point at either the skill directory or the `SKILL.md` inside it. Both forms are treated as equivalent.

### Parse a downloaded skill archive

```python
import zipfile
from dais_skills import Skill

with zipfile.ZipFile("my-skill.zip") as zf:
    skill = Skill.from_zip(zf)

print(skill.name, skill.description)
print("license:", skill.license)
print("compatibility:", skill.compatibility)
print("allowed-tools:", skill.allowed_tools)

for res in skill.resources:
    if res.type == "text":
        print("[text]", res.relative, len(res.content))
    else:
        print("[bin] ", res.relative, len(res.content))
```

`Skill.from_zip` will:

1. Locate the skill root inside the zip (the first-level directory containing `SKILL.md`, or the archive root)
2. Parse the frontmatter and body of `SKILL.md`
3. Collect every remaining file as a `SkillResource`, splitting them into `TextResource` and `BinaryResource` via `binaryornot`

## Public API

Exported from the top-level package:

| Name | Description |
| --- | --- |
| `scan_repo(repo_url) -> list[ScannedSkill]` | Scan a repository and return a summary of every skill found |
| `download_skill_zip(repo_url, skill_path) -> bytes` | Download the given skill directory and pack it into a zip |
| `Skill.from_zip(zip_file) -> Skill` | Parse a full skill object from a zip file |
| `ScannedSkill` | Scan-result dataclass |
| `Skill` | Fully parsed skill dataclass |
| `SkillResource` | `TextResource \| BinaryResource` |
| `SkillException` | Base exception for this package |

Submodule-specific exceptions (`ScannerError`, `DownloaderError`, `InvalidSkillArchiveError`, `GitHubError`) can be imported from their respective submodules.

## Development

```bash
uv sync
uv run pytest              # run unit tests
uv run pytest -m smoke     # run end-to-end smoke tests that hit real network services
```

## Roadmap

- [x] Scan skills in a GitHub repository
- [x] Download and package a single skill from GitHub
- [x] Parse a skill archive
- [ ] GitLab repository support
- [ ] Arbitrary remote Git repository support
- [ ] S3 and other custom sources
