# Skill Release Plus

> Multi-hub skill publisher. Sign → Audit (placeholder) → Pack (clean tar.gz with exclude rules) → Publish to clawhub.com, skillhub.cn (Tencent Cloud), or GitHub Releases, all in one command. Pluggable adapter framework — add your own hub via a user-hook script (subprocess + JSON envelope). Single source of truth: one exclude.json, one signing pass, one package, fan out to N targets. Default target: clawhub only. Use --target all for all three real hubs, or --target user-hook:./my-script.sh for custom destinations. Triggers on: publish skill, release skill, push to skill hub, ship skill to multiple hubs, 发布 skill, 上传到 clawhub, 发布到 skillhub.cn.

- Skill: `songhonglei/skill-release-plus` (Agent Skill, multi-file: 12 files)
- Install (CLI): `npx skillmds@latest add songhonglei/skill-release-plus`
- Raw SKILL.md: https://api.skillmd.com/api/skills/songhonglei/skill-release-plus/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: Songhonglei (https://skillmd.com/u/songhonglei)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/songhonglei/skill-release-plus

---


# skill-release-plus

**Version**: 1.0.3  
**Author**: Evan Song <[github.com/Songhonglei](https://github.com/Songhonglei)>
**Repo**: <https://github.com/Songhonglei/build-better-skills/tree/main/skills/skill-release-plus>
**License**: MIT (this repo) — clawhub mirror auto-uses MIT-0 (platform-enforced)  
**Part of**: [`build-better-skills`](https://github.com/Songhonglei/build-better-skills) suite — see [Stages](https://github.com/Songhonglei/build-better-skills#stages) for the lifecycle map.

Sign → Pack → Publish to **multiple skill hubs in one command**. Supports
`clawhub.com`, `skillhub.cn` (Tencent Cloud), `GitHub Releases`, and any
custom hub via a user-hook script.

---

## Quick start

```bash
# Default: publish to clawhub only
python3 scripts/release.py --slug my-skill -m "first release" --version 1.0.0

# Publish to all three real hubs at once
python3 scripts/release.py --slug my-skill -m "ship it" --version 1.0.0 --target all

# Custom hub via user hook
python3 scripts/release.py --slug my-skill -m "v1" --version 1.0.0 \
    --target user-hook:/path/to/my-publish.sh

# Check token readiness before publishing
python3 scripts/release.py --check --target all

# Dry-run (sign + pack, skip publish)
python3 scripts/release.py --slug my-skill -m "test" --version 1.0.0 --dry-run

# Inspect current exclude rules
python3 scripts/release.py --show-exclude
```

---

## Supported targets

| Target | Status | Auth | Notes |
|---|---|---|---|
| `clawhub` | ✅ | `SRP_CLAWHUB_TOKEN` (env) or `clawhub login` | Wraps the [clawhub CLI](https://www.npmjs.com/package/clawhub) |
| `skillhub-cn` | ✅ | `SRP_SKILLHUB_CN_TOKEN` (env) | Tencent Cloud — POST `/api/v1/community/skills/publish` (multipart). Rate-limit 3 publish/min/token. **Auto-retries on 429.** |
| `github-release` | ✅ | `SRP_GITHUB_TOKEN` (env) | Pure `git` + GitHub REST API (no `gh` CLI required). Supports `owner/repo[/subdir]` for monorepo layouts. |
| `user-hook:<path>` | ✅ | Hook-defined | Your script receives metadata via env vars + JSON stdin, returns `{"ok": true, "url": "..."}` on stdout |

> **Not supported**: `cn.clawhub-mirror.com` (ByteDance Volcano) is a **read-only public mirror** of clawhub.com — publishing to clawhub automatically syncs there. There is no separate publish endpoint.

---

## Installation

```bash
# Install Python 3.8+ (standard library only, no pip install needed)
git clone https://github.com/Songhonglei/build-better-skills.git
cd build-better-skills/skills/skill-release-plus

# Or install via clawhub:
clawhub install skill-release-plus
```

**Dependencies**:
- Python 3.8+ (standard library only — `urllib`, `subprocess`, `tarfile`, `json`, `argparse`, `dataclasses`, `pathlib`)
- `git` 2.x (for `github-release` target only)
- `clawhub` CLI (for `clawhub` target only — auto-detected from `$PATH`)
- `skill-sign` (optional — for content signing; skipped if not installed)
- `PyYAML` (optional — improves SKILL.md frontmatter parsing for edge cases; stdlib fallback handles `description: "..."` single-line and `description: >` folded scalar without it)

---

## Configuration

Resolution priority (highest → lowest):

1. **CLI args** (`--slug`, `--target`, `--version`, etc.)
2. **Process env vars** (`SRP_*` private namespace)
3. **Project-local `./.env`**
4. **Global `~/.config/skill-release-plus/.env`**
5. **Missing required key → onboarding prompt** (interactive)

### Environment variables (all `SRP_*` prefixed)

| Variable | Required for | Description |
|---|---|---|
| `SRP_CLAWHUB_TOKEN` | clawhub target | clawhub API token (or run `clawhub login`) |
| `SRP_SKILLHUB_CN_TOKEN` | skillhub-cn target | Tencent skillhub.cn personal token (starts with `skh_`) |
| `SRP_GITHUB_TOKEN` | github-release target | GitHub PAT with `repo` scope |
| `SRP_GITHUB_OWNER` | github-release (optional) | Default repo owner (defaults to authenticated user) |
| `SRP_GITHUB_REPO` | github-release (optional) | Default repo name (defaults to slug) |
| `SRP_GITHUB_BRANCH` | github-release (optional) | Default branch (default: `main`) |
| `SRP_GITHUB_BASIC_AUTH_HEADER` | github-release (optional) | If `true`, use HTTP Basic Auth header instead of URL-embedded token |
| `SRP_DEFAULT_TARGETS` | optional | Comma-separated default target list (default: `clawhub`) |
| `SRP_OUTPUT_DIR` | optional | Package output directory (default: `<cwd>/output/skill-release`) |
| `SRP_SIGN_OPTIONAL` | optional | If `true`, skip signing when `skill-sign` not installed (default: `true`) |
| `SRP_SIGN_SCRIPT` | optional | Explicit path to `sign.py` (overrides auto-detection of `skill-sign`) |

---

## Architecture

```
release.py (dispatcher, 352 LOC)
  ├─ _lib_config.py         (4-layer config + parse_targets, 289 LOC)
  ├─ _lib_package.py        (sign / pack / exclude rules / multipart, 305 LOC)
  ├─ _lib_adapters_base.py  (BaseAdapter ABC, 98 LOC)
  └─ _adapter_*.py:
       ├─ clawhub          (CLI wrap, 185 LOC)
       ├─ github-release   (git + GitHub REST API, 392 LOC)
       ├─ skillhub-cn     (Tencent Cloud HTTP/multipart, 308 LOC)
       └─ user-hook       (subprocess + JSON envelope, 140 LOC)
```

**Design principles**:

- **Single source of truth**: One `config/exclude.json`, one signing pass, one tar.gz, fan out to N targets
- **Adapter independence**: Each adapter is a separate Python module subclassing `BaseAdapter`; no cross-adapter coupling
- **Fail-open by default**: One target failing does not block others (dispatcher reports per-target status)
- **Protocol-agnostic base**: BaseAdapter doesn't assume HTTP / multipart / git — adapters own their protocol

---

## Writing a custom adapter (user-hook)

Create an executable script:

```bash
#!/bin/bash
# my-publisher.sh
set -e

# Hook receives:
#   $SRP_HOOK_SLUG, $SRP_HOOK_VERSION, $SRP_HOOK_CHANGELOG,
#   $SRP_HOOK_TAR_PATH, $SRP_HOOK_SKILL_DIR, $SRP_HOOK_DISPLAY_NAME

curl -sf -F "package=@$SRP_HOOK_TAR_PATH" \
     -F "version=$SRP_HOOK_VERSION" \
     "https://my-private-hub.example.com/publish"

# Print JSON envelope on stdout for the dispatcher to parse
echo '{"ok": true, "url": "https://my-private-hub.example.com/skills/'"$SRP_HOOK_SLUG"'"}'
```

Then invoke:

```bash
chmod +x my-publisher.sh
python3 scripts/release.py --slug my-skill -m "v1" --version 1.0.0 \
    --target user-hook:./my-publisher.sh
```

---

## Exclude rules

Default `config/exclude.json` strips:

- VCS: `.git`, `.svn`, `.hg`
- IDE: `.idea`, `.vscode`, `.DS_Store`, `Thumbs.db`
- Build artifacts: `__pycache__`, `*.pyc`, `node_modules`, `dist`, `build`
- Secrets: `.env`, `.secrets`, `*.pem`, `*.key`, `.token`
- Test/dev: `tests/__pycache__`, `.pytest_cache`, `.tox`
- See full list: `python3 scripts/release.py --show-exclude`

---

## Command reference

```
usage: release.py [-h] [--slug SLUG] [--changelog CHANGELOG]
                  [--version VERSION] [--display-name DISPLAY_NAME]
                  [--skill-dir SKILL_DIR] [--target TARGET] [--dry-run]
                  [--show-exclude] [--check]

options:
  -h, --help            show this help message and exit
  --slug SLUG           skill slug (lowercase, digits, hyphens)
  --changelog, -m       release changelog
  --version VERSION     explicit version (semver); default: auto-bump patch
  --display-name        human-readable display name (default: slug)
  --skill-dir SKILL_DIR  path to skill source directory (default: ./<slug>)
  --target TARGET       comma-separated targets, or "all". Default: clawhub.
                        Special: user-hook:<path> for custom hooks.
  --dry-run             sign + pack but skip publish
  --show-exclude        print current exclude rules and exit
  --check               check token readiness for selected targets
```

Exit codes:
- `0` — all targets succeeded
- `1` — at least one target failed (others may have succeeded)
- `2` — invalid arguments or config error

---

## Triggers

This skill should be used when the user says:

- "publish skill X to clawhub / skillhub.cn / GitHub"
- "ship my skill to multiple hubs"
- "release X.Y.Z of my skill"
- "发布 skill 到 clawhub"
- "把 skill 推到 skillhub.cn"
- "skill 同时发到三个 hub"
- "用 user-hook 发到我自己的 hub"

---

## Limitations

- **`skillhub-cn` rate limit**: 3 publish/minute/token. Dispatcher waits and retries on 429.
- **`github-release` requires explicit `--version`**: No auto-tag-bump (would require parsing existing tags reliably across owner/repo combos).
- **`skillhub-cn` requires `description`** in SKILL.md frontmatter (≤1024 bytes). YAML folded (`>`) scalar is supported.
- **No rollback**: If 2/3 targets succeed and 1 fails, you must manually clean up the 2 successes or accept partial state.

---

## License

MIT (this repo). See `LICENSE`.

Note: when this skill is published to clawhub.com, the platform enforces MIT-0
license metadata on hub mirror — original repo MIT license is unchanged.

