| name | skill-publishing |
| description | Makes any Claude Code skill shareable on GitHub by adding README, LICENSE, CHANGELOG, .gitignore, initializing a git repo, and pushing to GitHub. Supports individual repos, a monorepo (claude-code-skills), versioned monorepo releases with semver tags, and plugin assembly/distribution. Use when: (1) a skill directory needs to be published to GitHub, (2) user wants to make a skill installable by others, (3) user says 'share this skill' or 'publish skill to GitHub', (4) preparing a skill for open-source distribution, (5) syncing skills to the monorepo, (6) user says 'sync skills' or 'update monorepo', (7) creating a versioned monorepo release with tag, (8) assembling a plugin from skills + commands, (9) user says 'publish plugin' or 'package plugin'. |
| metadata | {"version":"3.5.0"} |
Skill to GitHub
Converts local Claude Code skill directories into shareable GitHub repositories.
Supports three distribution models: individual repos, a monorepo (claude-code-skills),
and plugins (bundles of skills + commands).
Quick Reference
SCRIPTS=~/.claude/skills/skill-publishing/scripts
$SCRIPTS/prepare-skill-repo.sh --dry-run /path/to/skill
$SCRIPTS/prepare-skill-repo.sh /path/to/skill
$SCRIPTS/sync-individual-repos.sh --dry-run --all
$SCRIPTS/sync-individual-repos.sh --all --push
$SCRIPTS/sync-monorepo.sh --init ~/dev/claude-code-skills
$SCRIPTS/sync-monorepo.sh --dry-run ~/dev/claude-code-skills
$SCRIPTS/sync-monorepo.sh ~/dev/claude-code-skills
$SCRIPTS/sync-monorepo.sh --add my-new-skill ~/dev/claude-code-skills
cd ~/dev/claude-code-skills
git add -A && git commit -m "Sync skills (YYYY-MM-DD)" && git push
$SCRIPTS/release-monorepo.sh --dry-run minor ~/dev/claude-code-skills
$SCRIPTS/release-monorepo.sh patch ~/dev/claude-code-skills
$SCRIPTS/release-monorepo.sh minor ~/dev/claude-code-skills
$SCRIPTS/release-monorepo.sh major ~/dev/claude-code-skills
$SCRIPTS/prepare-plugin.sh --dry-run /path/to/plugin-manifest.json
$SCRIPTS/prepare-plugin.sh /path/to/plugin-manifest.json
$SCRIPTS/validate-plugin.sh ./build/plugin-name
$SCRIPTS/sync-monorepo.sh --add-plugin plugin-name ~/dev/claude-code-skills
$SCRIPTS/install-plugin.sh --dry-run ./build/plugin-name
$SCRIPTS/install-plugin.sh ./build/plugin-name
$SCRIPTS/install-plugin.sh --uninstall ./build/plugin-name
Architecture
~/.claude/skills/ (SOURCE OF RECORD)
├── conversation-search/
├── skill-authoring/
├── skill-publishing/
├── git-flow/
│ └── plugin-manifest.json (build manifest for plugins)
└── ...
Individual repos: (each skill has its own GitHub repo)
├── github.com/USER/conversation-search
├── github.com/USER/skill-authoring
└── github.com/USER/skill-publishing
Monorepo: (all skills + plugins in one repo)
└── github.com/USER/claude-code-skills
├── README.md (auto-generated: skill table + plugin section)
├── conversation-search/ (skill — flat at root)
├── skill-authoring/
├── skill-publishing/
├── plugins/ (plugins — under plugins/ subfolder)
│ └── git-flow/
│ ├── .claude-plugin/plugin.json
│ ├── commands/
│ └── skills/
└── scripts/
├── validate-skill.sh
├── validate-plugin.sh
└── install-plugin.sh
Key principle: ~/.claude/skills/ is the single source of truth. Both individual repos and the monorepo are derived from it via flat-copy (no git subtrees). Plugins are assembled from build manifests and stored in plugins/.
Interactive Publishing Flow
When invoked (e.g., "publish this skill", "share skill", "sync skills"), start with target selection.
Step 1: Detect Current State
For the skill being published, detect which targets it's already published to:
SKILL_NAME="<name-from-frontmatter>"
GITHUB_USER=$(gh api user --jq '.login' 2>/dev/null)
MONOREPO_DIR="${HOME}/dev/claude-code-skills"
INDIVIDUAL_PUBLISHED=false
gh repo view "$GITHUB_USER/$SKILL_NAME" --json name >/dev/null 2>&1 && INDIVIDUAL_PUBLISHED=true
MONOREPO_SYNCED=false
[[ -f "$MONOREPO_DIR/$SKILL_NAME/SKILL.md" ]] && MONOREPO_SYNCED=true
PLUGIN_AVAILABLE=false
PLUGIN_SYNCED=false
[[ -f "$SKILL_DIR/plugin-manifest.json" ]] && PLUGIN_AVAILABLE=true
[[ -d "$MONOREPO_DIR/plugins/$SKILL_NAME" ]] && PLUGIN_SYNCED=true
Step 2: Present Target Selection
Use AskUserQuestion with multiSelect: true. Label each option with its current state so the user knows what's already published. Warn that deselecting a published target will remove the skill from that location.
Question: "Which publishing targets do you want for <skill-name>? Deselecting a currently published target will REMOVE it from that location."
Options (dynamic labels based on Step 1):
| State | Label | Description |
|---|
| Not published | Individual repo | "Create a standalone GitHub repo for this skill" |
| Published | Individual repo (published) | "Keep synced. Deselect to DELETE the repo" |
| Not synced | Monorepo | "Add to the claude-code-skills monorepo" |
| Synced | Monorepo (synced) | "Keep synced. Deselect to REMOVE from monorepo" |
| No manifest | Plugin | "Not available — no plugin-manifest.json found" |
| Has manifest, not synced | Plugin | "Assemble and add plugin to monorepo" |
| Synced | Plugin (synced) | "Keep synced. Deselect to REMOVE plugin from monorepo" |
If no plugin-manifest.json exists, disable the Plugin option by adding "(requires plugin-manifest.json)" to the description. Only offer it as selectable when the manifest exists.
Step 3: Dispatch
For each SELECTED target:
| Target | Already Published? | Action |
|---|
| Individual repo | No | Run Workflow A (prepare + push) |
| Individual repo | Yes | Run Workflow C (sync individual repo) |
| Monorepo | No | Run sync-monorepo.sh --add <name> then Workflow B |
| Monorepo | Yes | Run Workflow B (sync monorepo) |
| Plugin | No | Run Workflow E (assemble + sync to monorepo) |
| Plugin | Yes | Re-assemble + re-sync via Workflow E |
For each DESELECTED target that was previously published (removal):
| Target | Removal Action |
|---|
| Individual repo | gh repo delete $GITHUB_USER/$SKILL_NAME --yes (confirm with user first!) |
| Monorepo | rm -rf $MONOREPO_DIR/$SKILL_NAME/ then re-sync README + commit + push |
| Plugin | rm -rf $MONOREPO_DIR/plugins/$SKILL_NAME/ then re-sync README + commit + push |
Always confirm destructive removals with the user before executing. Phrase the confirmation as: "This will permanently delete <skill-name> from <target>. Proceed?"
Step 4: Auto-Sync to Monorepo
When any Monorepo or Plugin target is selected, automatically sync and push. Do NOT leave this as a manual step — the user expects publishing to be end-to-end.
SCRIPTS=~/.claude/skills/skill-publishing/scripts
MONOREPO_DIR="${HOME}/dev/claude-code-skills"
$SCRIPTS/sync-monorepo.sh $MONOREPO_DIR
cd $MONOREPO_DIR
git add -A
CHANGED=$(git diff --cached --stat)
if [[ -n "$CHANGED" ]]; then
git commit -m "Sync skills ($(date +%Y-%m-%d))"
git push origin main
fi
Important: The prevent-direct-push hook in some projects blocks git push origin main via Claude. If push is blocked, instruct the user to push manually from their terminal:
cd ~/dev/claude-code-skills && git push origin main
Step 5: Post-Publish
After all targets are processed:
- If monorepo was modified → ask whether to create a versioned release (Workflow D)
- Clean up build artifacts:
rm -rf ~/.claude/skills/skill-publishing/build/
- Report summary of what was published/synced/removed
Workflow A: Publish a New Skill (Individual Repo)
Step 1: Run the Preparation Script
~/.claude/skills/skill-publishing/scripts/prepare-skill-repo.sh /path/to/skill
The script:
- Reads
SKILL.md frontmatter to extract name, description, version
- Creates
.gitignore (with .claude/ exclusion for local settings)
- Creates
LICENSE (MIT)
- Creates
CHANGELOG.md from the extracted metadata
- Generates a
README.md with individual + monorepo install instructions
- Reports what files already exist (skips them) vs what was created
Step 2: Review and Customize
After the script runs, review the generated files. Common customizations:
- README.md: Add a Prerequisites section if the skill has dependencies (e.g.,
jq, perl)
- README.md: Add usage examples specific to the skill
- CHANGELOG.md: Expand the "Included" section with more detail
- SKILL.md: Add a See Also section linking to the GitHub repo
Step 3: Add See Also to SKILL.md
Append to the end of SKILL.md:
## See Also
- **GitHub**: https://github.com/<github-user>/<skill-name> — install instructions, changelog, license
Step 4: Initialize Git and Push
cd /path/to/skill
git init
git add .gitignore LICENSE CHANGELOG.md README.md SKILL.md scripts/ references/
git commit -m "Initial public release: <skill-name> v<version>"
gh repo create <skill-name> --public --description "<short-description>" --source . --push
git tag v<version>
git push origin v<version>
Known gotcha: If git remote add origin was already run before gh repo create --source .,
the latter fails with "Unable to add remote" — but the repo IS created. Fix with
git remote set-url origin <url> then push manually.
Username discovery: gh repo create reveals the actual GitHub username (e.g., abhattacherjee
not abhishek). After repo creation, update any references in README.md and SKILL.md with the
correct username.
Step 5: Verify
After pushing:
- Clone to a temp dir:
git clone <url> /tmp/test-skill
- Confirm
SKILL.md is at root with correct frontmatter
- Confirm
scripts/ and references/ are present (if applicable)
- Check that
.claude/ was NOT committed
Workflow B: Sync to Monorepo
First Time: Initialize the Monorepo
~/.claude/skills/skill-publishing/scripts/sync-monorepo.sh --init ~/dev/claude-code-skills
This creates the directory, syncs the default skills (conversation-search, skill-authoring, skill-publishing), generates the root README with a catalog table, and creates + pushes the GitHub repo.
Ongoing: Sync Changes
~/.claude/skills/skill-publishing/scripts/sync-monorepo.sh --dry-run ~/dev/claude-code-skills
~/.claude/skills/skill-publishing/scripts/sync-monorepo.sh ~/dev/claude-code-skills
cd ~/dev/claude-code-skills
git add -A && git commit -m "Sync skills ($(date +%Y-%m-%d))" && git push
Adding a New Skill to the Monorepo
~/.claude/skills/skill-publishing/scripts/sync-monorepo.sh --add my-new-skill ~/dev/claude-code-skills
Workflow C: Sync Individual Repos
When you update a skill locally and want to push changes to its individual GitHub repo:
~/.claude/skills/skill-publishing/scripts/sync-individual-repos.sh --dry-run --all
~/.claude/skills/skill-publishing/scripts/sync-individual-repos.sh --all --push
~/.claude/skills/skill-publishing/scripts/sync-individual-repos.sh conversation-search
Workflow D: Monorepo Release (Version Tag)
After syncing skills to the monorepo and committing, create a versioned release:
~/.claude/skills/skill-publishing/scripts/sync-monorepo.sh ~/dev/claude-code-skills
cd ~/dev/claude-code-skills
git add -A && git commit -m "Sync skills ($(date +%Y-%m-%d))"
git push
~/.claude/skills/skill-publishing/scripts/release-monorepo.sh minor ~/dev/claude-code-skills
Bump Levels
| Level | When | Example |
|---|
patch | Bug fixes, sync updates, typo fixes | 1.0.0 → 1.0.1 |
minor | New skill added, feature improvements | 1.0.0 → 1.1.0 |
major | Breaking changes, removed skills, restructured layout | 1.0.0 → 2.0.0 |
The script:
- Reads current version from the latest
v* semver tag
- Calculates the next version based on bump level
- Updates the CHANGELOG top entry from "Monorepo sync" to a versioned section
- Commits the changelog update
- Creates an annotated tag with skill inventory
- Pushes to
origin main --tags
Use --dry-run to preview without making changes.
Prerequisite: All changes must be committed before running. The script rejects uncommitted changes.
Workflow E: Publish a Plugin
A plugin bundles skills + commands + optional agents/hooks into a single installable package.
Plugin Format
plugin-name/
├── .claude-plugin/plugin.json # Required manifest: {name, version, description}
├── commands/ # Slash commands (.md files)
├── skills/skill-name/ # Skills (SKILL.md + scripts/ + references/)
├── agents/ # Subagents (.md files, optional)
└── hooks/ # hooks.json + scripts (optional)
Step 1: Create Build Manifest
Create plugin-manifest.json in the skill directory that anchors the plugin:
{
"name": "my-plugin",
"version": "1.0.0",
"description": "Short description",
"skills": [{ "name": "my-skill", "source": "~/.claude/skills/my-skill" }],
"commands": [{ "name": "cmd-name", "source": "~/.claude/commands/cmd-name.md" }]
}
Step 2: Assemble
$SCRIPTS/prepare-plugin.sh /path/to/plugin-manifest.json
This creates ./build/<plugin-name>/ with the official plugin format, scaffolding, and auto-runs validation.
Step 3: Validate
$SCRIPTS/validate-plugin.sh ./build/<plugin-name>
Step 4: Sync to Monorepo
$SCRIPTS/sync-monorepo.sh --add-plugin <plugin-name> ~/dev/claude-code-skills
cd ~/dev/claude-code-skills
git add -A && git commit -m "feat: add <plugin-name> plugin" && git push
Step 5: Install (Consumer)
git clone https://github.com/USER/claude-code-skills.git /tmp/ccs
/tmp/ccs/scripts/install-plugin.sh /tmp/ccs/plugins/<plugin-name>
rm -rf /tmp/ccs
Key Decisions
| Decision | Choice | Rationale |
|---|
.claude/ in .gitignore | Always | Contains settings.local.json with user-specific permissions |
| License | MIT default | Most permissive, standard for open-source tools |
| Version from frontmatter | Use as-is | Avoids version mismatch between SKILL.md and tag |
| No install script for skills | By design | git clone IS the installer — no extra ceremony |
| Flat copy, not subtree | By design | Simpler mental model; local dir is single source of truth |
| Monorepo README | Auto-generated | Catalog table derived from SKILL.md frontmatter; never hand-edit |
Plugins in plugins/ subfolder | By design | Different structure than bare skills; separates concerns |
| Plugin build manifest (JSON) | jq dependency | Plugins bundle multiple sources; CLI-only would be unwieldy |
install-plugin.sh in monorepo | Consumer-facing | Users need it to install plugins; not just an author tool |
See Also