| name | publish-skill-to-github |
| description | Package an existing Claude Code skill as a polished, propagation-ready open-source GitHub repo. Use whenever the user says "put this skill on GitHub", "open-source my skill", "给我这个 skill 开源", or wants a skill repo that mirrors the structure and presentation of high-star reference projects in the Claude-skill ecosystem. Handles the pre-publish secret scan, de-personalization triage, Agent Skills spec compliance, repo layout, README, licensing, versioning, plugin-marketplace distribution, and post-push verification. Not about writing the skill itself — about making an already-working skill safe and spreadable. |
| license | MIT |
| compatibility | Requires git, gh (authenticated), and gitleaks. Designed for Claude Code on macOS or Linux. |
| metadata | {"author":"Yijia Duan","version":"2.2.0"} |
publish-skill-to-github — from working skill to repo people can safely clone
Getting a skill to work, getting it to be safe to publish, and getting it to spread are three different crafts. This skill covers the last two.
Two reframes it enforces:
SKILL.md is written for an AI. README.md is written for a human. Different audiences, different voices. A good SKILL.md is terse and assumes the reader is an agent following orders. A good README is evocative and assumes a developer deciding in 30 seconds whether to clone. Write twice; never let one contaminate the other.
Your working copy is the repo. Skills live at ~/.claude/skills/<name>/, and that directory becomes the published repo. So every personal path, host and habit that made the skill useful to you is also about to become public — and to break for everyone else. De-personalization is not a nicety; it is the difference between a tool and a diary.
Scope. Publish-time concerns only: safety gates, repo layout, README, licensing, versioning, distribution, verification. Not in scope: writing the skill's recipe (that is the skill's own job), or ongoing maintenance.
When to trigger
Use when the user says any of:
- 「把这个 skill 放到我的 github 开源」/ "open-source this skill"
- 「给这个 skill 写个 README,参考 XXX 项目的结构」
- "publish this skill as a public repo"
- 「这个 skill 能发出去吗」/ "is this safe to open-source?" — the triage in Phase 1 answers exactly this
Do not use when:
- The skill has not been validated end-to-end on the user's own machine. Ship working things.
- The user wants a multi-skill monorepo. This is one-repo-per-skill. (For a shared index across many skills, see the marketplace-repo note in references/TEMPLATES.md.)
- The skill's value is the user's private infrastructure. Phase 1 will tell you; some skills are correctly unpublishable.
Hard rules
- Run
scripts/preflight.sh first, and again against the clone. Never judge safety by eye. A blocking finding means stop.
- Scan history, not just files. A credential deleted from a file still lives in every commit that contained it.
gitleaks git <dir> --log-opts="--all".
- If a real secret was ever committed: rotate first. Rotation is the fix. History rewriting is optional cleanup afterwards, and never a substitute — assume anything ever pushed was scraped.
- Publish
--private first, verify, then flip public. Crawlers mirror new public repos within seconds; one extra command makes every mistake in this list recoverable.
- Never
git add . or git add -A. Stage by name — the last line of defence against something untracked sitting in the directory.
- De-personalize before publishing, and never by deleting the user's own working content. Personal conventions move into a git-ignored
SKILL.local.md overlay — see Phase 2. The user keeps their workflow; the public repo stays generic.
- Repo name == directory name == frontmatter
name, and SKILL.md at the repo root. The advertised install is git clone <url> ~/.claude/skills/<name>, so the directory name is the skill's identity. Any drift breaks installation silently.
- Confirm the account and the visibility choice before pushing (Phase 0) — users with several accounts publish to the wrong one.
The pipeline
Phase 0 collect the publisher's inputs (never reuse anyone else's)
Phase 1 triage: is this publishable at all? ──► ⛔ / ⚠️ / ✅
Phase 2 de-personalize (overlay pattern)
Phase 3 preflight gates: secrets · personal · spec · hygiene
Phase 4 layout + reference-repo study
Phase 5 README (references/README-RECIPE.md)
Phase 6 trust artifacts: SECURITY.md, licence, CHANGELOG, version
Phase 7 stage by name, commit
Phase 8 push PRIVATE ──► verify ──► flip public
Phase 9 distribute (references/DISTRIBUTION.md)
Phase 0 — Collect the publisher's inputs
Every value below belongs to whoever is running this skill. Never reuse one from a previous run, from another user, or from this skill's own repo — including the author's. Ask for all of them in one message, then confirm before Phase 8.
| Need | Ask for | Fallback if they don't care |
|---|
| Target account | which GitHub owner to publish under | gh auth status; ask if more than one |
| Repo name | confirm it equals the skill's directory name | the directory name |
| Licence holder | the name to put in LICENSE | git config user.name |
| Commit identity | real email, or <handle>@users.noreply.github.com | git config user.email |
PERSONAL_WORDS | their own names, domains, handles | none — the gate cannot guess these |
| Visibility | private-first; the flip to public is a separate yes | private |
If they'd rather not retype these each time, they keep them in a git-ignored SKILL.local.md (Phase 2). That file is theirs and never ships.
Phase 1 — Triage: is this publishable at all?
Before any work, classify the skill. Read SKILL.md and everything in scripts/, then give one of three verdicts, out loud, with reasons:
| Verdict | Means | Example |
|---|
| ✅ Publishable | No personal data; works for a stranger as-is | a skill that drives a public API with the user's own key from an env var |
| ⚠️ After de-personalization | Useful to anyone, but currently hardcodes personal paths/hosts/names | a skill referencing ~/.config/secrets.env as "my key store", or /Users/<me>/... |
| ⛔ Not publishable | The skill's value is the private infrastructure | a deploy skill whose content is one specific server's IP, root login, key path and directory map |
For ⛔, say so plainly and stop. Offer the alternative: extract the generalizable recipe into a new skill and leave the infrastructure-specific one private. Do not "de-personalize" a ⛔ skill into a hollow shell — a deploy skill with the server removed does nothing.
Also raise, at this phase, anything that needs a human decision rather than a code change:
- Platform ToS. Skills that scrape or bulk-download from a platform need a scope limit and a disclaimer in the README ("your own account only", "personal/offline use"). Name the risk; the user decides.
- Third-party material. Reverse-engineered prompts, another creator's style, borrowed scripts — decide on attribution (
THIRD_PARTY_NOTICES.md, as Anthropic's own skills repo does) and on whether the material ships at all.
- Identity.
git config user.email lands in every commit — settle it in Phase 0.
- Maintenance appetite. Issues will arrive. Either say "personal project, no support guarantee" in the README, or turn issues off. Decide now.
Phase 2 — De-personalize with the overlay pattern
For every ⚠️ finding, apply in this order of preference:
- Generalize it.
~/.config/secrets.env(my key store) → "keep the keys in a file outside the repo, e.g. ~/.config/secrets.env, chmod 600". Same instruction, no ownership claim.
- Variable-ize it.
/Users/<me>/OSSDrive → /Users/<username>/OSSDrive, plus "resolve it with echo $HOME rather than hardcoding".
- Move it to the overlay. Anything genuinely specific to one person's setup — which host to deploy to, which bucket to upload to, which sibling skill to call — goes into a git-ignored
SKILL.local.md, and the corresponding SKILL.md step gains a conditional pointer to it.
- Neutralize the voice. 「 不做 QA,你做」→ "You do the QA, not the user." Second person generalizes; a name does not. Also drop gendered pronouns about the user — you do not know them.
The overlay template and the exact SKILL.md wording are in references/TEMPLATES.md. Add SKILL.local.md and *.local.md to .gitignore before creating the file, and verify with git check-ignore -v SKILL.local.md.
Watch for the subtler cases the eye skips: cross-skill calls (~/.claude/skills/other-skill/scripts/x.sh — that path exists on one machine), local research files referenced as if they ship, example values built from the author's own names (<yourname>-test-1), and personal domains in code comments.
Phase 3 — Preflight gates
PERSONAL_WORDS='<name>|<domain>\.com|<handle>' scripts/preflight.sh ~/.claude/skills/<NAME>
Four gates: secrets (tree + full history), personal data, Agent Skills spec compliance, repo hygiene. Exit 1 on any blocking finding. Read every warning and decide deliberately rather than skimming.
If gitleaks is missing: brew install gitleaks. Public repos also get GitHub's free push protection, but it only recognizes known token formats — it will not notice a server IP, a key path, or a bespoke credential.
Spec limits the gate enforces, from the Agent Skills specification: name ≤64 chars, lowercase alphanumerics and single hyphens, matching the directory; description ≤1024 chars, third person, saying both what it does and when to use it; no angle brackets in frontmatter; SKILL.md body under 500 lines and ~5000 tokens; relative links one level deep and actually resolving.
If SKILL.md is over budget, that is a real finding, not a formality — every token competes with the user's actual conversation. Move detail into references/*.md and link to it from SKILL.md. Add the optional license, compatibility and metadata frontmatter fields while you are there.
Phase 4 — Layout and reference-repo study
Ask the user for a high-star reference repo in an adjacent scene, or propose one. Fetch it with gh, not WebFetch — WebFetch returns HTML-converted-to-markdown and loses the code-block boundaries you need to read structure faithfully.
REF="<owner/repo>"
gh api repos/$REF --jq '{stars:.stargazers_count, license:.license.key, desc:.description}'
gh api "repos/$REF/git/trees/main?recursive=1" --jq '.tree[] | "\(.type)\t\(.path)"'
gh api repos/$REF/contents/README.md --jq '.content' | base64 -d | head -200
Read it for structure, not vibes: heading order, badge count, whether install shows both scopes, code-block density, table usage, voice, and what sits at root that you had not considered.
Then make a deliberate keep/drop call per directory. Conventional layout: scripts/ (executables), references/ (docs loaded on demand), assets/ (templates and data).
Never manufacture empty structure. If the reference has prompts/, tools/ and exes/ and your skill is one shell script, do not create hollow directories to look sophisticated. Reviewers spot it instantly. Instead, name the omission in the README's layout section and frame it as the deliberate choice it is.
Phase 5 — README
The README is a separate authorial act, not SKILL.md with badges. Full method in references/README-RECIPE.md — read it before writing.
The short version: a first-screen contract (what it does / how to install / what to say, all above the fold), then five required sections (header, install, usage, limits, licence), then tier-2 sections only when there is real content for them. Hard caps: 150 lines, 4 badges, bad-fit content within the first 60 lines.
Two things that are close to mandatory and that most skill READMEs lack:
- A safety table — what the skill reads, writes, and connects to on the reader's machine. In an ecosystem where Snyk found flaws in 36.8% of 3,984 scanned skills, readers arrive suspicious and correctly so. Eight lines of disclosure converts that into trust.
- An update line —
cd ~/.claude/skills/<NAME> && git pull. Cloned skills otherwise sit frozen forever, because nothing tells the user there is anything to pull.
For Chinese READMEs, run the typography pass: python3 scripts/fix_cjk_punct.py --write README.md. It converts halfwidth ,:;() to fullwidth in Chinese prose only, and leaves code blocks, YAML frontmatter, URLs, inline code and English sentences alone. Review the diff — it is a heuristic, not a proof. Never let it touch frontmatter; the description is machine-read and its trigger list is delimiter-sensitive.
Phase 6 — Trust artifacts, licence, version
- LICENSE — MIT by default (most of the ecosystem; Anthropic's own skills are Apache-2.0, which adds patent protection). Filename
LICENSE, no extension — GitHub's detection is pickiest about that form. Year plus the licence holder from Phase 0. Verify the repo sidebar shows it.
- SECURITY.md — the "what it does on your machine" declaration, what it deliberately does not do (no
curl | bash, no fetching instructions from a URL at runtime), and how to audit it. Template in references/TEMPLATES.md.
- CHANGELOG.md + version — Keep a Changelog format, SemVer, and
metadata.version in the frontmatter kept in sync. Without this, users cannot tell whether a git pull matters.
.gitignore — use the template. Note *.env, not .env: a bare .env line does not match secrets.env, which is the most common way a credential file slips through a .gitignore that looked right.
Phase 7 — Stage by name, commit
git add SKILL.md README.md LICENSE .gitignore SECURITY.md CHANGELOG.md
git add scripts/ references/
git status --short
Commit message format and a worked example in references/TEMPLATES.md: imperative subject under 70 chars, a paragraph on what and who for, a paragraph enumerating the pitfalls the skill encodes, and a line on layout convention.
Phase 8 — Push private, verify, then flip public
gh auth status
gh repo create <NAME> --private --source=. --description "<one-line pitch>" --push
Then verify — do not trust that it worked because gh did not error:
WORK=$(mktemp -d)
git clone --depth 1 https://github.com/<USER>/<NAME>.git "$WORK/check"
ls -a "$WORK/check"
head -8 "$WORK/check/SKILL.md"
PERSONAL_WORDS='<...>' scripts/preflight.sh "$WORK/check"
rm -rf "$WORK"
That last line is the one that matters: run the gates against the clone, not your working directory. It is the only check that sees exactly what a stranger receives.
Then look at the rendered README in a logged-out browser. Then flip:
gh repo edit <USER>/<NAME> --visibility public --accept-visibility-change-consequences
Phase 9 — Distribute
Topics immediately (not later), then release + zip, then marketplace.json, then awesome-list PRs. Channels, discovery targets and the exact order are in references/DISTRIBUTION.md.
The one-line summary: git clone is the baseline, but .claude-plugin/marketplace.json is the only channel that delivers your future fixes to existing users automatically — and it is the one most skill repos skip.
Things that will bite you
Fifteen failure modes from real publish runs — gitignore patterns that do not match what you think, history that keeps deleted secrets, links that resolve only on your machine, and the irreversibility of --public. Read references/PITFALLS.md before your first publish.
Files in this skill
SKILL.md — this playbook.
scripts/preflight.sh — the four publish gates. Run it first, and again against the clone.
scripts/validate_marketplace.py — schema validator for .claude-plugin/marketplace.json; a local stand-in for claude plugin validate when the CLI isn't on PATH.
scripts/fix_cjk_punct.py — Chinese punctuation normalizer for prose; skips code, frontmatter, URLs and English sentences.
references/README-RECIPE.md — the tiered README method, typography rules, and a copy-paste skeleton.
references/TEMPLATES.md — .gitignore, the overlay pattern, frontmatter, SECURITY.md, CHANGELOG.md, marketplace.json, CI workflow, commit message, gh cheat sheet.
references/DISTRIBUTION.md — the four install channels and where discovery actually comes from.
references/PITFALLS.md — the failure modes, in full.
Dependencies
git, with user.name and user.email configured
gh, authenticated as the Phase 0 owner
gitleaks (brew install gitleaks) — required, not optional
python3 — for the Chinese typography pass
- Optional:
skills-ref (uv tool install skills-ref) for independent spec validation; the claude CLI for claude plugin validate (absent on a desktop-app-only install — scripts/validate_marketplace.py covers the schema without it)
- An end-to-end-validated skill at
~/.claude/skills/<NAME>/SKILL.md
Related skills
-
skill-fork-playbook (a separate skill, if installed) — forks a high-star skill's logic into a scenario variant. This skill forks the packaging and safety layer instead. They compose.
Deliberately not a relative link: ../skill-fork-playbook/SKILL.md resolves in the author's ~/.claude/skills/ but not inside a fresh clone, so it would ship as a broken link. Cross-skill references are prose, never links.