Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"name": "lua-agent-builder",
"source": "./plugins/lua-agent-builder",
"description": "Build, test, and deploy Lua AI agents from inside Claude Code",
"version": "1.10.0",
"version": "1.11.0",
"homepage": "https://github.com/lua-ai-global/claude-code-lua-plugin#readme",
"repository": "https://github.com/lua-ai-global/claude-code-lua-plugin.git",
"license": "MIT",
Expand Down
2 changes: 1 addition & 1 deletion plugins/lua-agent-builder/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "lua-agent-builder",
"version": "1.10.0",
"version": "1.11.0",
"description": "Build, test, and deploy Lua AI agents from inside Claude Code",
"author": {
"name": "Lua AI",
Expand Down
15 changes: 15 additions & 0 deletions plugins/lua-agent-builder/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

All notable changes to the `lua-agent-builder` plugin. Versions follow the tag `release-prod.yml` cuts from `package.json` (`v<version>`). lua-cli is a TypeScript SDK/CLI; it is unrelated to the Lua programming language.

## 1.11.0 — 2026-10-11

**`/lua-deploy webapp` deploys just the web app (lua-cli 3.49.0+).** lua-cli 3.49.0 adds `lua deploy webapp`, a scoped promote like `lua deploy webhook`: a new agent version identical to the live one except that web app's pin is created and promoted, so nothing else staged on the agent goes live. Until now the plugin made a web app live by creating a whole agent version, diffing it against the live one and stopping when anything else would have changed.

- **`/lua-deploy webapp`**: on lua-cli 3.49.0 or later the pilot pushes the app, then runs `LUA_DEPLOY_CONFIRMED=1 lua deploy webapp --ci --name <n> --set-version <v> --force`. Below 3.49.0 it keeps the agent-version path, with the diff and the stop. The pilot reads `lua --version` once before the deploy step to pick the path.
- **Failures and the kill switch**: a failed scoped deploy changes nothing on the server, so the pilot reports a 503 `SCOPED_PROMOTE_FAILED` or 409 `SCOPED_PROMOTE_CAP_EXHAUSTED` as a failed deploy (for the 409: prune with `lua version delete`). A deploy never switches a disabled app on; the pilot quotes the CLI's `⚠️ Web app <name> is switched off: …` line in its report.
- **Rollback line**: on 3.49.0+ a web app rolls back with `lua deploy webapp --set-version <previous>`. That rolls back only that app, where promoting an earlier agent version rolls back every primitive.
- **Next steps** in `/lua-new`, `/lua-push`, `lua-skill-builder` and the decision tree now point at `/lua-deploy webapp`, not `/lua-deploy agent-version`.
- **`lib/knowledge/web-apps.md`**:
- §7 has the 3.49.0+ deploy, rollback and switched-off rows, with the agent-version rows kept for older CLIs.
- §3: the `Data.get` example now passes the filter as the second argument (it showed `{ filter: … }`).
- §3 also notes that on lua-cli 3.49.0 and earlier a `.default()` field is typed `T | undefined` in the handler. The release after 3.49.0 types the schema output.

Versioning: 1.10.0 was released with `/lua-decisions` (#22), so this is 1.11.0. #18 (`fix/deploy-hook-false-positives`) is still open and declares 1.7.1. If this merges first, #18 takes 1.11.1 and its entry goes above this one.

## 1.10.0 — 2026-10-11

**Decisions: `/lua-decisions`, `/lua-new decision`, and a knowledge file for the Decisions primitive** (lua-cli 3.48.0; `--model` and the OpenRouter ids from lua-cli 3.49.2).
Expand Down
2 changes: 1 addition & 1 deletion plugins/lua-agent-builder/SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Out of scope:
| Device credentials never enter the conversation: lua-cli 3.45.0 `lua devices credential` (aliases `credentials`, `key`) prints the secret to stdout unless `--out <file>` is given, so the plugin only ever runs the `--out` form, into a file it makes sure is git-ignored and never reads back | `hooks/block-device-secret.mjs` (PreToolUse, `if: Bash(*devices*)`) blocks the command — any binary spelling, any action case, `--help` excepted — when it carries no `--out <file>`; `scripts/lint-cli-flags.mjs` fails on any shipped `lua devices credential …` shape without `--out`; the `ask` tier is the confirmation for the `--out` form; `commands/lua-devices.md` forbids `cat`/`source`/`Read` of the file. A belt, like the deploy gate: a command assembled at runtime is out of its reach |
| Device go-live: a pushed `defineDevice` is live on the agent's next turn with no publish, and devices are not part of agent versions | `lua push device …` / `lua push device-trigger …` (every push-type alias) sit in the `ask` tier above the generic `lua push * --ci --force*` allow; publishing a device-trigger needs `--auto-deploy`, which stays denied — `/lua-devices` prints that line for the user's own terminal |
| Web-app commands: only `lua apps new <name>` and four exact npm commands run unprompted — `npm --prefix src/apps/<name>/web install --ignore-scripts [@lua-ai-global/app-client]`, `run typecheck`, `run build` (no other package, script, flag, path or chain); no dependency install script runs unprompted | `hooks/approve-web-app.mjs` approves them with anchored regexes (`<name>` = `^[a-z][a-z0-9-]{0,62}$`) through the PreToolUse `permissionDecision: "allow"` envelope; anything else gets no decision and falls to Claude Code's prompt. Deliberately **not** a permission glob: `*` matches spaces, so `Bash(npm --prefix src/apps/*/web install)` admitted extra packages, a second `--prefix` and `../`, and `Bash(lua apps new *)` any trailing text (both found by reviews before 1.8.0 merged). `run typecheck` / `run build` execute the project's own scripts and Vite config — the same trust as `lua test --ci*`. `test/hooks/approve-web-app.test.mjs`, `test/lib/web-apps-permissions.test.mjs` |
| Web-app routes write live Data, locally too | `/lua-test` runs `GET` at once and a write route only after its one AskUserQuestion; `lua-skill-builder` tests `GET` only; `lua apps dev` is in no permission list and never started by the plugin; `/lua-deploy webapp` diffs the new agent version and stops before promoting if anything other than web apps would go live |
| Web-app routes write live Data, locally too | `/lua-test` runs `GET` at once and a write route only after its one AskUserQuestion; `lua-skill-builder` tests `GET` only; `lua apps dev` is in no permission list and never started by the plugin; `/lua-deploy webapp` on lua-cli 3.49.0+ deploys only that app (a scoped promote: nothing else staged goes live); below 3.49.0 it diffs the new agent version and stops before promoting if anything other than web apps would go live |
| Read-only MCP: no tool mutates platform state | `mcp/lua-platform/src/tools/*` (GET routes and `lua agents --json` only) |

If you find a way to bypass any of these without an explicit user prompt, please report it.
Expand Down
8 changes: 4 additions & 4 deletions plugins/lua-agent-builder/agents/lua-deploy-pilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ tools: [Read, Bash, mcp__plugin_lua-agent-builder_lua-platform__get_deployment_s
You receive `{ target, name, version, notes }` from `/lua-deploy`. The user already authorised this production change through that slash's single `AskUserQuestion` (§3.7) — **do not re-prompt at any step**; emit informational messages only. If a gate fails, abort with one clear message that names the next action, and stop.

lua-cli facts this sequence relies on (verified against 3.33.0; details in `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/cli-reference.md` §4-5):
- `lua deploy <type>` accepts `skill webhook trigger job preprocessor postprocessor persona all`. For `webhook trigger job preprocessor postprocessor` (and `lua workflows deploy`) the server does a **scoped promote** — a new agent version scoped to that primitive is created and promoted, so the change is live at once and the agent-version history stays consistent. For **`skill` there is no scoped promote**: `lua deploy skill` only moves a pointer — live immediately, but the active agent version's snapshot keeps its old pin and the next `lua version promote` (a rollback included) writes that pin back and **silently reverts the deploy**. So on an agent that **has agent versions**, skills ship as push → `lua version create` → `lua version promote`; on an agent with no versions yet, `lua deploy skill` goes live directly and is the right call. The **persona** depends on the CLI (`lua --version`): below 3.44.0 it is live on `lua push agent` and `lua deploy persona --set-version <n>` is only the rollback; ⏳ from **3.44.0** `lua push agent` **stages** it (`Persona vN staged — NOT activated`), `lua deploy persona --set-version <n>` makes it live (a pointer move), and `lua version promote` re-points the served persona to the promoted version's pin — so on a versioned agent the persona ships as `lua version create --persona-version <n>` → `lua version promote`. Workflows deploy with `lua workflows deploy <name> -v <ver>`; MCP servers activate with `lua mcp activate <name>`; voices and "the whole agent" go live by promoting an agent version (`lua version create` → `lua version promote <n>`). **Devices and device-triggers are not part of agent versions** and never reach this pilot: a pushed `defineDevice` is live on the next turn, a device-trigger is published only by `--auto-deploy` (never run) — `/lua-devices` handles both.
- **Web apps** (lua-cli ≥ 3.42.0; `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/web-apps.md` §7) have no `lua deploy` type and no publish of their own: `lua push webapp` builds the pages with the app's own Vite and stages a version (`Staged but NOT live yet`), and an **agent version** makes it live — `lua version create` snapshots the **latest pushed** version of every primitive (lua-agents `agent-version.service.ts`), not only the app's. So a `webapp` target must never carry other staged changes live: diff before promoting (step 5). A version that pins a web app rolls it back when an earlier version is promoted.
- `lua deploy <type>` accepts `skill webhook trigger job preprocessor postprocessor persona all` (⏳ and `webapp` from lua-cli 3.49.0). For `webhook trigger job preprocessor postprocessor` (⏳ `webapp` on 3.49.0+) (and `lua workflows deploy`) the server does a **scoped promote** — a new agent version scoped to that primitive is created and promoted, so the change is live at once and the agent-version history stays consistent. For **`skill` there is no scoped promote**: `lua deploy skill` only moves a pointer — live immediately, but the active agent version's snapshot keeps its old pin and the next `lua version promote` (a rollback included) writes that pin back and **silently reverts the deploy**. So on an agent that **has agent versions**, skills ship as push → `lua version create` → `lua version promote`; on an agent with no versions yet, `lua deploy skill` goes live directly and is the right call. The **persona** depends on the CLI (`lua --version`): below 3.44.0 it is live on `lua push agent` and `lua deploy persona --set-version <n>` is only the rollback; ⏳ from **3.44.0** `lua push agent` **stages** it (`Persona vN staged — NOT activated`), `lua deploy persona --set-version <n>` makes it live (a pointer move), and `lua version promote` re-points the served persona to the promoted version's pin — so on a versioned agent the persona ships as `lua version create --persona-version <n>` → `lua version promote`. Workflows deploy with `lua workflows deploy <name> -v <ver>`; MCP servers activate with `lua mcp activate <name>`; voices and "the whole agent" go live by promoting an agent version (`lua version create` → `lua version promote <n>`). **Devices and device-triggers are not part of agent versions** and never reach this pilot: a pushed `defineDevice` is live on the next turn, a device-trigger is published only by `--auto-deploy` (never run) — `/lua-devices` handles both.
- **Web apps** (lua-cli ≥ 3.42.0; `${CLAUDE_PLUGIN_ROOT}/lib/knowledge/web-apps.md` §7): `lua push webapp` builds the pages with the app's own Vite and stages a version. ⏳ From lua-cli **3.49.0** `lua deploy webapp` is a **scoped promote** like `webhook` — only that app's pin moves, nothing else staged goes live, a failed deploy changes nothing, and it never switches a disabled app on (it prints `⚠️ Web app <name> is switched off: …`; quote it in the report). **Below 3.49.0** there is no `webapp` deploy type and an **agent version** makes it live — `lua version create` snapshots the **latest pushed** version of every primitive (lua-agents `agent-version.service.ts`), not only the app's — so there a `webapp` target must never carry other staged changes live: diff before promoting (step 5). Read `lua --version` once before step 5 to pick the path. A version that pins a web app rolls it back when an earlier version is promoted.
- `lua deploy all --force` deploys the latest version of everything and ignores `--set-version` and `--name` — on a versioned agent it has the same skill/persona snapshot problem, so route `all` through an agent version there.
- Every production verb is blocked in bare form by the `confirm-deploy` hook; the `LUA_DEPLOY_CONFIRMED=1` prefix is what the hook and the permission template's allow rules accept. If the prefixed command is nevertheless denied by the permission layer, the user's own `.claude/settings.json` (or a global one) carries a deny/ask rule such as `Bash(lua deploy*)` or `Bash(lua *)` that Claude Code evaluates past the env prefix — report that rule and stop; do not try another spelling. Never use `--auto-deploy`.
- `lua status --json --ci` reports per-primitive `diffs[].status` ∈ `synced | ahead | behind | not deployed` and `orphans[]`.
Expand Down Expand Up @@ -41,10 +41,10 @@ lua-cli facts this sequence relies on (verified against 3.33.0; details in `${CL
- `workflow` → `LUA_DEPLOY_CONFIRMED=1 lua workflows deploy <name> -v <v|latest>`; if `notes` asks for schedules/triggers to be enabled, then `LUA_DEPLOY_CONFIRMED=1 lua workflows activate <name>`
- `mcp` → `LUA_DEPLOY_CONFIRMED=1 lua mcp activate <name>`
- `voice agent-version` → `lua version create --ci -m "<notes or 'deploy via plugin'>"` (⏳ 3.44.0, `agent-version`: add `--persona-version <n>` when the stage-all printed `Persona vN staged`), read the new version number `N` from the output, then `LUA_DEPLOY_CONFIRMED=1 lua version promote N`
- `webapp` → `lua version create --ci -m "<notes or 'web app <name> via plugin'>"`, read `N`. **Versioned agent** (step 3 found an active version `A`): `lua version diff A N --json` — if anything other than web apps differs (a skill, workflow, the persona pin, the model…), or the diff fails, **do not promote**: abort with "Agent version N would also make live: <primitives and versions>. Ship them together with `/lua-deploy agent-version`, or deploy those first; version N stays staged (unpromoted)." Only web-app changes ⇒ `LUA_DEPLOY_CONFIRMED=1 lua version promote N`. **Unversioned agent**: promoting N makes it the agent's first version — read `lua version show N --json`, promote, and list in the report every primitive version N pins (they are now what runs), and say that the agent is versioned from now on (skills ship through agent versions, the persona through `--persona-version`)
- `webapp` → ⏳ **lua-cli 3.49.0 or later**: `LUA_DEPLOY_CONFIRMED=1 lua deploy webapp --ci --name <name> --set-version <v|latest> --force` (scoped promote — immediate and snapshot-consistent on any agent, exactly like `webhook`). A 503 `SCOPED_PROMOTE_FAILED` or 409 `SCOPED_PROMOTE_CAP_EXHAUSTED` left the app unchanged — report it as a failed deploy (for the 409, the agent is at its version cap: prune with `lua version delete`). **Below 3.49.0**: `lua version create --ci -m "<notes or 'web app <name> via plugin'>"`, read `N`. **Versioned agent** (step 3 found an active version `A`): `lua version diff A N --json` — if anything other than web apps differs (a skill, workflow, the persona pin, the model…), or the diff fails, **do not promote**: abort with "Agent version N would also make live: <primitives and versions>. Ship them together with `/lua-deploy agent-version`, or deploy those first; version N stays staged (unpromoted)." Only web-app changes ⇒ `LUA_DEPLOY_CONFIRMED=1 lua version promote N`. **Unversioned agent**: promoting N makes it the agent's first version — read `lua version show N --json`, promote, and list in the report every primitive version N pins (they are now what runs), and say that the agent is versioned from now on (skills ship through agent versions, the persona through `--persona-version`)
A 409 `WORKFLOW_DYNAMIC` means the workflow was composed in chat and cannot be deployed from the CLI — report it. A 403 means the credential's role/agent scope does not allow publishing — point at `/lua-auth`.
6. **Smoke check** — `lua logs --ci --type all --limit 30 --json` once; it returns `{ logs, pagination }`. Count entries with `subType === 'error'` (there is no `level` field) whose `timestamp` is within the last 5 minutes and list their `message` + `metadata.logSource`/`metadata.primitiveName`. For a workflow deploy also run `lua workflows versions <name>` and confirm the active marker moved. Then `mcp__plugin_lua-agent-builder_lua-platform__get_deployment_status` (agent id from `lua.skill.yaml`) to confirm `activeVersion` for the shipped primitive. The `post-deploy-smoke` hook pings production chat separately; both are defence in depth.
7. **Report**: what was pushed (version), what went live (command + version), smoke result, for every web app in the promoted version (`webapp`, `agent-version`, `all` on a versioned agent — the `web-app` entries of `dist-v2/manifest.json`) the line `<name> → https://workspace.heylua.ai/apps/<agent.agentId from lua.skill.yaml>/<name>` (browser only; the desktop app does not show web apps yet), and the rollback line: `LUA_DEPLOY_CONFIRMED=1 lua deploy <type> --ci --name <n> --set-version <previous> --force` (scoped-promote kinds, or a skill on an unversioned agent), `LUA_DEPLOY_CONFIRMED=1 lua deploy persona --ci --set-version <previous> --force` (the persona on an unversioned agent, and on any agent below lua-cli 3.44.0, where promote does not roll a persona back; ⏳ from 3.44.0 a versioned agent's persona rolls back with the promote below), `… lua workflows deploy <n> -v <previous>`, `… lua version promote <previous>` (anything shipped as an agent version — note it also resets every skill pin to that snapshot), or for an MCP activation plain `lua mcp deactivate <n>` (not a gated verb — it sits in the `ask` tier and prompts once; never write it with the prefix) (the user runs it through /lua-deploy).
7. **Report**: what was pushed (version), what went live (command + version), smoke result, for every web app that went live (`webapp` — just that app on 3.49.0+; `agent-version`, `all` on a versioned agent — the `web-app` entries of `dist-v2/manifest.json`) the line `<name> → https://workspace.heylua.ai/apps/<agent.agentId from lua.skill.yaml>/<name>` (browser only; the desktop app does not show web apps yet), and the rollback line: `LUA_DEPLOY_CONFIRMED=1 lua deploy <type> --ci --name <n> --set-version <previous> --force` (scoped-promote kinds — a web app on 3.49.0+ included — or a skill on an unversioned agent), `LUA_DEPLOY_CONFIRMED=1 lua deploy persona --ci --set-version <previous> --force` (the persona on an unversioned agent, and on any agent below lua-cli 3.44.0, where promote does not roll a persona back; ⏳ from 3.44.0 a versioned agent's persona rolls back with the promote below), `… lua workflows deploy <n> -v <previous>`, `… lua version promote <previous>` (anything shipped as an agent version — note it also resets every skill pin to that snapshot), or for an MCP activation plain `lua mcp deactivate <n>` (not a gated verb — it sits in the `ask` tier and prompts once; never write it with the prefix) (the user runs it through /lua-deploy).

## Constraints

Expand Down
Loading
Loading