puras

skill.yaml reference

Every field in a skill's manifest — entrypoint, model, input/output schemas (Puras dialect), examples, tools.

Every skill is a directory under skills/ in your project bundle, and every directory has one skill.yaml that declares how it runs.

skills/
└── my-skill/
    ├── skill.yaml          # this file
    ├── SKILL.md            # agentic only — the system prompt
    ├── scripts/main.py     # deterministic only — the function
    └── references/         # optional — reference docs an agent can read

Top-level fields

yaml
title: Human-friendly Name       # optional — display label in cards / playground / SEO
description: One-line summary    # required — what this skill does

entrypoint: SKILL.md             # required — agentic loop (system prompt = file)
                                 # OR "scripts/main.py:run" — deterministic function

model: claude/sonnet-4-6         # optional, agentic only — see docs/models
disable_bash: false              # optional, agentic only — strip the bash tool

input_schema:  { Puras dialect } # required — validated before run, drives playground form
output_schema: { Puras dialect } # required — validated after run, drives result rendering

examples:                        # optional — playground seed scenarios (0..N)
  - title: short label           # optional — falls back to "Example N"
    description: 1-line note     # optional — tooltip in the playground chip
    inputs: { ... }              # required — fully-formed input matching input_schema

tools:                           # optional, agentic only — user-defined Python tools
  - name: my_tool
    description: short
    entrypoint: tools/foo.py:run
    input_schema:  { ... }
    output_schema: { ... }

The skill name is the directory name. Use [a-z0-9_-]+ slug style. Names duplicate across the bundle are rejected at parse time.

entrypoint

Two shapes, distinguished by file suffix:

SuffixKindWhat the worker does
.mdagenticRuns an LLM loop with the file's text as system prompt.
.py:funcdeterministicImports func from the file and calls it with the inputs dict.

The path is relative to the skill's own directory. For deterministic skills, group helper modules under scripts/ (convention, not enforced).

model

Public slug in family/variant shape — claude/sonnet-4-6, gpt/5, gemini/2.5-pro. See models for the catalog. Routing between Anthropic / OpenRouter / etc. is resolved internally; never put a provider prefix in your skill.

examples

Each example is a complete inputs payload (must match input_schema), plus an optional title and description. The playground:

  1. Seeds the form with examples[0].inputs on mount.
  2. Renders the rest as clickable chips above the form.

Pick examples that cover the range of useful inputs — they double as discoverability ("Try it") and as a smoke baseline you can test against.

Puras schema dialect

input_schema and output_schema use a small dialect that adds end-user types on top of standard JSON Schema. Standard types pass through unchanged and are validated with jsonschema Draft 2020-12; Puras types are translated server-side before validation.

Type table

Puras typeUI widgetValidates as
stringSingle-line inputJSON Schema string
number, integerNumber inputJSON Schema number / integer
booleanToggle (switch)JSON Schema boolean
arrayJSON textareaJSON Schema array
array of file typeMulti-file uploaderJSON Schema array of file shape
objectJSON textareaJSON Schema object
null(rare)JSON Schema null
imageDrop-zone + URL field + previewbare URL string or {drive_path / url / data} object
videoSame as image, video previewsame
audioSame, audio playersame
fileGeneric file pickersame
textMulti-line textareaJSON Schema string
colorColor picker + hex inputstring with hex pattern

Standard JSON Schema fields you can still use

description, default, title, enum, minimum, maximum, minLength, maxLength, minItems, maxItems, additionalProperties, required, properties, items. Everything else from Draft 2020-12 is honored after dialect translation.

Why a custom dialect

Puras targets end-user playgrounds and structured I/O over a billed backend. JSON Schema doesn't have first-class concepts for "image upload", "hex color", or "multi-line text" — the spec leaves those to vendor extensions. Rather than scattering x-puras keys, contentMediaType strings, and polymorphic oneOf blocks across every field, the dialect encodes intent in the type. A single property:

yaml
image:
  type: image
  description: Kullanıcının fotoğrafı.

Replaces what used to be a 12-line type: object with oneOf, x-puras hints, accept lists, and a default. The runtime knows what to do with it, the playground picks the right widget, and authors stop carrying schema complexity that adds no signal.

File inputs end-to-end

A field declared type: image (or video/audio/file) accepts:

yaml
# Bare strings (any of these):
image: "https://example.com/photo.jpg"
image: "uploads/photo.jpg"                     # drive path
image: "data:image/png;base64,iVBORw0KGgo…"   # data URI

# Or an explicit object (see docs/inputs-and-drive):
image: { url: "https://…" }
image: { drive_path: "uploads/photo.jpg" }
image: { data: "data:image/png;base64,…" }

Inside a deterministic skill, puras.load_bytes(inputs["image"]) and puras.load_path(inputs["image"]) normalize all of these to bytes or a filesystem path — see inputs-and-drive.

In an agentic skill, the runtime auto-attaches file inputs as inline image or document blocks the LLM can see, alongside the JSON of all other non-file fields.

Output schema and structured returns

For agentic skills, output_schema powers a set_output tool the agent calls when it's done — its input_schema is your translated output schema. Authors should not instruct the agent to call set_output in SKILL.md; the runtime adds that. See writing-skills.

For deterministic skills, the function's return value is validated against output_schema and surfaced as the job result.

A whole-output media type works exactly like a file input — declare type: image and return a bare URL string:

yaml
# skill.yaml
output_schema:
  type: image
python
# scripts/main.py
def run(inputs):
    return media.run("openai/gpt-image-2-edit", { ... })["output_url"]

The playground sees type: image on the output and renders the URL as an inline <img> automatically.

Tools (agentic only)

User-defined Python helpers the agent can call from inside the loop:

yaml
tools:
  - name: search_inventory
    description: Look up SKU stock by warehouse.
    entrypoint: tools/inventory.py:run
    input_schema:
      type: object
      required: [sku]
      properties:
        sku: { type: string }
    output_schema:
      type: object
      properties:
        in_stock: { type: integer, minimum: 0 }

Same schema dialect as input_schema / output_schema. The agent sees the tool with its translated schema; the worker validates both the call's input and the function's output before feeding the result back. See agent-tools-reference for the full surface.

What changed from the old format

If you have skills written before the dialect existed:

WasNow
type: object + oneOf: [drive_path/url/base64] + x-puras: { widget: image, … }type: image
type: string + contentMediaType: image/*type: image
type: string + x-puras: { widget: long_text }type: text
type: string + x-puras: { widget: color }type: color
default: per property (used to seed playground)examples: [{ inputs: { … } }] at top level
x-puras: { help: "…" }description: "…" (rendered the same way)
x-puras: { placeholder: "…" }drop it
x-puras: { accept, max_size_mb, upload }drop — runtime defaults handle it

x-puras is no longer read by anything. Strip it.