> ## Documentation Index
> Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Publish an agent template

> Create a template from a working agent, draft and author its template: section, publish a version, and roll it out to consenting installs

After this guide, one promoted version of your agent is a published [agent template](/concepts/agent-templates) that your other clients, or other organizations, can install. To share one skill rather than a whole agent, [publish a marketplace skill](/marketplace/skills) instead.

*Verified against lua-cli 3.33.0.*

**Before you begin**

* A project connected to the source agent (`lua.skill.yaml` carries its `agentId`) and a signed-in CLI. See [Install](/get-started/install).
* Everything pushed and an active [agent version](/concepts/releases-and-versions) on the source agent: `lua push all --ci --force` (the source backup `lua version create` needs), `lua version create`, then `lua version promote <n>`. Publishing freezes that version.
* No voice binding and no device triggers on the source agent; such a publish is refused with `UNSUPPORTED_SOURCE`.
* Every secret read through `env('KEY')`, never a literal in code; publishing scans the frozen code for credentials.

<Steps>
  <Step title="Create the template">
    `create` registers the listing against the current agent and prints its id. Under `--ci`, pass all four flags: the CLI prompts for any missing one, including the optional `--description`, and `--ci` refuses prompts.

    ```bash theme={null}
    lua marketplace template create --name support-desk --display-name "Support Desk" \
      --description "Tier-1 support with ticket triage" --visibility private --ci
    ```

    The listing is private (your organization only) unless you pass `--visibility public`. Each version of a public template is reviewed; other organizations are offered approved versions only, while yours can install a pending one to test it.
  </Step>

  <Step title="Draft the template: section">
    `draft` asks the server to compose the `template:` section of `lua.skill.yaml` from the active agent version and writes it.

    ```bash theme={null}
    lua marketplace template draft --template-id <template-id> --ci
    ```

    It writes six sections: `connections`, `personaTemplate`, `triggerPresets`, and `paramsMeta` inferred from the agent's code, persona, jobs, and latest env contract, plus `onInstall` and `onUninstall` copied from your latest published version. It never composes hooks or `installPolicy`, and replaces the section as a whole, so re-add a hand-written `installPolicy` after every draft. A re-run merges additively and prints what it added and kept; `--force` replaces your edits.

    An agent with no active version is refused with exit 10: ``✖ http_400: Source agent has no active version — run `lua version create` + `lua version promote` first``.
  </Step>

  <Step title="Review the template: section">
    Edit the drafted section. Every installer-facing entry needs `displayName` and `description`; keys are the identity installers keep their settings under, so keep them stable across versions. `draft` declares a persona var for every `{{NAME}}` token (upper-case names) in the promoted persona, as a required string with an empty `description`: write `{{COMPANY_NAME}}` into the source persona before `lua version create` and `lua version promote`, or add the token and its var to the drafted `personaTemplate` by hand, then fill in each `description`. Give a required var a `default` if installers use the CLI, which cannot answer persona variables.

    ```yaml lua.skill.yaml theme={null}
    template:
      personaTemplate:
        template: |
          You are the support desk for {{COMPANY_NAME}}. Answer from the
          customer's order history, escalate refunds you can't approve, and
          keep replies short. Tone: {{TONE}}.
        editable: true
        vars:
          - name: COMPANY_NAME
            displayName: Company name
            description: Used in greetings and signatures.
            type: string
            required: true
            placeholder: Acme Inc.
          - name: TONE
            displayName: Tone of voice
            description: How the agent phrases its replies.
            type: enum
            enum: [friendly, formal]
            default: friendly
      connections:
        - key: crm
          capability: crm
          required: true
          displayName: Your CRM
          description: Used to read customers and log escalations.
          platforms:
            - type: hubspot
          boundSkills: [crm-sync]
      triggerPresets:
        stale-ticket-sweep:
          enabled: true
          displayName: Sweep stale tickets
          description: Nudges assignees about tickets idle for a day.
          editableParams:
            - path: preset.seconds
              label: Sweep every
              unit: minutes
              min: 60
              step: 60
      paramsMeta:
        SUPPORT_EMAIL:
          displayName: Support mailbox
          description: Where escalations are sent.
          type: string
      onInstall:
        tool: setup_ticket_watch
        instruction: Report which mailboxes setup_ticket_watch wired, briefly.
      installPolicy:
        perWorkspace: single
    ```

    A publish sends all seven sections; an absent or empty one clears the server's copy. Fields are on the [template manifest](/marketplace/manifest) and, for the hooks and `installPolicy`, [Lifecycle hooks](/marketplace/lifecycle-hooks).
  </Step>

  <Step title="Publish a version">
    `publish` freezes the active agent version (skills, webhooks, jobs, processors, triggers, workflows, model), the `template:` section, and the env contract into an integer-numbered version.

    ```bash theme={null}
    lua marketplace template publish --template-id <template-id> \
      --changelog "First release" \
      --env-contract "SUPPORT_EMAIL=Where escalations are sent" \
      --env-contract "SLACK_CHANNEL?=Optional channel for alerts" --ci
    ```

    `KEY?=` marks an optional variable; the contract carries names and descriptions, never values. On success the CLI prints `✅ Published v1`, `Frozen from agent version v<n>`, an `Auto-update:` line, and a manifest summary; `--json` prints the version instead, with `id`, `version`, `sourceAgentVersion`, `contentHash`, `consentSurfaceHash`, the authored sections, `approvalStatus` on a public template, and `autoApplyRunId` with `autoApplyTargetCount` when a fan-out started. A publish that clears or narrows an authored section prints the consequence and asks to proceed; `--yes` confirms only that prompt, and `--ci` proceeds with the diff printed.

    <Warning>
      Publishing a private template pushes the version onto every consenting install that is behind. Pass `--skip-auto-apply` to hold it back and roll out later with `apply`.
    </Warning>
  </Step>

  <Step title="Verify">
    Read the listing and its versions.

    ```bash theme={null}
    lua marketplace template view --template-id <template-id> --ci
    lua marketplace template versions --template-id <template-id> --ci
    ```

    `view` prints the listing with `Latest version: v1`; `versions` prints one line per version, newest first, as `v1 — from agent v<n> — <date>`. `view --version 1` prints the frozen primitives, env contract, and persona vars.
  </Step>
