I kept writing the same Agent Skill twice. A SKILL.md that worked in Cursor would quietly fail to upload to Claude Web, because Web and Cowork cap the skill description at 200 characters and the official validator doesn't check for it. So you find out when the upload bounces, not when you write the file. Then there's the .skill zip format for those uploads, and the bit where Claude rejects any archive with files at the zip root. Small rules, each one its own evening of discovering it the hard way.

So I built skillship, a small CLI that takes one SKILL.md and makes it portable across Cursor, Claude Code, Claude Web, and Claude Cowork.

What it actually is

skillship is a thin orchestration layer, and I want to be precise about that because it's easy to oversell. It does not reimplement the multi-agent install matrix: that's npx skills, and skillship shells out to it. It does not host a registry. It adds the four things I kept needing that nothing else covered:

  • Per-surface validation profiles, including Claude's 200-character description cap that the official validator skips.
  • .skill packaging for the Claude Web and Cowork uploads, with each skill under its own folder so the archive isn't rejected.
  • init scaffolding with release-please CI and Conventional Commits wired up.
  • Cursor rules and hooks install, so installing a skill also drops in its trigger rule and merges its hooks instead of leaving you to copy files by hand.

Install it

It runs on Node.js 18 or newer, which is what gives you npx.

The way I'd recommend is to install the bundled /skillship skill itself, which then drives the CLI for you: you ask it to publish a skill and it finds or scaffolds the SKILL.md, validates it, fixes what's broken, and installs or packages it for the surface you want.

npx skills add shivdeepak/skillship

Or just call the CLI directly, no install:

npx skillship@latest <command>

The commands

There are five, and the names say what they do:

skillship validate <dir>    [--profile <p>] [--json]
skillship package  <dir>    [--out <dir>]
skillship install  [source] [-a <a,b>] [--global] [--copy]
skillship init     [name]   [--ci] [--snippets] [--new-dir]
skillship doctor

validate and package default to the current directory and discover every skill under it (a lone SKILL.md, or each skill under skills/), so a repo can hold a whole family of sibling skills and the commands find them all.

validate

This is the part I reach for most. It parses the SKILL.md frontmatter and body and runs checks per profile:

  • name is present, lowercase with hyphens, and matches its parent folder.
  • description is non-empty and has no angle brackets.
  • description length is at most 1024 for the spec and Cursor profiles, and at most 200 for claude-web and claude-cowork.
  • The body is flagged if it runs past 500 lines.

--profile is one of spec, cursor, claude-web, claude-cowork, or all, and all is the default because it's the strictest combination. --json gives you machine-readable output for CI. If the Python spec validator (agentskills) happens to be on your PATH, its findings get merged in, but it's never a hard dependency.

package

package runs validate --profile all first and aborts if anything fails, then bundles every discovered skill into a single <name>.skill zip. Each skill sits under its own flat folder (a :-namespaced name like skillship:author becomes skillship-author/), because Claude rejects archives with files at the root. It also leaves out the noise: __pycache__/, .DS_Store, node_modules/, dist/, .git/.

install

install takes a local path or any remote ref that npx skills add understands: owner/repo, owner/repo@skill-name, a GitHub or GitLab URL, or an SSH git URL. For remote sources it does a shallow clone into a temp directory, finds the SKILL.md, installs, and cleans up.

For filesystem agents it shells out to npx skills add. For the upload-only surfaces it prints the upload instructions instead. In an interactive terminal it asks the questions worth asking before it acts: whether to install all the sibling skills it found, global or project scope, copy or symlink. Pass -y to skip the prompts in CI.

The Cursor case is the one I'm happiest with. If the skill directory carries a cursor/rules/*.mdc or a cursor/hooks.json, installing also copies the rule into place and merges the hooks by event and command. So skillship install is genuinely one step: the skill, its trigger, and its hooks, without hand-copying anything.

init

init scaffolds a skill repo that auto-releases through release-please and Conventional Commits. --ci adds the GitHub Actions workflows, --snippets adds the Cursor rule and hooks file that install will later deploy. By default it scaffolds into the current directory; --new-dir makes a fresh project folder instead.

The detail I like: re-running init on an existing skill only writes the files that are missing and leaves everything else alone, including your authored SKILL.md. So you can come back later and backfill the CI or the snippets without it clobbering your work.

doctor

Checks the local environment: Node 18+ and npx are required, gh and agentskills are optional. It's the thing to run first when an install behaves strangely.

How I actually use it

Runbook, the other skill I've written about, is published with skillship. It's a repo with skills under skills/, a release-please config, and the GitHub Actions workflow that validates the skill on every push and uploads runbook.skill to a release when the version bumps. I write the SKILL.md, commit with a feat: or fix: prefix, and merging the generated release PR publishes the package. Publishing uses npm trusted publishing over OIDC, so there's no token sitting in a secret anywhere.

None of that is novel on its own. release-please is release-please. What skillship gave me was not having to assemble it from scratch every time I start a new skill, and not finding out at upload time that a description was eleven characters too long.

That last one is the whole reason this exists. I wrote a validator for a rule I kept forgetting, and the rest grew around it.