Skip to main content

A Python CLI Tool for Skills Management

Project description

Polyskills — Portable LLM Skills Manager

polyskills is a Python CLI and library that installs, updates and lists portable LLM skills (Anthropic's 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.

Getting started

Install

pip install polyskills

polyskills requires Python 3.12+ and pulls in requests and 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

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

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:

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:

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 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

# 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. A minimal skeleton:

---
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:

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:

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:

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:

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:

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. The project follows PEP 8 (88-char lines, flake8 enforced) with Sphinx-style docstrings; see existing modules for the conventions.

License

MIT. See LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

polyskills-1.0.0.tar.gz (26.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

polyskills-1.0.0-py3-none-any.whl (28.5 kB view details)

Uploaded Python 3

File details

Details for the file polyskills-1.0.0.tar.gz.

File metadata

  • Download URL: polyskills-1.0.0.tar.gz
  • Upload date:
  • Size: 26.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for polyskills-1.0.0.tar.gz
Algorithm Hash digest
SHA256 776eb7d1b077293aedda13ca44e607722c9688b3ea11d0ce8a3d3777606088dd
MD5 33e7a8f495c270562ea586d3f4758d59
BLAKE2b-256 173bafb4a27119758d90a786f06917e782671e6857d49bd53c12a0251d557c4e

See more details on using hashes here.

Provenance

The following attestation bundles were made for polyskills-1.0.0.tar.gz:

Publisher: publish.yml on PyUtility/polyskills

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file polyskills-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: polyskills-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 28.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for polyskills-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 c11e39544fc36c49853dad8ea1d49166055ddc78ce75ee8b878c2e39fe464583
MD5 6ad5cd6aa13f6223e13b64470ce0ff5c
BLAKE2b-256 2a90aeaf9b9ddb0dc9e5f8287490a09887b3c745c709739d95222e0d9bac01d3

See more details on using hashes here.

Provenance

The following attestation bundles were made for polyskills-1.0.0-py3-none-any.whl:

Publisher: publish.yml on PyUtility/polyskills

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page