--- name: publish-ability description: Publish a skill, scene, MCP server, plugin, or bundle to the Vetta ability marketplace. Use when the user asks to upload/publish/submit an ability, put a plugin on the marketplace, share a skill with other users, or migrate an existing package into Vetta. metadata: version: 2.1.0 author: Vetta category: 开发 --- # Publish an Ability to the Vetta Marketplace Submitting is a **local** action: it reads an archive from the user's disk, so it runs through the bundled script rather than a tool call. ```bash node "$SKILL_DIR/scripts/publish.mjs" --input /abs/path/to/payload.json ``` Checking review status afterwards is a **remote** action — use the `list_my_abilities` tool from the built-in `vetta` MCP server. It is always available and needs no setup. ## Procedure 1. **Determine the type.** Ask the user if it is not obvious from the artifact. | Type | What it is | Physical artifact | | --- | --- | --- | | `skill` | A `SKILL.md` workflow | `.zip` / `.tar.gz` containing `SKILL.md` | | `scene` | A skill installed into the scene directory | same as `skill` | | `plugin` | A Vetta plugin | `.zip` containing `plugin.json` | | `mcp` | One entry in the user's `mcp.json` | none — config only | | `bundle` | A named group of already-published abilities | none — references only | 2. **Source `detail` from the package. Do not invent it.** `name`, `description`, `author`, and `content` are all required and the submission is rejected without them. Read `plugin.json` (`name`/`description`/`author`), `SKILL.md` frontmatter, `README.md`, `LICENSE`. Ask the user for anything you cannot source. For plugins, also list `locales/*.json`: those filenames are the exact locale keys your `detail.i18n` must use, and whatever they already translate is text you should not restate. See [Multi-language](references/payload.md#multi-language). `content` is the detail-page body in markdown — it is what a user reads to decide whether to install. Write what the ability does, when to use it, and any setup it needs. A one-line `content` is not acceptable. 3. **Read the available categories before you set one.** `category` is matched by **name** — a name that matches nothing is not an error, it silently lands in "uncategorised". So never invent one: ```bash node "$SKILL_DIR/scripts/categories.mjs" # human-readable node "$SKILL_DIR/scripts/categories.mjs" --json # machine-readable ``` Put the `name` field verbatim into the payload's `category`. The `i18n` values are display translations only — matching accepts them, but the canonical name is what you should write. Omit `category` entirely if nothing fits. 4. **Write the payload to a JSON file**, then pass it with `--input`. Never try to pass fields as command-line arguments: `content` is multi-line markdown containing backticks, quotes and `$`, and the shell will mangle it. Full field reference: [references/payload.md](references/payload.md) — read it when you need anything beyond the four required `detail` fields. It is also where the **valid icon names** and the meaning of each `showcase.canvas` value are listed; both are closed sets, and a wrong value either fails validation or renders as a fallback. 5. **Dry-run first** when the payload is non-trivial. It runs the full validation without submitting, so you spend no network round-trip on a payload that was never going to pass: ```bash node "$SKILL_DIR/scripts/publish.mjs" --input payload.json --dry-run ``` For `skill`/`scene`/`plugin` this also **opens the archive** and cross-checks the payload against `plugin.json`, `locales/*.json`, and `SKILL.md` frontmatter. That is the only place the "same data, two sources" mistakes surface: a translation key that does not match the package's own locale files, a `slug` or `version` that the server will ignore, a `vetta.json` that your `detail` silently supersedes. None of these fail the upload — they just make part of what you wrote unreachable. Do not skip the dry-run on packaged types. 6. **Submit.** On failure the script returns *all* problems at once — fix the whole list before retrying rather than resubmitting after each single fix. ## Script contract - **stdout is a single JSON object**; exit code 0 = success, 1 = failure. - Failure shape: `{"ok": false, "message": "...", "errors": [...]}` — `errors` is present for validation failures. - Success shape: `{"ok": true, "message": "...", "slug": "...", "version": "...", "review_status": "...", "has_pending": bool, "warnings": [...]}`. - `warnings` are things that did not block the submission but changed what gets published — a hand-written translation overriding the package's own, a `slug` that was ignored, a `vetta.json` that was skipped. Relay them to the user; do not treat the run as clean. - Input may also arrive on stdin (`cat payload.json | node .../publish.mjs`) if that is more convenient than a temp file. - The script reads the login token from `~/.vetta/auth.json` itself. If it reports "未登录", tell the user to log in through the Vetta client — do not attempt to pass credentials yourself. ## Review Submissions from non-administrators enter a review queue and are **not** visible in the marketplace until an administrator approves them. Tell the user this rather than implying the ability is live. Re-submitting an ability that is already published does not disturb it: the new version waits in a pending slot while the marketplace keeps serving the current version, so installed users are unaffected until the update is approved. `has_pending: true` in the result means exactly this. **A re-submission replaces `detail.i18n` wholesale.** Sending only the locale you changed drops every other translation on that entry, including any an administrator added later. Fetch the current state first (`list_my_abilities`) or resend the complete set of locales. You may only submit updates to abilities you own. Submitting to a slug owned by someone else is rejected. ## Verify After a successful run, state the returned `slug`, `version`, and whether the ability is live or pending review. If the run failed, fix every listed error and retry — do not report success. Use `list_my_abilities` to report progress later: `review_status` is `pending`, `approved`, or `rejected` (reason in `review_note`); a non-empty `pending_version` means an update is queued behind the live version.