# How to Create and Use Executable Skills as Python Packages in Prime Agent

> Learn to create and use executable skills as Python packages in Prime Agent. Discover how to structure your code and install it for direct use.

- Repository: [Prime Intellect/prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
- Tags: how-to-guide
- Published: 2026-08-12

---

**Prime Agent treats an executable skill as a Python directory containing [`SKILL.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/SKILL.md), [`pyproject.toml`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/pyproject.toml), and a `src/` package with an async `run()` function, then installs it editable into the persistent IPython kernel where it becomes directly awaitable.**

Prime Agent provides a powerful **skill system** that lets you extend agent capabilities with reusable Python code. This guide explains how to create and use **executable skills as Python packages**—full Python modules that install into the agent's kernel and expose both programmatic and shell interfaces.

## What Makes a Skill Executable

A skill becomes a **Python-backed skill** only when four requirements are met. According to the Prime Agent source code, missing any requirement degrades the skill to markdown-only mode with a load warning.

The detection contract in [[`packages/coding-agent/src/core/skills.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/skills.ts)](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/skills.ts) requires:

| Requirement | Purpose |
|-------------|---------|
| [`SKILL.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/SKILL.md) with YAML front-matter | Describes capability to the LLM |
| [`pyproject.toml`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/pyproject.toml) at skill root | Drives editable installation |
| Valid Python import name (`-` → `_`) | Determines module exposure in kernel |
| `src/<import_name>/__init__.py` | Provides callable implementation |

## Minimal Skill Directory Structure

Every executable skill follows this layout:

```text
my-skill/
├── SKILL.md
├── pyproject.toml
└── src/
    └── my_skill/
        └── __init__.py

```

The directory name `my-skill` becomes the import name `my_skill` after hyphen-to-underscore conversion. This naming convention is enforced in the skill discovery logic.

## SKILL.md: Defining the Contract

The [`SKILL.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/SKILL.md) file serves as the **user-visible contract**. Only `name` and `description` from the YAML front-matter are shown to the LLM; implementation details load on demand.

Example from [[`python-skills.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/python-skills.md)](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/skills/skill-creator/references/python-skills.md):

```markdown
---
name: word-count
description: Count word frequencies in text and return the most common words.
---

# Word Count

Call directly from the kernel:

    await word_count("some text to analyze", top=3)

Or from a shell cell:

    !word_count "some text to analyze" --top 3

```

## pyproject.toml: Package Configuration

The [`pyproject.toml`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/pyproject.toml) drives the **editable install** and optional CLI registration:

```toml
[project]
name = "word-count"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = []

[project.scripts]
word_count = "rlm.skill:cli"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/word_count"]

```

Key points:
- **`[project.scripts]`** creates the shell command `word_count` via `rlm.skill:cli`
- **Hatchling** is required when the distribution name differs from the package directory (common with hyphenated names)
- The wheel target explicitly maps `src/word_count` to resolve path mismatches

## Implementation: The run() Function

The core implementation lives in `src/<import_name>/__init__.py`. From the reference example:

```python
from collections import Counter

async def run(text: str, top: int = 5) -> str:
    """Count words in text and return the most common ones."""
    counts = Counter(text.lower().split())
    return "\n".join(f"{word}: {count}" for word, count in counts.most_common(top))

```

Critical implementation rules from the source:

- `run` can be async or sync—the kernel **always awaits** it
- The function's docstring and signature are copied to the module object for `help()` visibility
- Return values should be strings for clean REPL display

## How the Kernel Exposes Your Skill

After installation into the **persistent IPython kernel** (default `~/.prime/agent/kernel-venv`, override with `PRIME_AGENT_KERNEL_VENV`), the kernel:

1. Imports the package under the derived import name
2. Binds it to a matching variable in the REPL namespace
3. Wraps the module to make it **directly awaitable**—calling `await word_count(...)` invokes `word_count.run(...)`
4. Captures import failures: broken skills bind to a placeholder that raises `RuntimeError` with details

This behavior is implemented in [[`packages/coding-agent/src/core/skill-blocks.ts`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/skill-blocks.ts)](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/packages/coding-agent/src/core/skill-blocks.ts), which handles lazy loading of Python packages on demand.

## Three Ways to Use Your Skill

| Context | Syntax | Notes |
|---------|--------|-------|
| **IPython REPL** | `await word_count("text", top=3)` | Direct await on the bound module |
| **Shell cell** | `!word_count "text" --top 3` | Uses `[project.scripts]` entry point |
| **Other skills** | `import rlm; await rlm.run("subtask")` | Spawn sub-agents via `rlm` module |

## Verification Steps

Follow this checklist to validate your executable skill:

**1. Contract check**

Verify the directory structure, valid Python identifier conversion, and wheel configuration.

**2. Standalone test**

Run outside the kernel before loading into Prime Agent:

```bash
cd <skill-dir> && uv run python -c \
  "import asyncio, word_count; print(asyncio.run(word_count.run('a b a', top=1)))"

```

**3. Kernel test**

Start a fresh Prime Agent session, then verify:

```python
help(word_count)          # Shows docstring and signature

await word_count("test")  # Executes or raises RuntimeError with details

```

If any step fails, Prime Agent logs a warning and falls back to markdown-only mode.

## Summary

- **Executable skills as Python packages** require [`SKILL.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/SKILL.md), [`pyproject.toml`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/pyproject.toml), and a `src/` package with [`__init__.py`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/__init__.py) containing a `run()` function
- Prime Agent **installs packages editable** into `~/.prime/agent/kernel-venv` (configurable via `PRIME_AGENT_KERNEL_VENV`)
- The kernel makes modules **directly awaitable**: `await skill_name(...)` calls `skill_name.run(...)`
- Add **`[project.scripts]`** to expose shell commands usable with `!` prefix
- Use **async `run()` functions** for consistency; the kernel awaits both sync and async variants

## Frequently Asked Questions

### What happens if pyproject.toml is missing?

Prime Agent degrades the skill to **markdown-only mode** with a load warning. The [`SKILL.md`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/SKILL.md) description remains visible to the LLM, but no Python code executes.

### Why does the import name convert hyphens to underscores?

Python identifiers cannot contain hyphens. The conversion rule (`my-skill` → `my_skill`) ensures valid module names while preserving descriptive directory naming.

### Can I use setuptools instead of hatchling?

Yes, any PEP 517 build backend works. Hatchling is documented in the reference because it cleanly handles cases where the distribution name (with hyphens) differs from the package directory name (with underscores).

### How do skills handle dependencies?

List requirements under `[project] dependencies` in [`pyproject.toml`](https://github.com/PrimeIntellect-ai/prime-agent/blob/main/pyproject.toml). Prime Agent installs them during the editable install into the persistent kernel environment.