Metadata-Version: 2.4
Name: pyskills
Version: 0.0.26
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: fastaudit
Requires-Dist: fastcore>=2.1.1
Provides-Extra: dev
Requires-Dist: httpx; extra == "dev"
Provides-Extra: ast
Requires-Dist: remold; extra == "ast"
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 harness (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, [`xdir()`](https://AnswerDotAI.github.io/pyskills/core.html#xdir) for listing a module or class’s public symbols, 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.edit': 'Functions for creating, viewing, and modifying files. Each editing operation returns unified diffs showing what changed. Where the `exhash` pyskill is available, prefer it for editing: its hash-verified addressing fails loudly on stale context instead of editing nearby text.',
     'pyskills.ipynb': 'Functions for view/modifying ipynb file notebook cells. Each operation returns unified diffs showing what changed. Where `exhash` is available, prefer its hash-verified editing for cell source changes.',
     '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.',
     'tracefunc.skill': "Trace a Python function's execution at AST-line level: per-line hit counts and live variable values, via `sys.monitoring`. Use when debugging *why* code takes a branch, loops, recurses, or computes a wrong value, without editing the code under investigation or using an interactive debugger.",
     'dialoghelper.solveitskill': 'Read, search, edit, and manage Solveit dialogs using dialoghelper.core, including dialog/message addressing, line-numbered inspection, targeted message edits, add/update/delete/copy/paste workflows, and safe editing patterns.',
     'dialoghelper.termskill': 'Read and edit Solveit dialog (or Jupyter) .ipynb files from a CLI / script. Solveit is an online notebook application (like Jupyter with AI integration) where each notebook is called a "dialog" and is stored as an `.ipynb` file containing `code`, `note` (markdown), and `prompt` (markdown with a special delimiter) messages (aka "cells"). The `dialoghelper` package provides tools for reading, searching, adding, updating, and deleting those messages.',
     'ghapi.skill': 'GitHub REST API access via `GhApi`, plus local git operations via `fastgit.Git`. Use this for day-to-day GitHub work: reading/creating issues and PRs, checking CI status, managing releases/branches/gists, and repo-local git operations -- all from Python, no shelling out to `gh`/`git` needed.',
     'rgapi.skill': 'Fast and flexible file discovery and search for Python. Use this when code needs `fd`-style file finding or `rg`-style searching.',
     'exhash.skill': 'Universal hash-verified text editing for local files. Use this when an LLM needs one safe editing interface for reading, previewing, and modifying text files.',
     'cordslite.skill': 'Load this skill when an agent needs to search, summarize, or find information in Discord using cordslite. It covers read-only workflows for connecting to Discord, opening a guild, orienting through channels, searching messages, reading threads, and fetching attachments.',
     'bgtmux.skill': 'Use tmux-backed background terminal sessions from Solveit. Useful to have a persistent terminal session that both you and the user can inspect and edit, and that you can send input to from Solveit.',
     'clikernel.skill': 'Use the persistent `clikernel` MCP session as the default workspace for any task advanced through live Python execution -- stateful inspection, file-editing workflows, debugging, experiments, API probes, data transforms, or notebook-style work. Read this before writing, running, or debugging Python code in a session with `clikernel` connected.'}

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

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, submodules, and any [`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow) calls:

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

    ills.skill.SkillTestClass)
        doc(pyskills.skill.skill_test_func)

    ## Creating pyskills

    `from pyskills import createskill; doc(createskill)` for how to build and register your own pyskill modules.
    """

    ## types:
    - class SkillTestClass(str): …  # Some class.

    ## functions:
    - async def async_skill_test_func(x: int = 0) -> str: …  # A test function…
    - def skill_test_func(x: int = 0) -> str: …  # A test function…

    ## submodules:
      pyskills.createskill: …  # How to create a pyskills pyskill module.

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

``` python
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
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 callables are trusted to perform otherwise-denied operations (filesystem writes, subprocesses, network access). safepyrun uses [fastaudit](https://github.com/AnswerDotAI/fastaudit) to deny those effects unless they happen inside a callable registered in the `__pytools__` registry. The [`allow()`](https://AnswerDotAI.github.io/pyskills/core.html#allow) function is how pyskills register their trusted callables.

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

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

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

# Allow a function
allow(list_pyskills)

# Allow one specific callable instance (e.g. a dynamically-generated client op)
allow(client.images.create_image)
```

An object can also define `__allow__`, returning a list of items to register in its place; [`allow`](https://AnswerDotAI.github.io/pyskills/core.html#allow) recurses into the result, so a container (such as a fastspec client or op group) can register all its callables at once.

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. When `safepyrun`’s `RunPython` executes LLM-generated code, pure computation just runs; an operation with side effects is only permitted when it happens inside a registered callable.

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

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