# How to Create Skills for pi‑ai: A Complete Developer Guide

> Learn to create skills for pi-ai by defining metadata and executable scripts that the Mom Slack-bot discovers and exposes to the LLM. Follow this developer guide for pi-mono.

- Repository: [Mario Zechner/pi-mono](https://github.com/badlogic/pi-mono)
- Tags: how-to-guide
- Published: 2026-02-16

---

**Creating skills for pi‑ai involves defining a directory with a [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) metadata file and executable scripts, which the Mom Slack‑bot automatically discovers, validates, and exposes to the LLM via XML in the system prompt.**

The pi‑ai framework, part of the `badlogic/pi‑mono` repository, powers Mom, a Slack‑bot that extends its capabilities through modular skills. These skills are small CLI tools that live inside the workspace and are automatically integrated into the LLM's context. Understanding how to create skills for pi‑ai allows developers to add custom functionality without modifying the core codebase.

## What Are pi‑ai Skills?

Skills in pi‑ai are reusable CLI tools that function as extensions to the Mom Slack‑bot. Each skill resides in its own directory and contains a [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) file describing its purpose, along with executable scripts that perform the actual work. The system automatically discovers these skills, validates their metadata, and includes them in the LLM's system prompt as XML definitions, allowing the model to understand and invoke available tools dynamically.

## Skill Directory Structure and Location

The pi‑ai system recognizes skills based on their location within the workspace directory tree. According to [`packages/mom/src/agent.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/mom/src/agent.ts) (lines 5‑8), skills can exist at two distinct scopes.

### Global vs Channel‑Specific Skills

**Global skills** reside under `<workspace>/skills/<name>/` and are available across all channels in the workspace. **Channel‑specific skills** live under `<workspace>/<channel>/skills/<name>/` and are only exposed to the LLM when processing conversations in that specific Slack channel. This dual structure allows developers to deploy organization‑wide utilities alongside specialized tools for individual teams.

## Creating the SKILL.md Metadata File

Every skill must contain a [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) file at its root. This file serves as both documentation and configuration, using YAML front‑matter to declare metadata that the validation system consumes.

### Required Front‑Matter Fields

The front‑matter must define two keys: **`name`** and **`description`**. The `name` field becomes the skill's identifier and must match the directory name, while the `description` explains the tool's purpose to the LLM. As shown in [`packages/mom/src/agent.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/mom/src/agent.ts) (lines 21‑28), the system uses this metadata to build the skill registry.

### Optional Configuration Flags

Developers can add **`disable-model-invocation: true`** to prevent the LLM from automatically calling the skill. When this flag is present, the skill is excluded from the system prompt's XML list, but remains accessible via explicit `/skill:<name>` commands.

## Skill Validation and Discovery Process

The pi‑ai framework validates every skill before exposing it to the LLM. The core logic resides in [`packages/coding-agent/src/core/skills.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/skills.ts), where `loadSkillsFromDir` orchestrates the discovery and validation pipeline.

### Name and Description Validation

The `validateName` function (lines 91‑114) enforces strict naming conventions: names must be 1‑64 characters, contain only lowercase letters, numbers, and hyphens, cannot start or end with hyphens, and must match the directory name. The `validateDescription` function (lines 120‑128) ensures the description exists and falls within acceptable length limits.

### Loading and Path Translation

The `loadSkillsFromDirInternal` function (lines 46‑84) recursively walks the skill directory, respects `.gitignore`‑style ignore patterns, and constructs `Skill` objects. In [`packages/mom/src/agent.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/mom/src/agent.ts), `loadMomSkills` (lines 21‑38) translates host filesystem paths to container paths before injecting skills into the prompt, ensuring the LLM receives correct absolute paths like `/workspace/skills/<name>/SKILL.md`.

## Implementing Skill Logic

Beyond metadata, skills require executable components that perform actual work. These can be shell scripts, Python files, or any CLI‑compatible binary.

### Executable Scripts and Tools

Place executable scripts in the skill directory alongside [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md). The LLM learns about these tools by reading the [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) file via the `read` tool (e.g., `read /workspace/skills/note/SKILL.md`). After reviewing the instructions, the model may request Mom to run any script inside the skill’s directory (e.g., [`note.sh`](https://github.com/badlogic/pi-mono/blob/main/note.sh)). Mom executes the script and returns the output to the conversation.

### Complete Todo Skill Example

Below is a fully functional todo list skill demonstrating the required structure:

```bash

# Directory structure

/workspace/skills/todo/
├── SKILL.md
└── todo.sh

```

**[`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md)** contains the metadata and usage instructions:

```markdown
---
name: todo
description: Simple TODO list manager
---

# Todo Skill

Run `todo.sh add <item>` to add a task, `todo.sh list` to show all tasks, and `todo.sh clear` to empty the list.

```

**[`todo.sh`](https://github.com/badlogic/pi-mono/blob/main/todo.sh)** implements the logic:

```bash
#!/usr/bin/env bash
case "$1" in
  add)  echo "$2" >> "$HOME/.pi/todo.txt" ;;
  list) cat "$HOME/.pi/todo.txt" ;;
  clear) > "$HOME/.pi/todo.txt" ;;
  *) echo "Usage: $0 {add|list|clear} [args]" ;;