</Steps>

## Read the Auto-update line

* `pushing to <n> consenting installs — run <id>`: a private template applied the version to every install that allows creator updates and was behind; follow it with `lua marketplace template status`.
* `deferred until this version is approved`: a public template's version is pending review; the push happens at approval.
* `no consenting installs below v<n>`, or `skipped (--skip-auto-apply)`.

A version that changes the consent surface is refused per install with `RECONSENT_REQUIRED` until that installer accepts it, whether or not they opted in; see [What installers agree to and keep](/marketplace/lints-and-consent#what-installers-agree-to-and-keep).

## If it isn't working

<Accordion title="✖ http_400: Source agent has no active version">
  Nothing is promoted. Run `lua push all --ci --force`, `lua version create`, then `lua version promote <n>`, and retry.
</Accordion>

<Accordion title="✖ http_400: SECRET_DETECTED: <pattern> in <kind>/<key>">
  Frozen code or a description matches a credential pattern; the location is named, never the value. Read it through `env('KEY')`, declare `KEY` with `--env-contract`, push, promote, and publish again.
</Accordion>

<Accordion title="✖ http_400: <LINT_CODE>: <field>: <reason>">
  A lint refused the section and took no version number; every violation is in the one message. Codes and fixes are on [Lints and consent](/marketplace/lints-and-consent).
</Accordion>

## Next steps

<Columns cols={2}>
  <Card title="Template manifest" href="/marketplace/manifest">Every field of the section you edited.</Card>
  <Card title="Install and apply" href="/marketplace/install-and-apply">What installers run, and how to roll a version out to a fleet.</Card>
  <Card title="Fleet health" href="/marketplace/fleet-health">Per-install workflow health after the rollout.</Card>
  <Card title="lua marketplace" href="/reference/cli/marketplace">Every template action, flag, and exit code.</Card>
</Columns>
