Write your first skill
You ran the built-in example skills in the previous step. Now you’ll author one yourself: a greeter that asks for your name, drafts a friendly greeting, pauses so you can decide whether to continue, then offers a fun fact. It stays entirely in chat and needs no MCP tools — a complete skill in two small files.
This is the same greeter that ships with Skiller. Building it yourself shows you every moving
part: the folder layout, the manifest, a Liquid-templated prompt, a confirmation gate, and
per-step state through outputs.
1. Create the skill folder
Section titled “1. Create the skill folder”A skill is a folder. Skiller discovers workspace skills under .skiller/skills/<id>/, and the
folder name is the skill’s default id. Create:
.skiller/skills/greeter/├── skill.yaml└── steps/ ├── 01-greet.md └── 02-fact.mdThe workspace tier wins over the user tier and the built-in skills, so a greeter you author here
takes precedence — see Skills & discovery.
2. Write the manifest
Section titled “2. Write the manifest”The manifest is a strict-schema YAML file. It names the skill, declares the inputs collected
before the run, and lists the typed steps that execute in order.
.skiller/skills/greeter/skill.yaml:
id: greetername: Greeterdescription: A simple greeting workflow demonstrating multi-step skills with confirmationversion: "1.1.0" # any semver string — it's just metadata
inputs: - name: name type: string description: Your name required: true prompt: "What is your name?"
models: default: gpt-4o
steps: - id: greet type: llm file: steps/01-greet.md output: greeting
- id: confirm type: confirmation message: | {{ outputs.greeting }}
Would you like me to also generate a fun fact? options: - { label: "Yes, give me a fun fact", action: continue } - { label: "No thanks, just the greeting", action: abort } output: user_choice
- id: fact type: llm file: steps/02-fact.md model: gpt-4.1 # per-step model override output: fun_fact
on_error: abort
output: summary: "{{ outputs.fun_fact }}"What each part does:
inputsare collected before the skill runs. Thepromptis the question you’re asked in chat; the answer lands at{{ inputs.name }}.models.defaultsets the model for the whole skill. Thefactstep overrides it withmodel: gpt-4.1— see Use & override models.stepsrun top to bottom. Each step has a uniqueidand writes to a uniqueoutput. Thegreetstep’sgreetingis what theconfirmstep interpolates and what you see in the gate.confirmationpauses for a human choice.continueproceeds to the next step;abortstops the run cleanly. Your click is recorded atoutputs.user_choiceas an object — see Step types & state.output.summaryis the final message shown when the skill finishes.
3. Write the prompt
Section titled “3. Write the prompt”Each llm step points at a Markdown file rendered with Liquid — a small
templating language. In skills you’ll mostly use {{ ... }} to insert values, plus {% if %} /
{% for %} for logic. The prompt can read {{ inputs.* }} and any earlier step’s
{{ outputs.* }}.
.skiller/skills/greeter/steps/01-greet.md:
---id: greetdescription: Generate a personalized greeting---Generate a warm, friendly greeting for {{ inputs.name }}.Keep it to one sentence.The block between the --- fences is step-file frontmatter. Here it carries id and
description; it can also hold a step’s tool / tools / tool_mode / requires so a step’s
tool config sits next to its prompt. The frontmatter id is for your own reference — it does
not have to match the step id in the manifest. See the
Step-file frontmatter reference for the full set of keys.
Add a second prompt for the optional fun fact, .skiller/skills/greeter/steps/02-fact.md:
---id: factdescription: Generate a fun fact---Share one short, surprising fun fact related to the name {{ inputs.name }}.Keep it to one or two sentences.4. Run it
Section titled “4. Run it”In the Chat view:
@skiller /reload Pick up the new skill (reports added/removed/errors)@skiller /skill greeter Run itSkiller asks “What is your name?”, drafts a greeting, then pauses on the confirmation. Pick
Yes, give me a fun fact to continue to the fact step, or No thanks, just the greeting to
stop. To watch it visually, open the live execution graph first —
click the Show Graph CodeLens above your skill.yaml (or run @skiller /skills greeter) — then
run the skill and watch greet → confirm → fact light up as each step fires.
You can also skip the prompt by passing the input up front:
@skiller /skill greeter name=Ada5. Watch the templating
Section titled “5. Watch the templating”While authoring, set skiller.skills.verboseMode to "rendered" in VS Code settings to see
each prompt after Liquid interpolation alongside the model’s reply — the fastest way to confirm a
template resolves the way you expect. Set it to "raw" to see the raw model exchange instead. You
can also hover any step in the live execution graph to inspect its fully-interpolated prompt and
response. See Debug a skill.
A note on JSON replies
Section titled “A note on JSON replies”greeter’s llm steps return plain text, so outputs.greeting is just a string. But when an
llm reply is valid JSON, Skiller parses it automatically so a later step can read nested
fields — for example a draft step that returns { "message": "..." } lets you interpolate
{{ outputs.draft.message }}.
What you learned
Section titled “What you learned”- A skill is a folder: a
skill.yamlmanifest plus one Markdown prompt perllmstep. inputsare collected up front; aconfirmationstep pauses for a human choice.- State flows only through
outputs— steps share no conversation history. - A per-step
modeloverridesmodels.default, and valid-JSON replies are parsed into objects.
Next steps
Section titled “Next steps”- Add a tool step that writes a file with zero MCP setup — Build a tool/MCP skill.
- Branch and loop with confirmation
goto— Branch & loop with confirmations. - Understand the model behind it all — Step types & state and Templating with Liquid.
- Look up every field — the
skill.yamlreference.