Skip to content

English · 简体中文

Skills API

Skills are reusable bundles of system prompt, tool filter, and trigger conditions. They let you package domain expertise (code review, translation, summarization, RAG) into a single file that PAR auto-discovers and activates when relevant.

Status: v0.5.2-beta. API may change before v1.0.


What is a skill?

PAR has four abstraction layers. Skills sit alongside tools, agents, and middleware:

Layer What it packages Example
Tool A single function callable by the LLM read, web_search, bash
Skill System prompt + tool subset + trigger conditions "Code reviewer" (review prompt + read/grep tools only)
Agent Full config: model, tools, middleware, retry policy Default agent with GPT-4 + 23 tools
Middleware Cross-cutting hook (logging, retry, rate limit) Retry with exponential backoff

When to create a skill vs an agent: - Skill: You want to switch behavior within a conversation without restarting. The agent stays the same; the skill overlays its prompt and tool filter. - Agent: You need a fundamentally different model, tool set, or middleware chain.


Quick start

Create a skill in 30 seconds:

mkdir -p ~/.par/skills/greeter
cat > ~/.par/skills/greeter/skill.md << 'EOF'
---
schema_version: 1
id: greeter
name: Greeter
description: Greet users warmly. Use when the conversation starts or the user says hello.
system_prompt_override: "You are an enthusiastic greeter. Always start with a warm welcome."
tool_filter: All
trigger: Keyword [hello, hi, hey]
---

# Greeter Skill

When activated, greet the user warmly and ask how you can help.
Use their name if provided in the conversation context.
EOF

Now start PAR and check:

par
> /skills
  greeter              Greet users warmly. Use when the conversation starts...
> /skill greeter
ID:          greeter
Name:        Greeter
Description: Greet users warmly. Use when the conversation starts or the user says hello.
Trigger:     keyword

The skill is auto-discovered and registered. When the user types "hello", the keyword trigger activates the skill.


File format

Each skill lives in its own directory: ~/.par/skills/<id>/skill.md.

Directory structure

~/.par/skills/greeter/
└── skill.md          ← YAML frontmatter + markdown body

YAML frontmatter reference

Field Type Required Default Description
schema_version int yes Must be 1 for v0.5.2. Loader rejects unknown versions.
id string yes Lowercase-hyphen identifier. Must match directory name.
name string yes Display name.
description string yes What the skill does + when to use it. Max 1024 chars. Always loaded (L1 metadata).
system_prompt_override string | null no null Replaces the agent's system prompt when skill activates.
tool_filter All | Only [...] | Except [...] no All Restricts which tools the LLM can use.
trigger Auto | Manual | Keyword [...] no Auto When the skill gets activated.
expected_output JSON | null no null Forward-looking: typed success criteria (informational in v0.5.2, LLM judge planned for a future version).

Markdown body

The body after the second --- is the skill's instructions. It's lazy-loaded (L2) — only read into memory when the skill activates, not at startup. This keeps startup fast even with 50+ skills installed.


Trigger types

The trigger field controls when a skill becomes active:

Auto (default)

The skill description is loaded into the LLM's system prompt. The LLM itself decides whether to activate the skill based on the user's message and the description text.

trigger: Auto

Best for: skills that should be "always available" to the LLM (summarizer, RAG assistant).

Manual

The skill is never auto-activated. Users must explicitly invoke it.

REPL (activates for the current session, applies to all subsequent invokes):

