Skip to main content
After this guide, a push to main compiles and tests your agent, pushes a version, creates an agent version, and promotes it only after a reviewer approves. The same rules apply to scripts and coding agents: every command runs with complete flags and never waits on a prompt. For the release commands themselves, see Release an agent to production. Verified against lua-cli 3.33.0. Before you begin
  • A scoped API key for CI, stored as a repository secret. The workflow below needs agents:read, agents:write, automations:read, and automations:write; add workflows:read and workflows:write when you push workflows, and telephony:read and telephony:write when the project defines a voice. Two steps may need knowledge:read, the scope that also returns production environment values: the closing lua logs check, and lua test when the tool it runs reads Data or User; grant it knowingly or drop them. Leave out agents:manage (it deletes versions) and knowledge:write. agents:write also lets lua push agent change the served persona, model, model settings, and governance at once, with no promote. A scoped key cannot create or duplicate agents (lua init --agent-name exits 10), so create the agent once from your own user session and commit its lua.skill.yaml.
  • lua.skill.yaml committed and .env ignored; production secrets live on the server (lua env production -k <KEY> -v <value>).
  • Node.js 16 or later on the runner; the workflow below uses 20.
1

Make every command non-interactive

Pass the global --ci flag anywhere on the line. When a command would prompt, it fails instead with Interactive prompt required but --ci flag is set. Provide all required flags or arguments. and exit code 1, so supply the flags a prompt would have asked for: --name, --set-version, --force, -m, -e.
Without --ci, a non-TTY runner only gets a warning and then hangs on the prompt. Some prompts are not intercepted by --ci and block instead; the full list is on the CLI overview. Of the commands this workflow runs: lua test skill without --name, lua logs without --type, --name, --user-id, or --json, and lua deploy <type> without --set-version or --force. lua integrations connect needs a browser for the provider’s authorization, so run it once from your machine; lua integrations webhooks create prompts for nothing when you pass --connection, --object, --event, and --hook-url; lua skills production <action> and lua persona sandbox <action> (other than view) open a menu and exit 1 under --ci, so script lua skills <action> and lua skills sandbox view instead.
Under --ci, lua version delete <n> deletes without asking, even without --force. Keep it out of unattended jobs.
2

Authenticate with a secret

The CLI reads LUA_API_KEY from the environment (a .env file in the working directory counts) before any user session or credentials file. With nothing set, every command exits 9 with No Lua CLI authentication found. Run `lua auth configure` or set LUA_API_KEY.
lua status --json --ci reports auth.source (environment for the variable) and project.agentId, proving the runner targets the right agent. Set LUA_NO_HINTS=1 to drop the tip blocks the CLI prints after commands; the full list is on Environment variables.
3

Branch on exit codes and JSON

Every command exits 0 on success and a class-specific code otherwise, printed as one stderr line ✖ <code>: <message>; the table is on Errors and exit codes. lua sync --check exits 1 when local code and the server differ, and lua compile --ci exits 1 on a compile error.
Commands with --json print their result on stdout: lua status, lua logs, lua test, lua version list|show|diff, lua agents, lua models list, lua auth sessions, lua marketplace, and the lua workflows verbs. On failure, only lua workflows, lua auth sessions, and lua marketplace replace the typed line with { "success": false, "error": { "code", "statusCode", "message", "issues" } } on stdout; every other command keeps the line on stderr, so check the exit code before parsing stdout. lua push, lua deploy, and lua chat have no --json. Three outcomes exit 0 and must be read from the output instead: lua push all lists primitives that failed under ⚠️ <n> component(s) failed to push:, lua deploy all under ⚠️ <n> deployments failed:, and a tool that throws under lua test skill returns { status: 'error' }. lua sync --check also registers primitives the server has never seen (without a version).
4

Add the GitHub Actions workflow

Three jobs: verify (compile, one tool test, refuse to release over a newer server version), stage (push and version create), and promote, which runs in a GitHub environment with required reviewers so the live swap waits for a human. lua version promote itself asks no confirmation, so those reviewers are the only approval gate.
.github/workflows/release.yml
lua push all rewrites lua.skill.yaml on the runner; the next run derives versions from the server again, so it need not be committed back. Workflows are not part of lua push all; add lua push workflow --ci --force --name <workflow-name> and lua workflows deploy <workflow-name> -v latest to the stage and promote jobs when the project has any.
5

Verify

Push to main, approve the promote job in GitHub, then confirm from your machine that the active version carries the commit.
Output
A version created with --commit-hash also carries commitHash in this output.

Options you may need

Tie versions to git automatically

lua git connect makes lua push, lua version create|promote|delete, and lua pull create a commit (and a lua/v<n> tag on version create) in the project’s repository; --auto-push also pushes to a GitHub HTTPS origin after lua git auth github. See the lua git reference.

Ship one primitive from CI

Replace the stage and promote jobs with a single gated step when a CI job owns one webhook, job, processor, or trigger: lua push webhook --ci --force --name <webhook-name> followed by lua deploy webhook --ci --name <webhook-name> --set-version latest --force. For those types lua deploy creates and promotes a scoped agent version. A skill deploy is reset by the next lua version promote, so keep the version create and promote jobs for skills; a pushed persona needs no deploy (lua deploy reference).

If it isn’t working

Exit 1. The command needed a value you did not pass. For lua push it is usually --name (more than one entity of that type) or --force; for lua deploy, the type or --name (its version picker and confirmation block instead of failing, so pass --set-version and --force). Each reference page lists the flag behind every prompt.
Exit 9. The secret is not reaching the step. Set LUA_API_KEY at the job or workflow level rather than in a single step, and confirm with lua status --json --ci that auth.source is environment.
Exit 10. The key is valid but has no access to the agent in lua.skill.yaml, for example a key from another organization. Run lua agents --json with the same key to list what it can see, and issue the key from the organization that owns the agent.

Next steps

Release an agent to production

What each command in the CI job does and how to roll back.

Errors and exit codes

Every exit code and the --json error envelope.

Environment variables

LUA_API_KEY, LUA_NO_HINTS, LUA_DEBUG, and the rest.

Claude Code plugin

The same gate for a coding agent working locally.