apigee/devrel/skill-publisher

Claude Code and agent skill by apigee/devrel

Trust tier 2
  • community
LLM review pending
Source check pending

Checking that the SKILL.md file is still in the repository.

Publishes a local skill directory to the API hub catalog and

Install

npx vskill@latest install apigee/devrel/skill-publisher

Semi-extensibleMentions customization, but needs skill-specific knowledge. Read the extensible skills standard

SKILL.md

skill-publisher

⚠️ Runtime requirements (read this first)

This skill requires its bundled venv wrapper. publish.sh internally invokes the four scripts/* Python modules (pack_skill, sign_skill, upload_skill, register_skill), which depend on cryptography, google-auth, requests, and pyyaml. Those are installed into a per-user venv by the installer (bin/install-skill-publisher.sh). They are not available to the system python3.

Always invoke `publish.sh` via the wrapper, never via bare `bash`:

bash
# CORRECT - the wrapper exports PYTHON to point at the venv:
${SKILL_DIR}/bin/run-with-venv.sh <args>
# WRONG - publish.sh will fall back to system python3 which
# will hit ModuleNotFoundError mid-pipeline:
bash ${SKILL_DIR}/scripts/publish.sh <args>

The Command blocks in this file use the wrapper form. Do not "simplify" them — publish.sh reads $PYTHON from its environment and the wrapper is the only thing that sets it correctly. If you invoke publish.sh directly, the four sub-scripts will fail with ModuleNotFoundError.

If the wrapper at ${SKILL_DIR}/bin/run-with-venv.sh is missing, the skill was installed by a pre-v0.1.4 installer. Re-run bin/install-skill-publisher.sh to regenerate the wrapper.


Use this skill when the user asks to "publish a skill", "ship this skill to API hub", "upload and register my skill", "release the skill", or any close paraphrase. This is the single entry point for author-side distribution; it wraps the four scripts/* CLIs from the upstream source repo into one orchestrated sequence with fail-fast semantics.

What this skill does NOT do

  • It does not create the GCS bucket. The bucket must already

exist (see repo README §2).

  • It does not create API hub attribute definitions. Run

python3 -m scripts.update_taxonomy once per project first (see repo README §4).

  • It does not generate or rotate signing keys. The

ed25519 private key path is an input.

  • It does not install the published skill into any local

runtime. That is skill-finder's job.

If any prerequisite is missing the underlying script returns a non-zero exit code and this skill surfaces it verbatim and stops.

Inputs

VariableSourceRequiredNotes
SKILL_SRCUser / agentyesPath to a skills/<name>/ source directory containing manifest.yaml and (at minimum) SKILL.md.
SKILL_OUTOperatornoOutput path for the packed zip. Defaults to /tmp/${name}-${version}.skill derived from the manifest.
GCS_BUCKETOperator (${GCS_BUCKET})yesGCS bucket name (no gs:// prefix). Must match the bucket portion of the manifest's gs_uri.
SIGNING_PRIV_KEYOperator (${SIGNING_PRIV_KEY})yesPath to the raw 32-byte ed25519 private key. See the upstream README's author-flow section for how to generate one.
APIHUB_PROJECTOperator (${APIHUB_PROJECT})yesGCP project hosting the API hub instance.
APIHUB_LOCATIONOperator (${APIHUB_LOCATION})yesAPI hub region (e.g. us-central1).
REPO_ROOTAutonoRoot of the upstream source checkout. Defaults to the current working directory. The four scripts/* modules must be importable from there.
BOOTSTRAPUser / agentnoIf set to 1 or true, runs the bootstrap path that publishes skill-publisher itself. See "Bootstrap mode" below.

Runtime dispatch

This skill ships one orchestration contract that runs on multiple runtimes. Pick the invocation path that matches your runtime; the behaviour and stdout contract are identical.

RuntimeHow you invoke the publish script
OpenCodeUse the !bash`` injection blocks below. OpenCode auto-executes them on SKILL.md load.
Gemini CLIUse your bash tool. Run the matching Command block, substituting ${SKILL_DIR} with the install path (~/.gemini/skills/skill-publisher for global; <project>/.agents/skills/skill-publisher for workspace) and the operator-controlled env vars from the table above.
AntigravitySame as Gemini CLI but ${SKILL_DIR} is ~/.gemini/antigravity/skills/skill-publisher for global installs.
Any other runtimeSame as Antigravity / Gemini CLI: invoke via whatever bash mechanism the runtime provides.

Mode 1: Publish a third-party skill

Use this when the user wants to publish any skill other than skill-publisher itself.

Steps
  1. Resolve the source path. Take the value from SKILL_SRC

verbatim if provided. Otherwise ask the user once: "Which skill directory do you want to publish?" Expect a path like skills/apigee-policy-top10. Do not paraphrase the answer.

  1. Validate the inputs before invoking bash. Verify that the

following are all set and non-empty: SKILL_SRC, GCS_BUCKET, SIGNING_PRIV_KEY, APIHUB_PROJECT, APIHUB_LOCATION. If any is missing, REFUSE to invoke and tell the user which one is missing. Do not guess.

  1. Reject shell metacharacters in `SKILL_SRC`. The path is

interpolated into a bash command via POSIX double quotes, which do not suppress $(...) command substitution or backtick expansion. If SKILL_SRC contains any of ` ` $ \ " ; & | < > ( ) { }` REFUSE to invoke and ask the user to rephrase.

  1. **Invoke the publish script using the runtime path from the

table above.** The exact command is:

Command (OpenCode auto-runs this; on Antigravity / Gemini CLI run it via your bash tool):

!bash ${SKILL_DIR}/scripts/publish.sh \ --src "${SKILL_SRC}" \ --bucket "${GCS_BUCKET}" \ --priv-key "${SIGNING_PRIV_KEY}" \ --project "${APIHUB_PROJECT}" \ --location "${APIHUB_LOCATION}"

  1. Surface the script's stdout verbatim. The script prints

one [skill-publisher] step N/4 ... line per pipeline step, followed by the GCS URI and the API hub resource path on success. Reproduce every line as-is; do NOT summarize. The lines are a stable contract for log parsing.

  1. On non-zero exit, stop. Surface the failing step's stderr

verbatim. Do not retry, do not "fix" the manifest, do not re-sign with a different key. The four underlying scripts have distinct exit codes (see "Exit code mapping" below); use them to give the user an accurate diagnosis.


Mode 2: Bootstrap — publish skill-publisher itself

Use this when the user wants to publish skill-publisher for the first time, or republish it after editing its own source.

The bootstrap problem

skill-publisher cannot use its own installed copy to publish itself: at first run the installed copy does not yet exist in the API hub catalog. Once published, subsequent versions of skill-publisher can be republished by the currently-installed copy — but the very first publish is a chicken-and-egg situation.

Steps
  1. Confirm the user actually means bootstrap. Ask once: "Do

you want to publish skill-publisher itself (bootstrap mode)? This is normally only done by repo maintainers." If the user says no, switch to Mode 1.

  1. Validate the inputs exactly as Mode 1 step 2. SKILL_SRC

for bootstrap is always the path to the in-repo skills/skill-publisher/ directory; default to that if the user does not specify.

  1. Invoke with `--bootstrap`. The bootstrap flag tells

publish.sh to use the repo-local scripts/publish.sh (this file) directly via the four sibling Python modules in ${REPO_ROOT}/scripts/, rather than relying on any installed copy. The exact command is:

Command:

!bash ${SKILL_DIR}/scripts/publish.sh \ --src "${SKILL_SRC:-skills/skill-publisher}" \ --bucket "${GCS_BUCKET}" \ --priv-key "${SIGNING_PRIV_KEY}" \ --project "${APIHUB_PROJECT}" \ --location "${APIHUB_LOCATION}" \ --bootstrap

  1. Surface stdout verbatim (same as Mode 1 step 5).
  1. On success, advise the user to re-run with Mode 1. Tell

them: "skill-publisher is now in the catalog. Any future publish — including republishing skill-publisher itself — can use the installed copy via skill-finder + this skill, without --bootstrap."


Exit code mapping

publish.sh exits with the exit code of the first failing step. The mapping below is the union of the four underlying scripts' exit codes (see each script's docstring for detail):

Exit codeMeaningWhere it comes from
0All four steps succeeded.n/a
1User error: bad CLI args, missing file, invalid YAML, ADC unavailable, or empty required env var.any step
2System error: filesystem write failure, GCS network error, GCS 404 (bucket not found), or API hub network error.pack / upload / register
3Either (a) cryptographic error during sign (priv key unreadable / wrong length) OR (b) GCS IAM denial (403) during upload OR (c) API hub IAM denial (403) during register. The failing-step log line in stdout disambiguates.sign / upload / register
4API hub rejected the registration because the four attribute definitions are not initialised. Run python3 -m scripts.update_taxonomy once and retry.register only
5Packaging policy violation: scripts/common/ has the wrong file set (missing or extra files), or the source dir is missing SKILL.md.pack only (originally exit 3 in pack_skill; remapped here to disambiguate from sign/IAM).

The [skill-publisher] step N/4 FAILED line printed by publish.sh immediately before exit tells the user which step failed and what the underlying script's raw exit code was, so the remap above is auditable, not opaque.


Idempotency

All four underlying scripts are idempotent by design:

  • pack_skill.py writes the zip with sorted entry order, so

sha256(zip) is stable across builds.

  • sign_skill.py produces byte-identical output for identical

inputs (ed25519 is deterministic per RFC 8032; YAML is dumped with sort_keys=True).

  • upload_skill.py overwrites the object at the same name —

re-running uploads the same bytes to the same key.

  • register_skill.py reads first, only POSTs/PATCHes on diff;

re-running with no changes makes only GET calls.

Therefore re-running publish.sh with the same inputs is safe and observable as zero mutating API calls after the first successful publish. The skill does NOT need a "dry-run" mode of its own; pass --dry-run through to register_skill if you want to skip the registration step entirely (currently not wired — file an issue if needed).


Security notes

  • The ed25519 private key path is the only secret this skill

touches. It is read by sign_skill.py, never logged, never uploaded. The key file should have mode 0600.

  • The signature is computed over the canonical manifest bytes

(see scripts/common/canonical.py). Any post-signing edit to the manifest invalidates the signature; skill-finder will refuse to install the skill.

  • The signing_key_id field is the sha256 of the public key,

not the private key. It is safe to publish. skill-finder cross-checks it against installed trust roots (keys/*.pem); a manifest signed by a key not in the trust root is rejected client-side. Multiple keys can be installed side-by-side to support multi-publisher orgs and zero-downtime key rotation.

  • GCS uploads use ADC. The operator must have

storage.objects.create on the target bucket. The skill does not run a permission pre-flight; it relies on the upload itself to surface 403 as exit code 3.


Common rationalizations

This is the start of the file. Read the full SKILL.md on GitHub.

Security

  • Security scanPassed, 100 of 100Pattern checks on the skill's files.
  • Model reviewNot runA model review is scheduled for the next rescan.

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
220
Repository forks
173
7-day trend score
27

Stars and forks are for the whole GitHub repository, not this skill alone. Not refreshed yet.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/apigee/devrel/skill-publisher/badge)](https://verified-skill.com/skills/apigee/devrel/skill-publisher)

Something wrong with this skill or its page? Tell us.