> /skill use my-skill
Skill activated: my-skill
> hello
[response with my-skill's system_prompt_override applied]
> /skill unuse
Manual skill activation cleared (1 were active).

Activation is persistent across invokes within the session until cleared with /skill unuse. It composes with auto-triggered skills (keyword/auto) — system_prompt_override uses last-wins, tool_filter intersects. Use this for specialized workflows that should remain on for a stretch of a conversation.

Standalone CLI (validation only — a CLI process exits before any invoke, so it can't actually apply activation):

par skill use my-skill    # validates the skill exists, prints how to activate in REPL

Best for: skills that should only fire on explicit request (dangerous operations, specialized workflows).

Keyword

A fast substring pre-filter runs before LLM judgment. The skill is only considered if one of the keywords appears in the user's message:

trigger: Keyword [pdf, form, document] confirm
  • confirm (default): after keyword match, LLM still judges whether to activate
  • deterministic: keyword match immediately activates, no LLM judgment needed
trigger: Keyword [pdf, form] deterministic

Best for: skills with clear domain keywords (PDF extractor, translator for specific languages).

Token budget

To prevent system prompt explosion, PAR caps total skill description tokens at 2048 (configurable via runtime config skill_token_budget). When the budget is exceeded, lowest-priority skill descriptions are dropped with a warning.


Tool filter

Restricts which tools the LLM can access when the skill is active:

tool_filter: All                    # all registered tools (default)
tool_filter: Only [read, grep] # only these tools
tool_filter: Except [bash]          # all tools except these

When multiple skills are active simultaneously, filters compose by intersection (most restrictive wins). This ensures no skill can widen another skill's restriction.


Discovery

PAR discovers skills from three sources, in this precedence order (first match wins):

  1. SDK-registered (highest): Runtime.register_skill(descriptor) — explicit, programmatic
  2. Project: ./.par/skills/<id>/skill.md — checked into the repo
  3. User: ~/.par/skills/<id>/skill.md — user-wide defaults
  4. Builtin (lowest): 4 starter skills shipped with PAR

Hot-reload: On each Runtime.invoke, PAR checks if any skills directory's mtime changed. If so, it rescans automatically. You can also force a rescan:

par skill reload

CLI usage

REPL commands

Command Description
/skills List all registered skills with description preview
/skill <id> Show full detail of a specific skill (default subcommand)
/skill use <id> Manually activate a skill (overrides its trigger; persistent until /skill unuse)
/skill unuse Clear manual activation; subsequent invokes use only auto-triggered skills
/skill create <id> Interactive wizard: create a new skill in ~/.par/skills/<id>/skill.md

Activation semantics: /skill use <id> adds <id> to the runtime's user_activated_skills set. On every invoke, skills in this set are resolved regardless of their trigger (Auto/Manual/Keyword) and composed with any auto-triggered skills. system_prompt_override uses last-wins (order is: auto-triggered first, then user-activated); tool_filter intersects (most restrictive wins). Manual-trigger skills are only activatable via /skill use — they are dead weight otherwise.

Standalone commands

par skill list                    # list all discovered skills (non-interactive)
par skill show <id>               # show skill detail (id, name, trigger, tool_filter, override)
par skill use <id>                # validate a skill is available; print activation hint
par skill create [id]             # interactive wizard (see below); id optional, prompted if omitted
par skill reload                  # force filesystem rescan (invalidates mtime cache)

/skill create wizard

The wizard prompts for all 8 frontmatter fields and writes a valid skill.md to ~/.par/skills/<id>/skill.md:

$ par skill create my-analyst
Name [my-analyst]:
Description (≤1024 chars): Analyze data and report insights
System prompt override (blank = none, \n for newlines): You are a data analyst.
Tool filter [All|Only|Except] (default: All): Only
Only tools (comma-separated): read, grep, bash
Trigger [Auto|Manual|Keyword] (default: Auto): Keyword
Keywords (comma-separated): data, analyze, report
Expected output JSON schema (blank = none):
Created: /home/user/.par/skills/my-analyst/skill.md
Use /skill use my-analyst to activate it in this session.

The file is immediately discoverable by /skills and par skill list (the wizard calls Skill_loader.force_reload to invalidate the mtime cache). Edit the ## Instructions and ## Examples sections in the generated file to customize the L2 body.


SDK API

Python

from par_runtime import Runtime
import json

rt = Runtime(config_json)

# Register a skill programmatically
rt.register_skill(json.dumps({
    "schema_version": 1,
    "id": "my-skill",
    "name": "My Skill",
    "description": "Does something useful.",
    "system_prompt_override": "You are a specialist.",
    "tool_filter": "Only [read]",
    "trigger": "Auto"
}))

# List all registered skills
skills = rt.list_skills()
for s in skills:
    print(f"  {s['id']}: {s['description']}")

OCaml

(* Create a skill descriptor *)
let descriptor =
  Par.Runtime.make_skill
    ~id:"my-skill"
    ~description:"Does something useful."
    ~system_prompt_override:"You are a specialist."
    ~tool_filter:(Par.Types.Only ["read"])
    ~trigger:Par.Types.Auto
    ()
  |> Result.get_ok

(* Register it *)
let _ = Par.Runtime.register_skill rt descriptor

(* List all skills *)
let skills = Par.Runtime.list_skills rt
List.iter (fun s -> Printf.printf "  %s: %s\n" s.Par.Types.id s.Par.Types.description) skills

Builtin skills

PAR ships with 4 starter skills. Override them by creating a skill with the same id in ~/.par/skills/ or ./.par/skills/:

ID Trigger Tools Description
code-reviewer Keyword: review, audit read, grep, find Reviews code for bugs, security, style
summarizer Auto All Summarizes long text into key points
translator Keyword: translate, 翻译 All Translates between languages
rag-assistant Auto All Answers using retrieved document context

Examples

Example 1: Custom skill — Python data analyst

mkdir -p ~/.par/skills/python-analyst
cat > ~/.par/skills/python-analyst/skill.md << 'EOF'
---
schema_version: 1
id: python-analyst
name: Python Data Analyst
description: Analyze Python code for data science patterns. Use when the user asks about pandas, numpy, or data analysis code.
system_prompt_override: "You are a Python data science expert. Focus on pandas/numpy patterns, performance, and correctness."
tool_filter: Only [read, grep, find]
trigger: Keyword [pandas, numpy, dataframe, data analysis]
---

# Python Data Analyst

When analyzing Python code:
1. Check for common pandas anti-patterns (chained indexing, in-place modification)
2. Verify numpy vectorization opportunities
3. Suggest type annotations for data science functions
4. Flag potential memory issues with large DataFrames
EOF

Example 2: Override a builtin

To customize the summarizer skill, create a project-level skill that overrides the builtin:

mkdir -p .par/skills/summarizer
cat > .par/skills/summarizer/skill.md << 'EOF'
---
schema_version: 1
id: summarizer
name: Tech Summarizer
description: Summarize technical documents. Use when the user asks for a summary or TL;DR.
system_prompt_override: "You are a technical writer. Summarize with structured headers, preserve all technical terms, and include a one-sentence TL;DR at the top."
tool_filter: All
trigger: Auto
---

# Tech Summarizer

Format summaries as:
## TL;DR
[one sentence]

## Key Points
- bullet points

## Technical Details
[code blocks, configs, commands]

Because this is in ./.par/skills/ (project), it takes precedence over the builtin summarizer (which is lowest priority).

Example 3: Deterministic keyword skill

A skill that activates immediately on keyword match, without LLM judgment:

mkdir -p ~/.par/skills/log-parser
cat > ~/.par/skills/log-parser/skill.md << 'EOF'
---
schema_version: 1
id: log-parser
name: Log Parser
description: Parse and analyze log files. Use when the user mentions logs, stack traces, or error output.
system_prompt_override: "You are a log analysis expert. Extract timestamps, error codes, and stack traces. Correlate events across log lines."
tool_filter: Only [read, grep, bash]
trigger: Keyword [log, stack trace, error output, traceback] deterministic
---

# Log Parser

1. Identify the log format (syslog, JSON, plain text)
2. Extract all ERROR/WARN lines
3. Find the root cause (first error in the chain)
4. Suggest fixes based on the error pattern

The deterministic flag means: if the user's message contains "log", "stack trace", "error output", or "traceback", this skill activates immediately — no LLM round-trip needed.


Tiered context loading

PAR uses a 2-level context loading model inspired by Anthropic Agent Skills:

Level What When loaded Token cost
L1 id, name, description (frontmatter only) At startup, always in memory ~100 tokens per skill
L2 Markdown body (instructions) Only when skill activates Variable (skill-specific)

This means you can install 50+ skills without bloating every LLM call — only the L1 metadata is always resident. The L2 body is loaded on-demand when a skill triggers.


Migration and versioning

The schema_version field (currently 1) lets PAR evolve the skill format without breaking existing skills:

  • Unknown versions are rejected at load time with a clear error: "skill targets schema_version 2, current is 1. See MIGRATION.md."
  • Old versions may be auto-migrated (future) or rejected with upgrade instructions.

When PAR adds new frontmatter fields in v0.6+, skills with schema_version: 1 will still work — new fields default to sensible values.


Skills as behavioral modes

A common agent pattern is switching the same agent between plan / build / review / debug behaviors with different tool sets and prompts. PAR implements this via Skills, not a separate Mode type.

Why not a separate Mode type?

Skill's data model is a strict superset of what "Mode" typically means:

Mode concept Skill equivalent
allowed_tools tool_filter (All_tools \| Only \| Except)
prompt_fragment system_prompt_override (Stable_prompt \| Volatile_prompt \| Both_prompts)
register_mode(id, ...) register_skill(descriptor)
set_mode("plan") set_user_activated_skills(["plan"]) or invoke(..., skills=["plan"])

Adding a parallel Mode type would create two systems doing the same thing — a type-rigor violation (STRATEGY.md §3). See DECISIONS.md #2 for the full rationale and escalation trigger.

Per-call mode switching

Use the ?skills parameter on Runtime.invoke / invoke_async for race-free per-call activation:

(* Activate "plan-mode" skill for this invoke only *)
Runtime.invoke rt ~agent_id:"coder" ~message:"implement X" ~skills:["plan-mode"] ()

The ?skills parameter does NOT modify rt.user_activated_skills — it overrides the skill snapshot for this single invoke only. This is safe for concurrent invoke calls on the same runtime.

Skill activation events

v0.8.1 adds two event variants for observability (Skill_activated, Skill_deactivated):

  • Skill_activated of { skill_id : string } — emitted at invoke entry for each active skill
  • Skill_deactivated of { skill_id : string } — reserved for future use (when skills gain explicit deactivation)

Subscribe via the event bus to audit mode transitions.

Python API

rt.set_user_activated_skills(["plan-mode", "debug-mode"])
rt.invoke("coder", "implement X")
rt.clear_user_activated_skills()
rt.get_user_activated_skills()   # -> [] (after clear)

See also

  • Agent API — agents, Runtime.invoke, tool handlers
  • Tools — 23 builtin tools, custom registration
  • RAG API — embeddings, vector store, retrieval (used by rag-assistant skill)
  • Architecture — how PAR works internally