Metadata-Version: 2.4
Name: polyskills
Version: 1.0.0
Summary: A Python CLI Tool for Skills Management
Author-email: ZenithClown <dpramanik.official@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Debmalya Pramanik
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://github.com/PyUtility/polyskills
Project-URL: Repository, https://github.com/PyUtility/polyskills
Project-URL: Issues, https://github.com/PyUtility/polyskills/issues
Keywords: skills,anthropic,claude,codex,antigravity
Classifier: Development Status :: 2 - Pre-Alpha
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: requests>=2.31
Requires-Dist: packaging>=23.0
Dynamic: license-file

<div align = "justify">

# Polyskills — Portable LLM Skills Manager

`polyskills` is a Python CLI and library that installs, updates and
lists portable **LLM skills** (Anthropic's [`SKILL.md`][skill-md]
files) hosted on any git-versioned remote repository. One source of
truth, many runtimes — Claude Code, Codex, Cursor, plus the
Anthropic / OpenAI Python SDKs.

Skills are addressed by a `skillName@vX.Y.Z` git tag convention, so a
single repository can host many independently versioned skills and
consumers can pin to a specific version exactly like they would pin a
`pip` package.

> **Status: pre-alpha.** GitHub is the only supported source. Three
> target adapters are wired up — `ClaudeCode` exposes Claude Code's
> native `~/.claude/skills` and `<root>/.claude/skills` install
> paths, `Codex` exposes OpenAI Codex CLI's native `~/.codex/prompts`
> and `<root>/.codex/prompts` custom prompt paths, and `Cursor`
> exposes Cursor's native `~/.cursor/rules` and `<root>/.cursor/rules`
> rule-bundle paths. `SKILL.md` → `.mdc` / `.cursorrules` format
> conversion for Cursor is still pending, and the CLI does not yet
> auto-select adapters (a `--target` flag is on the roadmap), so for
> now it writes raw `SKILL.md` bundles into the user-specified
> `--directory`.

</div>

## Getting started

### Install

```shell
pip install polyskills
```

