Metadata-Version: 2.4
Name: pyskills
Version: 0.0.9
Summary: Python-native skills system
Author-email: Jeremy Howard <github@jhoward.fastmail.fm>
License: Apache-2.0
Project-URL: Repository, https://github.com/AnswerDotAI/pyskills
Project-URL: Documentation, https://AnswerDotAI.github.io/pyskills/
Keywords: nbdev
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: fastcore>=1.12.33
Dynamic: license-file

# pyskills


<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->

pyskills is a plugin system that lets Python packages register “skills”
(units of LLM-usable functionality) via standard [entry
points](https://packaging.python.org/en/latest/specifications/entry-points/).
An LLM host (e.g. solveit) discovers available pyskills without
importing them, reads lightweight descriptions via AST inspection, and
selectively loads chosen pyskills into context using standard imports.

It includes
[`list_pyskills()`](https://AnswerDotAI.github.io/pyskills/core.html#list_pyskills)
for discovery,
[`doc()`](https://AnswerDotAI.github.io/pyskills/core.html#doc) for
rendering module/class/function documentation in LLM-friendly format,
and an
[`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow)
system for registering safe callable access in sandboxed environments.
Skills can be installed as regular packages with entry points, or
dropped into an XDG data directory for quick local use.

pyskills shares the progressive disclosure philosophy of the [Agent
Skills](https://agentskills.io/specification) specification: both
separate lightweight discovery metadata from full instructions loaded on
demand. However, where Agent Skills uses a file-system convention
(`SKILL.md` with YAML frontmatter, `scripts/`, `references/`
directories), pyskills takes a Python-native approach: pyskills are
regular Python modules discovered via standard entry points, documented
with docstrings, and loaded with `import`. This means pyskills are
directly executable, come with auto-generated structured documentation
via [`doc()`](https://AnswerDotAI.github.io/pyskills/core.html#doc), and
include a sandboxing layer via
[`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow) for
safe execution. This makes pyskills a superset that covers discovery,
documentation, execution, and security in one system.

## Usage

### Installation

Install the latest from [pypi](https://pypi.org/project/pyskills/)

``` sh
$ pip install pyskills
```

### How to use

``` python
from pyskills import *
```

Discover what pyskills are available. This works without importing any
pyskill modules:

``` python
list_pyskills()
```

    {'pyskills.skill': 'Pyskills is a plugin system allowing Python packages to register "skills" — units of LLM-usable functionality — via standard Python entry points. An LLM host (e.g. solveit) discovers available pyskills without importing them, reads lightweight descriptions via AST inspection, and selectively loads chosen pyskills into context using standard imports.',
     'test.skill': 'A test pyskill.'}

### The [`doc`](https://AnswerDotAI.github.io/pyskills/core.html#doc) function

Once you’ve found a pyskill you want to use, import its module using
standard python syntax:

``` python
import pyskills.skill
```

Use [`doc()`](https://AnswerDotAI.github.io/pyskills/core.html#doc) to
read its full documentation.
[`doc()`](https://AnswerDotAI.github.io/pyskills/core.html#doc) works on
modules, classes, and functions, rendering LLM-friendly output in each
case.

For a **module**,
[`doc`](https://AnswerDotAI.github.io/pyskills/core.html#doc) shows all
public classes and functions with their signatures and first docstring
line:

``` python
print(doc(pyskills.skill)[-500:])
```

    illTestClass)
        doc(pyskills.skill.skill_test_func)

    ## Creating pyskills

    `from pyskills import createskill; doc(createskill)` for how to build and register your own pyskill modules, including the allow/policy system."""

    def allow(*c, allow_policy=None): ...  # Add all items in `c` to `__pytools__`, optionally constrained by `allow_policy`
    class SkillTestClass(str): ...  # Some class.
    def skill_test_func(x: int = 0) -> str: ...  # A test function

    allows:
    - allow(skill_test_func, SkillTestClass)

For a **function**,
[`doc`](https://AnswerDotAI.github.io/pyskills/core.html#doc) renders
the full signature with parameter comments (docments):

``` python
print(doc(pyskills.skill.skill_test_func))
```

    def skill_test_func(
        x:int=0, # the input
    )->str: # the output
    """A test function"""

For a **class**,
[`doc`](https://AnswerDotAI.github.io/pyskills/core.html#doc) shows the
class hierarchy, docstring, `__init__` signature, and all public methods
with their first docstring line:

``` python
print(doc(pyskills.skill.SkillTestClass))
```

    class SkillTestClass(str):
        """Some class.
        More info about it."""
        def __init__(self): ...
        def f(self, x: int = 0) -> str: ...  # A test method
        @property
        def g(self) -> str: ...  # A test prop

### The [`allow`](https://AnswerDotAI.github.io/pyskills/core.html#allow) system

When pyskills run in a sandboxed environment like
[safepyrun](https://github.com/AnswerDotAI/safepyrun), they need to
declare which functions and methods are safe for an LLM to call.
safepyrun uses
[RestrictedPython](https://restrictedpython.readthedocs.io/) to
intercept every attribute access and function call, checking each one
against an allowlist stored in the `__pytools__` registry. The
[`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow)
function is how pyskills register their safe callables into that
registry.

You can allow individual functions, all public methods of a type, or
specific methods:

``` python
# Allow specific methods on a type
allow({str: ['zfill']})

# Allow all public methods on a type
allow({list: ...})

# Allow a callable function
allow(list_pyskills)
```

Skill modules typically call
[`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow) at
module level, so permissions are registered automatically when the
pyskill is imported. For instance, when `safepyrun`’s `RunPython`
executes LLM-generated code, it checks every call against `__pytools__`.
If a function isn’t registered, the call is blocked.

The `pyskills.skill` module used in the examples above is itself
registered as a pyskill entry point. It ships with pyskills both as a
working sample and as a self-documenting pyskill that explains the
system itself. Its docstring’s “Creating pyskills” section
cross-references `pyskills.createskill`, a companion module (not
registered as an entry point, so not shown in
[`list_pyskills()`](https://AnswerDotAI.github.io/pyskills/core.html#list_pyskills))
that documents how to build your own pyskills:

``` python
from pyskills import createskill
```

``` python
print(doc(createskill)[:300])
```

    module pyskills.createskill:
    """How to create a pyskills module.

    A pyskill is a standard Python module that registers itself via entry points so LLM hosts can discover and load it.

    ## 1. Create your module

    Your module needs:
    - A docstring — first paragraph is the short description shown durin

## Creating pyskills

A pyskill is a standard Python module that registers itself via entry
points so LLM hosts can discover and load it. Your module needs three
things:

- **A docstring**: the first paragraph is the short description shown
  during discovery via
  [`list_pyskills()`](https://AnswerDotAI.github.io/pyskills/core.html#list_pyskills);
  the rest is the detailed documentation the LLM reads after loading.
- **`__all__`**: lists the symbols available to the LLM.
- **[`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow)
  calls**: declares what the LLM is permitted to call in sandboxed
  environments.

Here’s a minimal example:

``` python
'''Short description for discovery.

Detailed docs read by the LLM after import.'''

from pyskills.core import allow

__all__ = ['my_func', 'MyClass']

def my_func(x: int) -> str:
    "Does something useful"
    ...

class MyClass:
    "A useful class"
    def method(self) -> str:
        "Does something"
        ...

allow(my_func, {MyClass: ...})
```

To register your module as a discoverable pyskill, add an entry point in
your `pyproject.toml`:

``` toml
[project.entry-points.pyskills]
my_skill = "mypackage.mymodule"
```

The key is an arbitrary name; the value is the module path. After
installing the package,
[`list_pyskills()`](https://AnswerDotAI.github.io/pyskills/core.html#list_pyskills)
will include your pyskill automatically.

For full details on creating pyskills, including allow policies for
write-guarded operations, see `doc(createskill)` after importing it as
shown above.

### Local pyskills without packaging

The entry point approach above requires installing a package. But
sometimes you want to create pyskills quickly without a full package:
personal utility pyskills, or pyskills shared across multiple projects
that each use isolated environments (like
[uv](https://docs.astral.sh/uv/) venvs).

pyskills provides an
[XDG](https://specifications.freedesktop.org/basedir-spec/latest/)-based
pyskills directory for this. When you first `import pyskills`, it
creates a directory at your platform’s XDG data home (typically
`~/.local/share/pyskills/`) and writes a `.pth` file into
`site-packages`. This `.pth` file tells Python to add the pyskills
directory to `sys.path` on startup, so any modules placed there are
importable as standard Python modules without any special import
machinery. This works across all Python environments on your system,
even separate uv projects with isolated venvs.

You can check where this directory is (although you can use the
functions below without needing to know):

``` python
pyskills_dir()
```

    Path('/Users/jhoward/.local/share/pyskills')

You can drop pyskill modules directly into this directory, or use
[`register_pyskill`](https://AnswerDotAI.github.io/pyskills/core.html#register_pyskill)
to create one programmatically:

``` python
register_pyskill('my_local.skill', docstr='A quick local pyskill.', code='''
from pyskills.core import allow

__all__ = ['hello']

def hello(name: str) -> str:
    "Greet someone"
    return f"Hello, {name}!"

allow(hello)
''')
```

This writes the module file into the XDG pyskills directory and creates
a minimal entry point, so the pyskill immediately appears in
[`list_pyskills()`](https://AnswerDotAI.github.io/pyskills/core.html#list_pyskills).

You can also manage pyskills with
[`enable_pyskill`](https://AnswerDotAI.github.io/pyskills/core.html#enable_pyskill)
and
[`disable_pyskill`](https://AnswerDotAI.github.io/pyskills/core.html#disable_pyskill)
to toggle their visibility without deleting files, or use
[`disable_pyskill`](https://AnswerDotAI.github.io/pyskills/core.html#disable_pyskill)
to remove one entirely.