esac

```

Once deployed, Mom automatically discovers this skill and includes it in the system prompt:

```xml
<available_skills>
  <skill>
    <name>todo</name>
    <description>Simple TODO list manager</description>
    <location>/workspace/skills/todo/SKILL.md</location>
  </skill>
</available_skills>

```

When a user asks "Add a reminder to buy milk," the LLM reads the skill file and Mom executes `/bin/bash /workspace/skills/todo/todo.sh add "buy milk"`.

## How Skills Appear in the LLM Prompt

Skills are communicated to the language model through structured XML injected into the system prompt. This mechanism allows the LLM to understand available tools without hardcoding them.

### XML Formatting with formatSkillsForPrompt

The `formatSkillsForPrompt` function (lines 90‑103 in [`packages/coding-agent/src/core/skills.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/skills.ts)) renders each skill as XML tags containing the name, description, and absolute path to the [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) file. Only skills **without** the `disable-model-invocation` flag are included in this output.

### Disabling Automatic Model Invocation

To prevent the LLM from automatically using a sensitive skill, add `disable-model-invocation: true` to the YAML front‑matter. Such skills remain functional but require explicit user commands to activate, keeping them hidden from the model's automatic tool selection.

## Invoking Skills in Conversations

Once discovered and loaded, skills can be triggered through natural language or explicit commands, depending on their configuration.

### Automatic Invocation Flow

During a conversation, the LLM can request the skill's documentation using the `read` tool (e.g., `read /workspace/skills/note/SKILL.md`). After reviewing the instructions, the model may request Mom to run any script inside the skill’s directory (e.g., [`note.sh`](https://github.com/badlogic/pi-mono/blob/main/note.sh)). Mom executes the script and returns the output to the conversation.

### Explicit Command Invocation

For skills marked with `disable-model-invocation: true`, or when users prefer direct control, skills can be invoked via the `/skill:<name>` command followed by arguments. This bypasses the LLM's decision‑making and immediately executes the skill's entry point.

## Summary

Creating skills for pi‑ai requires understanding the directory structure, metadata format, and validation pipeline:

- Skills reside in `<workspace>/skills/<name>/` (global) or `<workspace>/<channel>/skills/<name>/` (channel‑specific)
- Each skill requires a [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) file with YAML front‑matter containing `name` and `description`
- The system validates names against strict regex rules and checks description length via `validateName` and `validateDescription` in [`packages/coding-agent/src/core/skills.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/skills.ts)
- Skills are automatically discovered by `loadSkillsFromDir` and injected into the LLM prompt as XML via `formatSkillsForPrompt`
- Use `disable-model-invocation: true` to hide skills from automatic LLM usage while keeping them available via explicit commands

## Frequently Asked Questions

### What file naming conventions must pi‑ai skills follow?

Skill names must be 1‑64 characters, contain only lowercase alphanumeric characters and hyphens, cannot start or end with hyphens, and must exactly match the directory name. The `validateName` function in [`packages/coding-agent/src/core/skills.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/src/core/skills.ts) (lines 91‑114) enforces these rules during the discovery phase.

### How does pi‑ai handle skill security and permissions?

Skills operate within the containerized environment's permissions. While the LLM can only invoke skills that appear in its system prompt, sensitive operations should set `disable-model-invocation: true` to prevent automatic execution, requiring explicit user consent via the `/skill:<name>` command.

### Can I create channel‑specific skills in pi‑ai?

Yes. Place skills under `<workspace>/<channel>/skills/<name>/` instead of the global `<workspace>/skills/<name>/` location. The `loadMomSkills` function in [`packages/mom/src/agent.ts`](https://github.com/badlogic/pi-mono/blob/main/packages/mom/src/agent.ts) (lines 21‑38) loads both global and channel‑specific skills, making them available only within their respective Slack channels.

### Why is my pi‑ai skill not appearing in the system prompt?

First, verify that [`SKILL.md`](https://github.com/badlogic/pi-mono/blob/main/SKILL.md) contains valid YAML front‑matter with both `name` and `description` fields. Check that the skill passes validation by reviewing logs for errors from `validateName` or `validateDescription`. Finally, ensure the skill does not have `disable-model-invocation: true` set, as this flag excludes it from the automatic prompt injection performed by `formatSkillsForPrompt`.