lua init to a promoted version without guessing. Paste the following agent prompt into its instructions and add the docs MCP server; in Claude Code, the plugin wraps all of this in slash commands behind a production gate.
Verified against lua-cli 3.33.0.
Before you begin
lua-cli3.33.0 or later, signed in from your own terminal (Install the CLI and sign in); the assistant never needs your email, one-time code, or API key.- A project from
lua init, or an agent id fromlua agents --json --ci.
1
Give the assistant the rules
Put this block in the file your assistant reads on every turn, such as
CLAUDE.md, AGENTS.md, or a Cursor rule.Agent prompt
2
Import from 'lua-cli' only
Everything an agent needs is on the root entry point; there is no
lua-cli/skill, defineTool, or defineSkill.A tool is a class that implements
LuaTool, as in the scaffold; runtime objects such as Products are imported for their types and injected as globals at run time (SDK reference).src/skills/tools/SearchProductsTool.ts
3
Lay the project out
lua init creates the scaffold. The compiler starts from the file that constructs LuaAgent (src/index.ts in the scaffold) and compiles only what it references (Project structure).snake_case; skill, job, webhook, trigger, and processor names are kebab-case. Every tool needs a description and every skill a context: the model reads them to decide when to call your code.4
Run the build loop
Every command accepts
--ci, which fails instead of prompting. lua test runs one function in a local VM with .env loaded and no model involved; lua chat -e sandbox compiles the project, uploads it and the .env values as sandbox versions, and lets the platform run the conversation with the model.Output
lua init --agent-id <agent-id> binds an existing agent instead, the only form a scoped API key can use. lua test types are skill, webhook, job, preprocessor, postprocessor, and workflow; for skill, --name is the tool name and --input is the tool’s own fields. -t starts a fresh thread. The first sandbox run after you add a primitive registers it and may answer without it (Skipping skill <name> - no skillId found in lua.skill.yaml), so run it twice, then read lua logs --ci --type agent_error --limit 5 --json. A tool that throws under lua test still exits 0 and returns { status: 'error', error }.5
Branch on the exit code
Every command ends an error with one line,
✖ <code>: <message>, plus a hint; LUA_DEBUG=1 adds the stack (Errors and exit codes).lua workflows adds 4 to 8 for run outcomes; lua status --json --ci always exits 0 and reports sign-in as auth.authenticated.6
Release
lua push uploads a version and changes nothing for end users, except the persona: lua push agent and lua push all serve the pushed persona from the agent’s next message. lua version create snapshots the agent; lua version promote <n> makes that snapshot live and is the rollback path for everything but the persona. Never pass --auto-deploy.lua push all skips workflows: push each with lua push workflow --ci --force --name <workflow-name> and go live with lua workflows deploy <workflow-name> -v latest. lua deploy <type> --name <name> --set-version <v> --force goes live at once for webhooks, jobs, preprocessors, postprocessors, and triggers by creating and promoting an agent version scoped to that primitive. lua deploy skill also serves that version at once, but the next lua version promote resets every skill to the version pinned in the promoted agent version, so a durable change is push, create, promote. To roll a persona back, run lua deploy persona --set-version <n> --force; promote never changes the served persona (Release an agent to production).Compile rules
The compiler bundles eachexecute on its own and strips the lua-cli imports; hence these rules.
- Register everything on the
LuaAgent; a file nothing references is never compiled or pushed. - Keep no state in module scope; each invocation runs in a fresh VM. Use
User,Data, or a job’smetadata. Jobs.createstores the source text ofexecute, so it can’t close over variables; pass values inmetadataand readjob.metadatainside.LuaWebhook.secretmust be a string literal or aconstthe compiler can resolve, neverenv().User.get()returnsnullwhen a lookup by email or phone misses; in webhooks and jobs, pass a user id, because there is no conversation to infer one from.lua env sandbox -k KEY -v <value>writes.env, whichlua testreads andlua chat -e sandboxuploads with each sandbox version;lua env productionsets what deployed code reads.DataandUserare shared by both environments.
Where the deployed runtime lags the typings
Local runs only. The deployed runtime doesn’t accept the options object as the third argument of
Data.create and Data.update yet and fails with searchText must be a string. In deployed code, pass searchText as a plain string.Data.collections()andVoice.createSessionare not available in deployed agents.job.executionon aJobInstanceexists in deployed runs, is not in the typings, and isundefinedunderlua test.
Next steps
Add the docs MCP server
Let the assistant search and read these pages.
Build with the Claude Code plugin
Slash commands and a hook that gates production changes.
Automate releases in CI
The same commands from a pipeline with a scoped API key.
Errors and exit codes
Every exit code and typed error line the CLI prints.