`polyskills` requires **Python 3.12+** and pulls in
[`requests`](https://pypi.org/project/requests/) and
[`packaging`](https://pypi.org/project/packaging/) at install time.
Installing the package registers the `polyskills` console command on
your `PATH`. The equivalent ``python -m polyskills ...`` invocation
also works for editable installs and environments where the entry
point script is not on the `PATH`.

### List the skills published on a remote

```shell
polyskills "https://github.com/<owner>/<repository>" --get-skills
```

Sample output:

```
Available skills at https://github.com/<owner>/<repository>:

  codeReview       latest: v1.2.0
  releaseNotes     latest: v0.4.1
  triageInbox      latest: v0.1.0
```

Flag reference:

| Flag | Effect |
| --- | --- |
| `--all-versions` | List every published version of every skill, not only the latest. |
| `--json` | Emit a machine-readable payload (`{skill: {latest, tags}}`). |
| `--timeout SECONDS` | HTTP timeout for the tags REST call. Default: `30`. |

### Install (or update) a single skill

```shell
polyskills "https://github.com/<owner>/<repository>" \
    --update codeReview \
    --directory .skills/codeReview
```

This downloads the repository archive at the `codeReview@vX.Y.Z` git
tag of the resolved version, extracts **only** the
`skills/codeReview/` subtree, and writes the contents into
`.skills/codeReview/`. Any prior contents at the target directory are
replaced. Missing parent directories are created.

Pin to an explicit version:

```shell
polyskills "https://github.com/<owner>/<repository>" \
    --update codeReview --version v1.1.0 \
    --directory .skills/codeReview
```

The leading `v` is optional — `--version 1.1.0` works too.

### Authentication and rate limits

The GitHub REST API allows **60 unauthenticated requests per hour per
IP** — enough for casual use, easily exhausted in CI. If the
`GITHUB_TOKEN` environment variable is set (a classic or fine-grained
personal access token with `public_repo` read scope is sufficient for
public repos), `polyskills` automatically uses it as a bearer token
and the limit rises to **5000 req/hr**:

```shell
export GITHUB_TOKEN="ghp_..."
polyskills "https://github.com/<owner>/<repository>" --get-skills
```

No CLI flag is exposed for the token to keep secrets out of shell
history. The token is also passed to the archive download endpoint;
GitHub redirects the archive to S3 and `requests` correctly strips
the `Authorization` header on cross-host redirect.

### Exit codes

| Code | Meaning |
| --- | --- |
| `0` | Success. |
| `2` | User error (bad URL, unknown skill, version not found, missing `--directory`). |
| `3` | The requested tag exists but the `skills/<name>/` subtree is empty or absent. |
| `4` | HTTP error from the REST or archive endpoint. |
| `5` | Network error (DNS, TLS, connection reset, timeout). |

## Authoring a skills repository

A skills repository is **any git repository** whose top-level layout
is:

```
your-skills-repo/
├── skills/
│   ├── codeReview/
│   │   ├── SKILL.md
│   │   ├── prompts/
│   │   │   └── strict.md
│   │   └── ...
│   ├── releaseNotes/
│   │   ├── SKILL.md
│   │   └── ...
│   └── ...
└── README.md
```

Each top-level directory under `skills/` is one independently
versioned skill. The contents of `skills/<skillName>/` are what
`polyskills --update <skillName>` writes verbatim into the user's
`--directory`.

### Versioning with `skillName@vX.Y.Z` tags

`polyskills` discovers skills and versions by scanning the
repository's git tags for the pattern:

```
<skillName>@v<semver>
```

Examples:

```
codeReview@v0.1.0
codeReview@v1.0.0
codeReview@v1.2.0
releaseNotes@v0.4.1
```

Notes:

- `<skillName>` must match `[A-Za-z0-9_-]+`.
- `<semver>` is parsed with [PEP 440][pep440] semantics by the
  `packaging` library — `1.2.0`, `1.2.0rc1`, `1.2.0.post1` all work,
  though plain `MAJOR.MINOR.PATCH` is recommended.
- Tags that do not match the pattern (`v1.0`, `release-2024-01`, …)
  are silently ignored when listing.
- "Latest" is whichever tag has the highest semver — **not** the most
  recently created tag.

### Publishing a new version

```shell
# 1. Make and review your changes in skills/codeReview/
git add skills/codeReview
git commit -m "codeReview: tighten the review checklist"

# 2. Tag the new version
git tag codeReview@v1.3.0

# 3. Push the commit and the tag to the remote
git push origin main
git push origin codeReview@v1.3.0
```

Consumers running `polyskills <repo> --get-skills` will immediately
see `codeReview  latest: v1.3.0`. There is no registry, no publish
step beyond `git push`.

### Authoring `SKILL.md`

`polyskills` does not parse or validate `SKILL.md` content — it
simply ships the files as authored. For the schema and best practices
refer to Anthropic's [skills documentation][skill-md]. A minimal
skeleton:

```markdown
---
name: codeReview
description: Performs a focused code review on a diff.
---

# Instructions

You are an expert reviewer ...
```

## Using as a library

The CLI is a thin wrapper around three classes. They are stable and
can be used directly:

```python
from polyskills import SkillsManager, SkillInstaller, ValidSources

skills = SkillsManager(
    remote = "https://github.com/<owner>/<repository>",
    source = ValidSources.GITHUB,
    timeout = 30,
)

# Discover what is available.
catalog = skills.getSkills()
for name, entry in catalog.items():
    print(name, entry["latest"])

# Install one skill into the local project. ``installSkill`` returns
# ``(installed_directory, resolved_version)`` so the caller can log
# which version actually landed without paying for a second tag-list
# round-trip.
installer = SkillInstaller(skills, timeout = 60)
target, resolved = installer.installSkill(
    skillName = "codeReview",
    directory = ".skills/codeReview",
    # version = "1.2.0",  # omit for latest
)
print(f"Installed codeReview v{resolved} to {target}")
```

The deep-import style still works (`from polyskills.manager.skills
import SkillsManager`, etc.) for code that pins to it.

`SkillsManager` paginates the GitHub tags REST API; `SkillInstaller`
performs a single streaming tarball download. Reuse one
`SkillsManager` across multiple `installSkill` calls to avoid
re-paginating the tag listing for each install.

### Resolving runtime-native install paths

Each supported LLM runtime ships a concrete `TargetAdapters` subclass
that returns the canonical directory the runtime auto-discovers
skills from. For Claude Code:

```python
from pathlib import Path
from polyskills.targets.claude import ClaudeCode

claude = ClaudeCode()
claude.globalDirectory()              # ~/.claude/skills
claude.localDirectory(Path.cwd())     # <cwd>/.claude/skills
```

For OpenAI Codex CLI, which has no native `skills/` directory but
does natively discover custom slash-command prompts under `.codex/`,
the `Codex` adapter resolves Codex's `prompts/` convention so skills
land where the runtime actually looks for them:

```python
from pathlib import Path
from polyskills.targets.codex import Codex

codex = Codex()
codex.globalDirectory()               # ~/.codex/prompts
codex.localDirectory(Path.cwd())      # <cwd>/.codex/prompts
```

For Cursor, the `Cursor` adapter resolves the editor's `.cursor/rules`
directory — the canonical home for `.mdc` rule bundles that Cursor
auto-loads when the editor is opened on a project. The user-wide
mirror keeps the install layout symmetrical even though Cursor itself
does not auto-discover rules outside the project tree at this time:

```python
from pathlib import Path
from polyskills.targets.cursor import Cursor

cursor = Cursor()
cursor.globalDirectory()              # ~/.cursor/rules
cursor.localDirectory(Path.cwd())     # <cwd>/.cursor/rules
```

To look up an adapter by name (useful for config-driven workflows or
the upcoming `--target` CLI flag), go through the registry. The
registry maps the runtime's upper-case identifier to the adapter
class itself; instantiating the class yields a parameterless object
whose `globalDirectory()` and `localDirectory(root)` methods resolve
the runtime-native install paths:

```python
from polyskills.targets.registry import LLM_APPLICATIONS

adapter = LLM_APPLICATIONS["CLAUDE"]()
target = adapter.localDirectory(Path.cwd()) / "codeReview"

installer.installSkill("codeReview", target)
```

Currently registered targets: **`CLAUDE`**, **`CODEX`**, **`CURSOR`**.

## Roadmap

Implemented:

- GitHub-hosted skills, tag discovery, version resolution.
- Tarball-at-tag streaming install of `skills/<name>/` subtree.
- `GITHUB_TOKEN` auth, CLI exit codes, JSON output.
- `ClaudeCode` target adapter (native Claude Code install-path
  resolution).
- `Codex` target adapter (native OpenAI Codex CLI install-path
  resolution via `~/.codex/prompts`).
- `Cursor` target adapter (native Cursor install-path resolution via
  `~/.cursor/rules`).
- Name-keyed adapter registry exposing all three adapters
  (`LLM_APPLICATIONS["CLAUDE"]`, `LLM_APPLICATIONS["CODEX"]`,
  `LLM_APPLICATIONS["CURSOR"]`).

Planned (contributions welcome):

- CLI `--target` flag that uses the adapter registry to derive
  `--directory` automatically (e.g. `--target claude` writes to
  `./.claude/skills/<skillName>/` without an explicit path).
- `SKILL.md` → `.mdc` / `.cursorrules` rule-format conversion so the
  `Cursor` adapter ships skills in the editor's native rule format.
- Additional sources beyond GitHub (GitLab, Bitbucket, generic Git).
- A lockfile for bulk `polyskills update` across an entire project.

## Contributing

Issues and pull requests are tracked on
[GitHub](https://github.com/PyUtility/polyskills/issues). The project
follows [PEP 8][pep8] (88-char lines, flake8 enforced) with
Sphinx-style docstrings; see existing modules for the conventions.

## License

MIT. See [LICENSE](LICENSE).

[skill-md]: https://docs.claude.com/en/docs/claude-code/skills
[pep440]: https://peps.python.org/pep-0440/
[pep8]: https://peps.python.org/pep-0008/
