From publish-skill
Publish a Claude Code skill to GitHub as a polished, adoptable open-source repo, AND diagnose `claude plugin install` failures on a published skill. Use when the user says "publish this skill", "put this on GitHub", "share this skill", "release this skill publicly", "open source my skill", "make this skill installable", "create a GitHub repo for my skill", "package this skill for the marketplace", or wants to update an existing published skill repo. Also trigger when the user says "submit to awesome-claude-skills", "add my skill to the awesome list", "how do I let others install my skill?", "I finished my skill, now what?", "push my skill to a public repo", "generate a README and publish", "bump the version and republish", or "turn my local skill into a polished repo". ALSO trigger on `claude plugin install` failures and diagnostic questions: `Plugin X not found in any configured marketplace`, `Plugin X not found in marketplace Y`, `Invalid schema: plugins.0.source: Invalid input`, `Failed to add marketplace: Failed to parse marketplace file`, "my plugin install is failing", "why can't I install my own skill?", "claude plugin marketplace add is silently failing", "plugin not found in marketplace", "marketplace add appears to work but install fails", "debug plugin install", "wrong marketplace.json path", "wrong plugin source field". The Common failure modes section (Step 2.5) documents the three bug layers (deprecated command, wrong manifest path, wrong source path) with symptoms, diagnoses, and fixes that apply to BOTH new publishes and already-published broken skills. Covers: canonical `plugins/<name>/` subdirectory layout (v2.0.0+), `.claude-plugin/marketplace.json` packaging, README generation with demo screenshots via puppeteer, multi-agent review panel for README quality, research verification of thresholds/claims, visual distinction of grounded vs heuristic thresholds, GitHub repo metadata (description, topics), PR submission to awesome-claude-skills, PDF output for HTML-generating skills, and end-to-end install flow verification before publishing. Do NOT use for creating new skills from scratch (use skill-creator instead), improving skill trigger accuracy or quality (use schliff instead), general code deployment, writing READMEs for non-skill projects, or non-skill package management.
How this skill is triggered — by the user, by Claude, or both
Slash command
/publish-skill:publish-skillThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Turn a local Claude Code skill into a polished, adoptable open-source GitHub repo.
Turn a local Claude Code skill into a polished, adoptable open-source GitHub repo.
These skills automate the hardest parts of safe publishing. If installed, they'll be invoked automatically at the relevant steps:
git clone https://github.com/wan-huiyan/skill-anonymizer.git ~/.claude/skills/skill-anonymizergit clone https://github.com/wan-huiyan/data-provenance-verifier.git ~/.claude/skills/data-provenance-verifier/skill-sync init after publishing to register the new skill for future updates.If skill-anonymizer is installed, run /skill-anonymizer first — it automates this step.
Scan for client-identifying data before publishing. Combination risk: even innocuous details (campaign type + currency + dates + industry) can identify a client when combined.
Scan targets: SKILL.md, README.md, demo screenshots/HTML, marketplace.json (no employer email), supporting files (references/, docs/, examples/).
Sanitization checklist:
Demo domain selection: Use common domains (e-commerce, churn, retail, SaaS) or public datasets (NYC taxi, MovieLens). Avoid niche verticals where your team has few clients. Even sanitized data in the same domain as client work raises questions.
Find the SKILL.md to publish:
~/.claude/skills/{skill-name}/SKILL.md
Read the frontmatter to extract: name, description, version, author.
User preference (Huiyan, standing rule, 2026-05-08): Default to Pattern B (multi-skill plugin) for ALL new published plugins, even single-skill ones. Wrap the skill at
plugins/<plugin-name>/skills/<skill-name>/SKILL.mdrather thanplugins/<plugin-name>/SKILL.md. This matches thesuperpowersplugin layout, lets the plugin grow to N skills later without restructuring, and gives one install command (/plugin install <plugin>@<marketplace>) that delivers the whole bundle. Pattern A (1-skill = 1-plugin) is documented below for reference and existing legacy publishes; do not produce new ones in that shape.
⚠️ BREAKING CHANGE IN v2.0.0: The canonical Claude Code plugin layout places
marketplace.json INSIDE .claude-plugin/ (not at repo root) and each plugin lives
in its own plugins/<name>/ subdirectory. Skills published with the v1.x layout
will fail the claude plugin install flow with Invalid schema: plugins.0.source: Invalid input. See "Common failure modes" section at the end of this skill for
details. Mirror the layout of claude-plugins-official
and voltagent-subagents —
they are the canonical reference implementations.
plugin-name/ # ← repo root (becomes the marketplace)
├── .claude-plugin/
│ └── marketplace.json # ← MUST be inside .claude-plugin/
├── plugins/
│ └── plugin-name/ # ← THE single plugin (same name as repo, by convention)
│ ├── .claude-plugin/
│ │ └── plugin.json # ← plugin manifest INSIDE plugins/<name>/.claude-plugin/
│ └── skills/
│ ├── skill-1/
│ │ ├── SKILL.md # ← skill at plugins/<plugin>/skills/<skill>/SKILL.md
│ │ ├── scripts/ # ← skill-specific files alongside SKILL.md
│ │ └── references/
│ ├── skill-2/SKILL.md
│ └── ...
├── docs/ # ← repo-level (screenshots, case studies)
├── tests/ # ← repo-level
├── README.md # ← repo-level
├── LICENSE # ← MIT (see "Why MIT" below)
└── package.json # ← optional
For a single-skill publication, use this same shape with one entry under skills/. Don't collapse to Pattern A — keep room to grow.
Old shape, used by dashboard-audit-toolkit. Documented here so you can read existing repos in this layout, but do not produce new publishes in this shape:
skill-name/ # ← repo root (becomes the marketplace)
├── .claude-plugin/
│ └── marketplace.json # ← lists each skill as a separate plugin entry
├── plugins/
│ └── skill-name/ # ← each plugin in its own subdirectory
│ ├── .claude-plugin/
│ │ └── plugin.json # ← plugin manifest INSIDE plugins/<name>/.claude-plugin/
│ ├── SKILL.md # ← canonical plugin skill location (NO skills/ wrapper)
│ ├── hooks/ # ← plugin-specific hooks (if applicable)
│ │ └── {hook-name}.sh
│ └── references/ # ← files referenced by SKILL.md
├── docs/ # ← repo-level (screenshots, case studies)
│ ├── demo-{name}.html
│ └── demo-{name}.png
├── tests/ # ← repo-level (manifest consistency, etc.)
├── README.md # ← repo-level (GitHub browsers see this)
├── LICENSE # ← MIT (see "Why MIT" below)
└── package.json # ← optional, for npm-based tests
When publishing additional skills under a Pattern B plugin, just add another directory under skills/ and update the README's plugin table — no marketplace or manifest changes needed beyond bumping version in plugin.json.
README link-path gotcha when migrating Pattern A → Pattern B (or starting Pattern B from a Pattern A draft): the per-skill links in your README must point at
plugins/<plugin>/skills/<skill>/, NOTplugins/<skill>/. If you wrote the README expecting the older one-skill-per-plugin shape and then moved skills underplugins/<plugin>/skills/, every per-skill link will 404 on GitHub with "Error loading page" while everything still installs correctly viaclaude plugin install. Verify with:python3 -c "import re,pathlib; [print('✗',l) for l in re.findall(r'\(plugins/[^)]+\)', pathlib.Path('README.md').read_text()) if not pathlib.Path(l.strip('()')).exists()]"from the marketplace root.
Why MIT? Standard for Claude Code skills (~44% of GitHub). Apache 2.0 if patent protection matters; avoid GPL.
Key rules (verified against the Claude Code docs at https://code.claude.com/docs/en/plugin-marketplaces):
marketplace.json lives at .claude-plugin/marketplace.json, NOT at the repo root.
The old v1.x layout (marketplace.json at root) silently fails claude plugin marketplace add —
the marketplace never registers in ~/.claude/plugins/known_marketplaces.json and the
subsequent error message is misleading.plugins/<plugin-name>/, never at the marketplace root. Claude Code
copies the plugin directory into a cache on install — if the plugin were the marketplace
directory, this would be recursive self-reference, and the schema validator rejects it.plugins[0].source in marketplace.json must start with ./ AND point to a subdirectory.
Valid: "./plugins/skill-name". Invalid: "." or "./" (rejected by schema validator).plugins/<plugin-name>/.claude-plugin/plugin.json, NOT at
the repo root.plugins/<plugin-name>/SKILL.md (the canonical skill location).
Do NOT keep a second SKILL.md at the repo root — if the two fall out of sync, only the
nested one is loaded when users install via claude plugin install, and the root copy
becomes stale and misleading.Why MIT? Standard for Claude Code skills (~44% of GitHub). Apache 2.0 if patent protection matters; avoid GPL.
plugin.json template (place at plugins/{skill-name}/.claude-plugin/plugin.json):
{
"name": "{skill-name}",
"description": "{from SKILL.md frontmatter}",
"version": "{version}",
"author": { "name": "{user's name}" },
"repository": "https://github.com/{username}/{skill-name}",
"license": "MIT",
"keywords": ["{domain-tags}"]
}
marketplace.json template (place at .claude-plugin/marketplace.json — NOT the repo root):
{
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
"name": "{username}-{skill-name}",
"description": "{one-line marketplace description — required by validators}",
"owner": { "name": "{user's name}" },
"plugins": [{
"name": "{skill-name}",
"source": "./plugins/{skill-name}",
"description": "{one-line plugin description}",
"version": "{version}"
}]
}
CRITICAL — distinguishing names:
plugins[0].name): what users type in the install command, e.g. skill-name.
Must match the name field in plugin.json.name): the marketplace identifier, e.g. {username}-{skill-name}.
Can be owner-prefixed to avoid collisions across users publishing plugins with the same name./plugin install {skill-name}@{username}-{skill-name} — note the @ separator.The two names are DIFFERENT. A common mistake is to make them identical or to assume the install command takes just the marketplace name — both fail.
After creating the files, verify the install flow works locally before publishing. This catches all three layout bugs that cost 4+ PRs to debug in the wild:
# 1. Verify marketplace.json is in .claude-plugin/ (NOT repo root)
test -f .claude-plugin/marketplace.json && echo "✓ marketplace.json in right place" || echo "✗ WRONG PATH"
# 2. Verify plugin.json is inside plugins/<name>/.claude-plugin/
test -f plugins/{skill-name}/.claude-plugin/plugin.json && echo "✓ plugin.json in right place" || echo "✗ WRONG PATH"
# 3. Verify source path in marketplace.json starts with ./plugins/
grep '"source"' .claude-plugin/marketplace.json # should show "./plugins/{skill-name}", never "." or "./"
# 4. Locally test the marketplace add (requires a committed working tree on a local path)
claude plugin marketplace add ./
# 5. Verify it actually registered
cat ~/.claude/plugins/known_marketplaces.json | grep {marketplace-name}
# If nothing appears, the marketplace add silently failed — the layout is wrong
# 6. Clean up the local test marketplace before pushing
claude plugin marketplace remove {marketplace-name}
Alternatively, validate via the built-in CLI (faster, no side effects):
claude plugin validate .
# or inside a Claude Code session:
# /plugin validate .
Do NOT skip this step. The three layout bugs below all pass git/GitHub syntactically but fail Claude Code's schema validator or silently mis-register the marketplace.
These are the three bug layers that v1.x of this skill produced. If you follow the v2.0.0 layout above, you will avoid all of them. If you inherit a v1.x skill repo, expect to hit them in sequence.
Layer 1: Deprecated install command in README — v1.x READMEs sometimes showed
claude skill install <owner/repo>. This command DOES NOT EXIST; the deprecated form was
claude install skill which was removed. Users get "command not found" or confusing errors.
Fix: always use the two-step flow claude plugin marketplace add <owner/repo> then
claude plugin install <plugin-name>@<marketplace-name>.
Layer 2: marketplace.json at the wrong path — v1.x placed marketplace.json at the
repo root. Claude Code looks for it at .claude-plugin/marketplace.json. When the file is
missing at the expected path, claude plugin marketplace add silently fails to register
the marketplace — it doesn't error, it just doesn't write to
~/.claude/plugins/known_marketplaces.json. The subsequent plugin install then reports a
misleading error: Plugin "X" not found in marketplace "Y" (implying the plugin is missing,
when the marketplace was never registered). Always verify via cat ~/.claude/plugins/known_marketplaces.json
after running marketplace add — if your marketplace name isn't there, the path is wrong.
Layer 3: Plugin at the marketplace root — v1.x used "source": "." or
"source": "./" in marketplace.json, trying to make the repo root itself the plugin.
Claude Code's schema validator rejects both with Invalid schema: plugins.0.source: Invalid input.
Reason: Claude Code copies the plugin directory into a cache on install, so the plugin
cannot BE the marketplace directory (self-reference). The docs explicitly state: "Local
directory within the marketplace repo. Must start with ./" — meaning a subdirectory,
not the current directory. Always use "source": "./plugins/<plugin-name>".
Test-suite gotcha: If you use the tests/manifest-consistency.test.mjs pattern common
in skill repos, check that it uses an ASSERTIVE check (assert.ok(existsSync(path))) rather
than a GRACEFUL-DEGRADATION guard (if (existsSync(path)) describe(...)). The graceful form
silently skips all marketplace.json assertions when the file is missing, producing green tests
despite a broken repo. This masks the Layer 2 bug indefinitely.
Additional test-writing rule: NEVER assert marketplaceJson.name === pluginJson.name.
The marketplace name is owner-prefixed (e.g., wan-huiyan-causal-impact-campaign) while the
plugin name is unprefixed (e.g., causal-impact-campaign). The real invariant to check is
marketplaceJson.plugins[0].name === pluginJson.name — that's what users type in the
install command after the @.
Follow this structure (order matters for first-time visitor conversion):
Title + one-line descriptor — what it IS, factual. Not a scenario. 1b. Badge row — immediately after the title. Provides at-a-glance trust signals.
Prefer dynamic badges (auto-update from GitHub API) over static ones:
[]({REPO_URL}/releases)
[](LICENSE)
[]({REPO_URL}/commits)
[](https://www.python.org/)
[](https://claude.com/claude-code)
Dynamic badges (no manual updates needed):
github/v/release — reads from GitHub Releases (create a release first!)github/license — reads from repo metadatagithub/last-commit — shows repo activity (signals maintenance)Static badges (update manually or via skill-sync):
badge/python-{PY_VERSIONS}-yellow — no GitHub API sourcebadge/Claude_Code-skill-orange — constant markerbadge/eval_assertions-{N}_passed-brightgreen — if eval-suite.json existsbadge/grounded_in-{N}%2B_papers-blueviolet — if references/ with bibliographyImportant: Dynamic version badge requires a GitHub Release. Create one:
gh release create v{VERSION} --title "v{VERSION}" --notes "Release notes"
Where to get values:
{USERNAME}/{SKILL_NAME}: GitHub owner/repo (e.g., wan-huiyan/causal-impact-campaign){PY_VERSIONS}: from SKILL.md description or requirements (e.g., "3.9--3.12"){N} assertions: count entries in eval-suite.json assertions array{N} papers: count entries in references/bibliography.mdScreenshot (if visual output) — above the fold. This is the #1 adoption driver.
Quick Start — conversation example showing usage flow
Installation — Claude Code + Cursor:
Claude Code:
# Plugin install (recommended — requires plugin packaging)
/plugin marketplace add {username}/{skill-name}
/plugin install {skill-name}@{username}-{skill-name}
# Git clone (always works)
git clone https://github.com/{username}/{skill-name}.git ~/.claude/skills/{skill-name}
Cursor (Cursor 2.4+, global discovery can be flaky):
# Per-project rule (most reliable)
mkdir -p .cursor/rules
# Create .cursor/rules/{skill-name}.mdc with SKILL.md content + alwaysApply: true
# npx skills CLI
npx skills add {username}/{skill-name} --global
# Manual global install
git clone https://github.com/{username}/{skill-name}.git ~/.cursor/skills/{skill-name}
IMPORTANT: claude install-skill is NOT a real command. Never use it in READMEs.
The correct plugin install command is: claude plugin install <plugin-name>
For git-based installs: git clone https://github.com/{username}/{skill-name}.git ~/.claude/skills/{skill-name}
Suggested Hook (if applicable) — skills that benefit from automatic triggering should include a ready-to-use hook config. See "Bundling Hooks with Skills" below.
What You Get — concrete, tangible outputs
Comparison table — "Typical Ad-Hoc vs With Skill" (realistic baseline, not strawman)
How It Works — step table
Key Design Decisions — the non-obvious architectural choices
Limitations — what it does NOT do. Builds trust with technical audiences.
Dependencies — required vs optional. Explain what happens without each.
Quality Checklist in <details> — what the skill guarantees
Related Skills — cross-link companion skills with hyperlinks
Research/References in <details> — citations with hyperlinks, if applicable
Version History — even 3 lines. Trust signal that it's maintained.
License — MIT
.fillna(0) tips when the skill's real value is "safe training window extension" makes it look like a niche tool. Visitors scan pitfalls to decide "is this for me?" — implementation details don't answer that question.Structure pitfalls in two tiers:
Apply this strategic-first ordering to ALL list/table sections (pitfalls, "what worked", "key lessons", "what you get"). If the README already has strong narrative sections, keep them but add a scannable "What This Catches" list as a quick entry point.
Frame narratives as methodology, not anecdote. The intro must show the story illustrates the skill's systematic process, not one lucky outcome. Bad: "we achieved X." Good: "Every analysis follows this pattern — here's one run."
If the skill uses numeric thresholds, visually distinguish grounded vs heuristic:
Thresholds in **bold** are grounded in published sources.
Thresholds in *italic* are practitioner heuristics — adjust for your domain.
| Criterion | Threshold | Source |
|-----------|-----------|--------|
| Gain ratio | **Rank-based** | [Quinlan (1993)](link) |
| Coverage gaps | *>20%* | Heuristic |
Some skills benefit from automatic triggering via Claude Code hooks. If your skill can enhance existing workflows without user invocation (e.g., post-commit checks, post-write formatting, pre-tool validation), include a suggested hook configuration.
When to include a hook:
| Skill type | Hook event | Example |
|---|---|---|
| Git workflow tools | PostToolUse on Bash (filter for git commit) | Squash hint after trivial commits |
| Code quality tools | PostToolUse on Write|Edit | Auto-lint, type-check, or validate |
| Safety/audit tools | PreToolUse on Bash | Block dangerous commands |
| Session lifecycle | Stop or SessionStart | Save learnings, load context |
What to include in the published skill:
hooks/ directory with the ready-to-use script(s). Users copy to ~/.claude/hooks/.settings.json snippet (hook config JSON)Hook design principles:
timeout: 5).git commit),
exit immediately with no output. Don't print "no action needed."hookSpecificOutput.additionalContext to inject suggestions into the conversation
(the model sees it). Use systemMessage for user-visible warnings.Example from git-squash skill:
// In settings.json — PostToolUse array
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "~/.claude/hooks/squash-hint.sh",
"timeout": 5,
"statusMessage": "Checking commit..."
}]
}
The hook script checks if the command was git commit, scores the last commit on message/diff/timing
heuristics, and injects a /squash suggestion if the score is 60+.
For skills that produce visual output (HTML slides, diagnostics, reports):
docs/demo-{name}.html using a generic use case
(SaaS churn, e-commerce conversion — NOT university enrollment, specific client names)cd /tmp/{repo} && npm install puppeteer
const puppeteer = require('puppeteer');
const browser = await puppeteer.launch({ headless: 'new' });
const page = await browser.newPage();
await page.setViewport({ width: 900, height: 1200, deviceScaleFactor: 2 });
await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle0' });
await new Promise(r => setTimeout(r, 2000)); // wait for fonts
const element = await page.$('.panel'); // or '.slide', '.section'
const box = await element.boundingBox();
await page.screenshot({
path: 'docs/demo-screenshot.png',
clip: { x: box.x - 20, y: box.y - 10, width: box.width + 40, height: box.height + 20 },
});
demo-header.png, demo-session.png, demo-actions.png etc.
Use element-targeted clips (page.$('#section-id').boundingBox()) for each.node_modules/, package.json, package-lock.json before committingIf the skill uses numeric thresholds or cites academic papers, run parallel research agents to verify each claim:
Launch 1 agent per threshold/claim in parallel. Each agent searches for:
- Does the cited paper actually specify this threshold?
- Is this a ranking metric used with a fixed cutoff (likely wrong)?
- What do popular implementations use as defaults?
Common findings (from verifying ML thresholds across 5 skills):
| Threshold | Verdict | What to do instead |
|---|---|---|
| Gain ratio >0.3 | Invented. Quinlan uses ranking only. | Rank-based: above-average info gain filter |
| AUC >0.005 | Invented. No paper defines this. | DeLong test p<0.05 (DeLong 1988) |
| Conditional MI >50% | Invented. Brown et al. uses ranking. | Rank-based: JMI/CMIM scoring |
| PSI <0.10/0.25 | Industry convention. Siddiqi 2006. | Keep but note sample-size dependent (Yurdakul 2018) |
| Population >200 | Misattributed. Van der Ploeg = 200 EPV. | 20-300 range (LightGBM docs) |
Rule: Label honestly. Bold for published, italic for heuristic.
If the skill ships with data files (CSV, JSON, examples/), run data-provenance-verifier
to ensure all data is genuine and has provenance documentation before publishing.
For each piece of guidance, ask: "Would this apply with a different database/language/framework?" If not, abstract further.
Checklist:
Generate an automated test suite from the skill's eval-suite.json and manifest files. This catches version mismatches, invalid regex patterns, duplicate IDs, and structural issues before they reach users. Tests run on every push via GitHub Actions.
Generated files:
tests/manifest-consistency.test.mjs — cross-validates plugin.json, SKILL.md, eval-suite versions/namestests/eval-suite-integrity.test.mjs — validates structure, regex compilation, ID uniqueness, trigger balancetests/trigger-classification.test.mjs — validates trigger entries and skill name references.github/workflows/test.yml — CI pipeline (Node.js 20+22 matrix)package.json (if not present) — minimal, with "test": "node --test tests/*.test.mjs"Templates location: ~/Documents/skill-test-templates/ (generalized from agent-review-panel's 363-test suite)
Process:
~/Documents/skill-test-templates/ into the repo's tests/ directorytest.yml into .github/workflows/package.json if missing (parameterized from plugin.json name/version/description)npm test to verify all tests passOr use the backfill script: ~/Documents/skill-test-templates/backfill.sh /path/to/repo
Skills WITHOUT eval-suite.json: Only manifest-consistency tests are generated. eval-suite and trigger tests skip gracefully.
After generating, add a Tests badge to the README:
[](https://github.com/{owner}/{repo}/actions/workflows/test.yml)
⚠️ Critical test-writing rule: When generating manifest-consistency.test.mjs, use
assertive existence checks for required manifests, not graceful-degradation guards.
// BAD — silently skips all assertions if marketplace.json is at the wrong path
if (existsSync(marketplaceJsonPath)) {
describe("marketplace.json", () => { /* ... */ });
}
// GOOD — fails loudly if the file is missing or at the wrong path
assert.ok(
existsSync(marketplaceJsonPath),
`marketplace.json must exist at .claude-plugin/marketplace.json`
);
The graceful form masks the Layer 2 bug described in Step 2.5 — 160 tests pass green while
the repo is actually broken for claude plugin install. Similarly, NEVER assert
marketplace.name === plugin.name — those are different by convention (marketplace is
owner-prefixed). The real invariant is marketplace.plugins[0].name === plugin.name.
Run /agent-review-panel on the README before publishing:
Review the README at {path}. Evaluate whether it would attract a first-time
visitor to install this Claude Code skill. Use 3 reviewers
(Clarity Editor, Completeness Checker, Devil's Advocate).
Common panel findings across 5 repo reviews:
Before pushing, verify every link in the README actually works and points to the correct source. This catches two common problems:
1. Broken links (404s): Schliff, Claudeception, and other tools get forked and moved. The repo you linked last month may not exist today.
# Extract all GitHub repo links and check each one
grep -oP 'https://github\.com/[a-zA-Z0-9_-]+/[a-zA-Z0-9_-]+' README.md | sort -u | while read url; do
repo=$(echo "$url" | sed 's|https://github.com/||')
if gh api "repos/$repo" --jq .full_name 2>/dev/null > /dev/null; then
echo "OK: $url"
else
echo "BROKEN: $url"
fi
done
2. Wrong attribution (fork instead of upstream): When you install a skill from someone else's repo, your README should link to the ORIGINAL author's repo, not your own fork. Common mistakes:
brainstorming / using-superpowers → should link to obra/superpowers, not your forkschliff → should link to Zandereins/schliff, not any forkclaudeception → built-in skill, no separate repo (don't link to a random repo)Rule of thumb: If you didn't write the skill, link to the upstream repo. Check
with gh api repos/OWNER/REPO --jq .fork — if it returns true, find the parent.
gh repo create {username}/{skill-name} --public \
--description "{one-line}" --source . --push
Set default branch to main if created as master:
gh api repos/{username}/{skill-name} -X PATCH -f default_branch=main
git branch -m master main && git push origin main
git push origin --delete master
Set topics:
gh api repos/{username}/{skill-name}/topics -X PUT --input - <<'EOF'
{"names":["claude-code","claude-code-skill","{domain-1}","{domain-2}"]}
EOF
Fork contributions: gh repo edit and the topics API require admin/owner access.
If you're contributing via a fork PR (not the repo owner), you cannot set topics programmatically.
Instead, include a "Suggested repo settings" section in the PR description listing recommended
topics for the owner to add manually.
The repo uses these categories (from CONTRIBUTING.md):
Add your entry in alphabetical order within the chosen category.
Format (strict):
- [Skill Name](https://github.com/{username}/{skill-name}) - One-sentence description. *By [@username](https://github.com/{username})*
Format rules:
Description length: aim for ~130 characters (the norm across the repo). Descriptions over ~150 chars will likely be flagged by reviewers.
What to INCLUDE in the description:
What to DROP from the description (move to your repo's README instead):
Rule of thumb: Drop paper references, specific counts, and implementation details from the description. Keep the core value proposition.
# Clone your fork (or create one)
gh repo fork ComposioHQ/awesome-claude-skills --clone
cd awesome-claude-skills
git checkout -b add-{skill-name}
# Add entry to README.md in the appropriate category, alphabetical order
git add README.md && git commit -m "Add {skill-name} skill"
PR title: "Add {skill-name} skill" (or "Add {skill-1} and {skill-2} skills" for multiple)
PR description (from CONTRIBUTING.md — reviewers expect these):
gh pr create --title "Add {skill-name} skill" --body "$(cat <<'EOF'
## What problem it solves
{One paragraph on the real-world use case}
## Who uses this workflow
{Target audience — ML engineers, data scientists, etc.}
## Example usage
{Brief conversation example or trigger phrase}
## Links
- Repo: https://github.com/{username}/{skill-name}
- License: MIT
EOF
)"
Reviewers commonly flag:
*By [@username](profile)* suffixWhen amending per review feedback, use git commit --amend + git push --force-with-lease
to keep the PR clean (single commit).
Important: Never create multiple PRs from the same fork to the same upstream.
Update existing PRs with git commit --amend && git push --force-with-lease.
When you need to update a PR that targets an upstream repo (yours or someone else's), the PR may come from any fork. Don't guess — look it up:
# Step 1: Find which fork and branch the PR comes from
gh api repos/{owner}/{repo}/pulls/{num} --jq '.head.repo.full_name, .head.ref'
# Output: wan-huiyan/field-notes
# fix/installation-guide
# Step 2: Clone THAT FORK (not the upstream repo)
git clone https://github.com/{fork-full-name}.git /tmp/{repo}
# Step 3: Checkout THAT BRANCH
cd /tmp/{repo} && git checkout {branch}
# Step 4: Make changes, commit, push to THAT FORK
git add . && git commit -m "..." && git push origin {branch}
Common mistake: Cloning the upstream repo and fetching pull/{num}/head gives you a
detached ref you can't push back to. Always clone the fork directly.
After publishing, register the skill so future updates can be pushed with /skill-sync:
/skill-sync init
This scans your GitHub repos and matches them to local skill directories.
For ongoing updates (editing SKILL.md, eval-suite.json, etc.), use /skill-sync push {name}
instead of manually cloning and copying. See the skill-sync companion skill for details.
If the skill generates HTML (slides, reports, diagnostics), add PDF as an output. Key CSS rules for clean page breaks:
@media print {
.section, .card, .callout { break-inside: avoid; }
p { break-inside: avoid; }
h1, h2, h3 { break-after: avoid; }
.section-header + .section-desc { break-before: avoid; }
.page-break-before { break-before: page; }
}
flex-wrap: nowrap on flow diagrams prevents node wrapping to second rowbreak-inside: avoid on paragraphs prevents text splitting mid-sentence.page-break-before classFor ongoing updates to already-published skills, use the skill-sync companion skill:
/skill-sync push {skill-name}
This handles cloning, copying tracked files, committing, and pushing automatically.
For version bumps (not just content edits), skill-sync includes a full checklist
covering all 7 files that need version updates. See /skill-sync for details.
For 7+ phase workflows, use named stages (Gather/Analyze/Apply/Finalize) with sequential numbering. Avoid decimal sub-phases (1.5, 3.5) — they imply sub-phases are secondary and accumulate confusingly. Fix numbering early (pre-v2.0).
git commit -m "$(cat <<'EOF'
{type}: {description}
{body explaining what and why}
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
EOF
)"
Types: feat: (new feature), docs: (README/docs), fix: (bug fix), chore: (packaging)
Input: A target skill path (e.g., ~/.claude/skills/{name}/SKILL.md) containing valid
frontmatter with name and description fields. The skill must exist locally before publishing.
Output: A complete GitHub repository with SKILL.md, README.md, LICENSE, plugin.json,
marketplace.json, and optionally docs/ with demo screenshots. Returns the GitHub repo URL
and (optionally) the awesome-claude-skills PR URL.
git (any version) for repo creation and pushinggh CLI (GitHub CLI) for gh repo create, gh api, and PR creation. If gh is not
available, fall back to manual GitHub workflow instructionsnpm + puppeteer for screenshot generation (skip gracefully if unavailable).cursor/rules/ copygh CLI fails (auth, network), provide manual git + GitHub web UI fallback instructionsSafe to re-run on the same skill. Running publish-skill twice produces the same repo structure. Updating an already-published skill is an explicit supported workflow (version bump + push). No destructive side effects on the local skill installation.
skill-creator instead),
improving skill quality/triggers (use schliff), general code deployment to cloud providers,
or writing READMEs for non-skill projectsskill-creator if the user needs to build a skill first, then come back here;
hand off to schliff if the user wants to improve the skill before publishing;
hand off to agent-review-panel for README quality review (this skill invokes it as a sub-step)All generated files are scoped to the publish-skill workflow namespace:
plugin.json, marketplace.json, and repo structure under {skill-name}/. Does not modify
files outside the target repo directory or ~/.claude/skills/{name}/.
Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub wan-huiyan/publish-skill --plugin publish-skill