Skip to main content
After this page, a coding assistant in your project knows what to import, where files go, which command to run at each step, and what each exit code means, so it can take an agent from 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-cli 3.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 from lua 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).
Tool names are 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 each execute 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’s metadata.
  • Jobs.create stores the source text of execute, so it can’t close over variables; pass values in metadata and read job.metadata inside.
  • LuaWebhook.secret must be a string literal or a const the compiler can resolve, never env().
  • User.get() returns null when 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, which lua test reads and lua chat -e sandbox uploads with each sandbox version; lua env production sets what deployed code reads. Data and User are 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() and Voice.createSession are not available in deployed agents.
  • job.execution on a JobInstance exists in deployed runs, is not in the typings, and is undefined under lua 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.