context is injected into the prompt whenever the skill is active, and each tool’s description is what the model reads to pick a tool: both are prompt text. Use this guide when a tool is ignored, called with missing arguments, or called when it shouldn’t be; for the agent’s overall tone or identity, change the persona instead.
Verified against lua-cli 3.33.0.
Before you begin
- A skill with at least one tool registered on the agent (Add a tool to a skill).
- Signed in, so
lua chatandlua logscan reach the agent (Install and sign in).
1
Write the rules that span the tools
context carries what applies across the skill: when to ask before calling, what never to do, and what to say back. Name tools by their name; the model matches on it. The skill’s own description is listing text for lua skills view and the admin dashboard, not prompt text.Don't
Do
2
Describe each tool for the model
description says what one tool does and when it applies, in one or two sentences. Field descriptions in inputSchema tell the model how to fill each argument, and constraints such as .email() or an enum reject values it made up.Don't
Do
context; what one tool does belongs in its description.3
Test on a fresh thread
lua chat compiles the project and runs the conversation with your local skills, so a context change needs no push (About environments). -t starts a fresh thread each time; without it the previous answer shapes the next one.lookup_tickets without asking. The first sandbox run after adding a skill registers it and answers without the tools (Skipping skill tickets - no skillId found in lua.skill.yaml); run it again.4
Verify
Read the skill’s log: A
--name is the skill’s name, and each entry names the tool the model called and the arguments it chose.Output
Calling tool with input {…} line shows the arguments; if one is invented, describe that field in inputSchema and say in context to ask for it.5
Release
lua push uploads a version and changes nothing for end users; lua version create snapshots the agent; lua version promote <n> makes that snapshot live and is also the rollback path (Release an agent to production).lua version create prints ✓ Created v<n> (staged). Run `lua version promote v<n>` to deploy.; <n> comes from that line, promote accepts <n> or v<n> and asks no confirmation, and in a script n=$(lua version list --limit 1 --json --ci | jq -r '.[0].version') reads it.Options you may need
Give voice and text different context
context also takes { base, voice, text }: base applies everywhere and voice or text is added for that kind of conversation; at least one must be set. The persona uses the same object form.
Hide the skill when it doesn’t apply
A skillcondition runs before every turn with a 30-second budget, so keep it to one cheap read. When it returns false or throws, the tools and the context are left out of the prompt: the end user talks to an agent that has never heard of the skill. Your file differs; add only the highlighted lines to your own LuaSkill.
src/skills/tickets.skill.ts
accountId is a field your own tools stored on the end user’s record (Identify users). To gate one tool instead, put the condition on the tool class: the skill’s context stays in the prompt, so the model can still say what it can’t do for this user.
Teach a formatting component
A::: formatting block is a prompt rule like any other. Put the instruction in the skill’s context when the component belongs to that skill’s replies (a list-item per product in a catalog skill, a payment block after create_checkout), and in the persona when it should apply to every reply. Blocks render differently per channel, so name the channel when a block renders only there: “On WhatsApp, send the verification flow.”
If it isn’t working
The tool never shows in lua logs
The tool never shows in lua logs
Either the skill’s
condition returned false or threw, which hides the skill, or the context never says when the tool applies. Check what User.get() returns for the end user you test with, then name the tool in context.The model calls the tool with an invented argument
The model calls the tool with an invented argument
The field has no
.describe() and nothing in context says to ask for it. Add both, and add a constraint (.email(), .regex(), an enum) so a wrong value is rejected before execute runs.Two sandbox messages give different tool choices
Two sandbox messages give different tool choices
Without
-t both messages share your default thread, so the first answer anchors the second. Run each probe with -t, or lua chat clear between them.Next steps
Skills and tools
How context and descriptions reach the model, and when to split skills.
Test an agent before you release
Local runs, sandbox chat, thread isolation.
LuaSkill reference
Every field, the context object form, and errors.

