Audience. This is the operator's manual for the person who maintains this repo. It explains how the code on disk turns into the
uspec-skillsnpm package that strangers install withnpx uspec-skills init, and exactly what to do when you want to change something.Written for someone seeing CLI publishing for the first time. If you've shipped npm packages before, the Quick reference at the bottom is probably enough.
Architecture deep-dive. This file is the release manual. For the system architecture — how skills, MCP, the Figma plugin, and the CLI fit together at a code level — see
implementation.md. The two files are deliberately separate: this one tells you how to ship, that one tells you how the system works.Active release. For the branch currently being prepared as uSpec V2.7, use the checkboxes in
release-checklist.md. This manual explains each procedure; the checklist records whether the release candidate has completed it.
┌────────────────────────────────────────────────────────────────────┐
│ THIS REPO (source of truth) │
│ │
│ skills/<name>/SKILL.md ──┐ │
│ references/<area>/*.md ──┤ │
│ │ │
│ ▼ │
│ packages/cli/scripts/build.mjs │
│ │ │
│ ▼ │
│ packages/cli/templates/ │
│ packages/cli/dist/index.js │
│ │ │
└────────────────────────────────────┼───────────────────────────────┘
│
│ npm publish --access public
▼
┌──────────────────────────────────┐
│ registry.npmjs.org │
│ uspec-skills@0.3.x │
└──────────────────────────────────┘
│
│ npx uspec-skills@latest init
▼
┌────────────────────────────────────────────────────────────────────┐
│ USER PROJECT (e.g. /tmp/my-design-system) │
│ │
│ .cursor/skills/ (or .claude/skills/, .agents/skills/) │
│ references/ │
│ uspecs.config.json │
└────────────────────────────────────────────────────────────────────┘
The headline rule: skills/ and references/ at the repo root are the source of truth. Everything else is derived.
uSpec/
├── skills/ SOURCE OF TRUTH for skills (platform-neutral)
│ └── <name>/SKILL.md uses {{skill:name}} and {{ref:area/file.md}} tokens
├── references/ SOURCE OF TRUTH for shared docs
│ └── <area>/*.md agent instructions, reference material
│
├── packages/cli/ the published CLI lives here
│ ├── package.json "name": "uspec-skills", "version": "0.3.x"
│ ├── .npmrc pins this folder's npm registry to public npm
│ ├── src/ TypeScript source for the CLI
│ ├── scripts/
│ │ ├── build.mjs bundles src/ → dist/, copies skills+refs → templates/
│ │ └── check-registry.mjs safety guard, runs before every publish
│ ├── dist/ BUILT ARTIFACT (not committed in production sense — see below)
│ └── templates/ BUILT ARTIFACT (copy of /skills + /references)
│
├── .cursor/skills/ GENERATED — what `npx uspec-skills install --platform cursor` writes
├── .claude/skills/ (same, for claude-code) ← gitignored in user projects
├── .agents/skills/ (same, for codex) ← gitignored in user projects
│
├── figma-plugin/ the Figma Desktop plugin (separate from npm package)
├── docs/ Mintlify docs site (uspec.design)
├── README.md GitHub-facing README
├── implementation.md system architecture reference for AI agents
└── maintaining.md this file
The dist/ and templates/ folders inside packages/cli/ are build outputs. They're regenerated by npm run build and they ship inside the npm tarball, but you do not edit them by hand.
When a user runs npx uspec-skills@latest init in some random folder, here's the chain:
npxqueriesregistry.npmjs.orgfor the package calleduspec-skills, gets the version taggedlatest(0.3.3 for the V2.7 release), and downloads the tarball.- The tarball contains
dist/index.js(the bundled CLI) andtemplates/(the skills + references that ship with this version). npxruns thebindefined inpackage.json(uspec-skills→./dist/index.js).- The CLI walks up from the user's current directory looking for a project marker (
.git/,package.json, oruspecs.config.json). If none is found, it bootstraps into the current directory (this is the fix that shipped in0.1.1). - The CLI's render engine reads each
SKILL.mdfromtemplates/skills/, rewrites the{{skill:...}}and{{ref:...}}tokens to host-correct values, and writes the result into.cursor/skills/<name>/SKILL.md(or.claude/skills/,.agents/skills/depending on the platform choice). - It copies
templates/references/verbatim into./references/in the user's project. - It writes
uspecs.config.jsonwith their environment + MCP choice + the CLI version that did the install.
The user is then "installed". They never need this repo or git to use uSpec.
This is the most common change. You edited skills/create-color/SKILL.md, or references/structure/agent-structure-instruction.md.
┌─────────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ edit skills/ or │ ─▶ │ bump version │ ─▶ │ npm publish │ ─▶ │ users get it │
│ references/ │ │ in package. │ │ │ │ via update │
│ │ │ json │ │ │ │ │
└─────────────────┘ └──────────────┘ └──────────────┘ └──────────────┘
Step by step:
# 1. Make your edits to skills/<name>/SKILL.md or references/<area>/*.md.
# Use {{skill:other-skill}} and {{ref:area/file.md}} for cross-references —
# bare relative paths break on Claude Code and Codex.
# 2. Bump the patch version in packages/cli/package.json
# (e.g. 0.2.0 → 0.2.1). The "version" field is the only required edit.
# 3. From inside packages/cli, rebuild — this re-copies skills/ and
# references/ into templates/ and re-bundles dist/index.js
cd packages/cli
npm run build
# 4. Locally smoke test (optional but recommended) — see
# "Smoke testing before publish" section below.
# 5. Publish (opens your browser for npm web-based 2FA approval)
npm publish --access public
# 6. Verify it landed
npm view uspec-skills version # should print your new versionThat's it — the package is now live on npm. The next time anyone runs npx uspec-skills update (or init in a new project), they'll get your changes.
You changed something under packages/cli/src/. Same flow as above — bump the version, rebuild, publish. The build script bundles src/index.ts into dist/index.js via esbuild.
- Create
skills/<new-skill-name>/SKILL.mdfollowing the structure of an existing one. Use tokens for cross-references. - If it needs new shared docs, create
references/<area>/*.mdand link to them from the SKILL.md via{{ref:area/file.md}}. - If the skill is a new spec type with its own template, also update
firstrun(skills/firstrun/SKILL.md) so it knows to extract the new template's component key during onboarding. - Update the skills tables in
implementation.md,CLAUDE.md, andAGENTS.md. - Bump version, rebuild, publish.
The Figma plugin does not ship in the npm package. It's distributed separately on the Figma Community, where users install it directly — no local build required. The source stays open in figma-plugin/ for contributors who want to run a modified build locally.
This repository contains the public Community build by default (npm run build). It requires
the user to paste a Figma component link because public plugins cannot rely on figma.fileKey;
after the first valid entry, the link is remembered in that document and prefilled on later runs.
The same source can also produce an organization-internal, no-link edition
(npm run build:internal). That edition's plugin id lives in gitignored
figma-plugin/editions.local.json — copy editions.local.json.example and do not commit it.
Before publishing from Figma Desktop, confirm you loaded the edition you intend to ship: Community
id 1635184425006534227 (link required), or your organization plugin (no link).
If you change the plugin:
- Edit files under
figma-plugin/src/. - Build and test locally (see
figma-plugin/README.md). - Commit and push to GitHub.
- Publish the new version to the Figma Community from Figma Desktop so users get the update. No npm publish involved — the Community release is a separate manual step from the npm package.
The npm package is only the AI skills + the install CLI. The plugin and the npm package can drift in version — they're loosely coupled via the _base.json schema. If you change the schema, bump both: edit the plugin and publish it to Community, then update the validator and any consumer skills (mainly extract-* and create-component-md), and publish a new npm version.
The Mintlify docs at uspec.design auto-deploy from main:
- Edit
.mdxfiles underdocs/. - Push to
main. - Mintlify deploys within 1–2 minutes. No publish step.
If you add or remove a page, update docs/docs.json so it appears in the navigation.
You're on 0.3.x. The 0.x.y range is npm's convention for "early, expect breakage." Use it deliberately:
| What you changed | Bump | Example |
|---|---|---|
| Fixed a bug, edited a skill, improved an error message | patch | 0.3.1 → 0.3.2 |
Added a new skill, added a new CLI command, added a new field to uspecs.config.json |
patch (still 0.x — anything goes) | 0.3.2 → 0.3.3 |
| Renamed a CLI flag, changed config schema, removed a skill | minor | 0.3.x → 0.4.0 |
| You're confident the CLI surface is stable and you'll commit to not breaking it | major | 0.x.y → 1.0.0 |
Bumps in 0.3.x are cheap. Don't agonize. Once you ship 1.0.0, every breaking change costs a major bump.
Versioning the product vs. the CLI. "uSpec" the product is at V2.7 (per the changelog and docs). uspec-skills 0.3.3 ships that release on npm and versions on its own track. The public Figma Community plugin has a third version, 2.7.0. These numbers are independent: the product version goes in the changelog and GitHub tag, the npm version goes in packages/cli/package.json, and the plugin version goes in figma-plugin/package.json plus its extraction metadata and footer.
You'll do this every time you ship. This is the canonical sequence.
- An npm account with publish rights to
uspec-skills. You'reiguisard. - 2FA enabled on your npm account (npm requires it for new publishers by default). npm uses web-based 2FA by default — when you run
npm publish, it opens a browser tab where you approve the publish. No authenticator code or--otpflag is needed. - Logged in:
npm whoami --registry=https://registry.npmjs.org/should printiguisard. If it doesn't, runnpm login --registry=https://registry.npmjs.org/.
Always run from inside packages/cli/ so the local .npmrc (which pins this folder's registry to public npm) is honored.
cd packages/cli
# 1. Bump version. Edit package.json's "version" field by hand,
# or use `npm version patch` to bump and tag in one step.
# 2. Rebuild
npm run build
# 3. Sanity-check what would be published, no upload yet
npm publish --dry-run
# Verify the output ends with: + uspec-skills@<your-new-version>
# and that it lists Publishing to https://registry.npmjs.org/
# 4. Real publish (opens your browser for web-based 2FA approval)
npm publish --access public
# 5. Verify it landed
npm view uspec-skills version
# Should print your new version. Sometimes there's a few-second lag.After merging to main and publishing to npm and the Figma Community, tag the release commit and create a GitHub Release. Product version (v2.7.0), npm version (0.3.3), and plugin version (2.7.0) are separate — the tag uses the product version, while each package uses its own metadata.
# From repo root, on main after merge
git pull origin main
git tag -a v2.7.0 -m "uSpec V2.7 / uspec-extract 2.7.0 / uspec-skills 0.3.3"
git push origin v2.7.0
# Create the GitHub Release (notes from docs/help/changelog.mdx)
gh release create v2.7.0 \
--title "uSpec V2.7" \
--notes-file /tmp/v2.7-release-notes.md \
--latestWrite /tmp/v2.7-release-notes.md from the matching <Update> block in docs/help/changelog.mdx before running gh release create. Mintlify auto-deploys docs from main within 1–2 minutes — no separate docs publish step.
If a product release tag already exists on main but has no GitHub Release (e.g. v2.5.0 was tagged without gh release create), backfill it:
gh release create v2.5.0 --title "uSpec V2.5" --notes-file /tmp/v2.5-release-notes.mdYou'll see warnings like:
npm warn publish "bin[uspec-skills]" script name was cleaned
npm warn publish "repository.url" was normalized to "git+https://..."
These are auto-corrections npm makes silently. They are harmless and don't affect the published package.
Before any npm publish runs, three layers of protection check that you're publishing to public npm.
packages/cli/package.json→publishConfig.registrypinshttps://registry.npmjs.org/for this package.packages/cli/.npmrcsetsregistry=https://registry.npmjs.org/for this directory, overriding any user-level or environment registry config.packages/cli/scripts/check-registry.mjsruns asprepublishOnlyand aborts with a clear error if the effective registry is not public npm.
If any of these is broken or missing, the publish stops. This is paranoid by design — it's much easier to recover from a failed publish than from accidentally pushing source code to a private registry.
If you ever see ABORTED: npm publish would target the wrong registry, do not bypass it. Fix the registry config and try again.
Highly recommended for any change that touches the CLI commands themselves, or any non-trivial edit. Lets you run the package in a fresh empty directory exactly as a user would, but without uploading anything.
cd packages/cli
# 1. Build and pack into a local tarball
npm run build
npm pack
# Produces uspec-skills-<version>.tgz in the current dir.
# 2. Set up a sandbox to install into
mkdir /tmp/uspec-tooldir
cd /tmp/uspec-tooldir
npm init -y >/dev/null
npm install /Users/ian.guisard/uSpec/packages/cli/uspec-skills-<version>.tgz
# 3. Try the CLI in another fresh, empty dir
mkdir /tmp/uspec-fresh
cd /tmp/uspec-fresh
/tmp/uspec-tooldir/node_modules/.bin/uspec-skills init --platform cursor --mcp figma-console --yes
/tmp/uspec-tooldir/node_modules/.bin/uspec-skills doctor
# 4. Inspect the output: .cursor/skills/, references/, uspecs.config.json
ls -la
cat uspecs.config.json
# 5. Cleanup
cd /Users/ian.guisard/uSpec/packages/cli
rm uspec-skills-<version>.tgz
rm -rf /tmp/uspec-tooldir /tmp/uspec-freshIf init and doctor succeed in the sandbox, you're safe to publish.
You can't unpublish a version older than 72 hours from npm without contacting support. Don't try. Instead, ship a patch.
- Edit the bug.
- Bump the patch version (
0.2.0 → 0.2.1). - Rebuild and publish.
- Tell users in the changelog. The bad version is forever in the version list, but
latestnow points at the fix, so anyone runningnpx uspec-skills@latestgets the good one.
If the bug is severe (e.g. data loss, accidental destructive action), you can also npm deprecate uspec-skills@<bad-version> "use 0.2.1+ instead" which adds a warning when anyone tries to install that version specifically.
npx caches packages in ~/.npm/_npx/. After publishing a new version, users (and you, when verifying) may keep hitting the old cached binary even with @latest.
Fix: pin the exact version (npx uspec-skills@0.2.1 init instead of @latest). That forces a fresh download because the cache key changes.
For a stuck local cache:
rm -rf ~/.npm/_npxnpm error code EOTP
npm error This operation requires a one-time password from your authenticator.
The browser approval window for web-based 2FA expired or was dismissed before you confirmed. Re-run the publish — it will open a fresh approval tab:
npm publish --access publicIf your account is set to authenticator-app 2FA instead of web-based, pass --otp=<6-digit-code> from your authenticator on each publish (e.g. npm publish --access public --otp=123456).
Either:
- You're not logged in. Run
npm whoami --registry=https://registry.npmjs.org/. - Someone else owns the package name (shouldn't happen for
uspec-skillssince you own it). - The version already exists on npm. You can't republish the same version. Bump and try again.
The safety guard caught you. See Layered registry safety. Don't bypass — fix the registry config:
cd packages/cli
npm config get registry # what your shell currently uses
cat .npmrc # what this folder pins it toIf npm config get registry doesn't print https://registry.npmjs.org/, run:
npm publish --access public --registry=https://registry.npmjs.org/The --registry flag overrides everything else for that one command.
For when you've done this enough times to skip the explanations:
# Standard ship
cd packages/cli
# bump "version" in package.json
npm run build
npm run test # prepare tests + typecheck via build
npm publish --dry-run # sanity check
npm publish --access public # opens browser for web-based 2FA approval
npm view uspec-skills version # verify
# After merge to main
git tag -a v2.7.0 -m "uSpec V2.7 / uspec-extract 2.7.0 / uspec-skills 0.3.3"
git push origin v2.7.0
gh release create v2.7.0 --title "uSpec V2.7" --notes-file /tmp/v2.7-release-notes.md --latest# Verify a fresh install works (use the version you just published)
rm -rf /tmp/uspec-real && mkdir /tmp/uspec-real && cd /tmp/uspec-real
npx --yes uspec-skills@<just-published-version> init --platform cursor --mcp figma-console --yes
npx --yes uspec-skills@<just-published-version> doctor| Want to... | Do this |
|---|---|
| Edit a skill | Edit skills/<name>/SKILL.md, bump version, build, publish |
| Edit a reference doc | Edit references/<area>/*.md, bump version, build, publish |
| Add a new skill | New folder under skills/, update implementation.md/CLAUDE.md/AGENTS.md, bump, build, publish |
| Edit CLI behavior | Edit packages/cli/src/, bump, build, publish |
| Ship a product release | Merge to main, publish npm, tag vX.Y.Z, gh release create |
| Edit docs site | Edit docs/*.mdx, push to main. No publish. |
| Edit Figma plugin | Edit figma-plugin/src/, push to main, publish the update to Figma Community. No npm publish. |
| Roll back a bad publish | Ship a patch. Don't unpublish. |
| Get out of npx cache jail | Pin the exact version: npx uspec-skills@<exact> ... |
- npm package page: npmjs.com/package/uspec-skills
- Package settings (deprecate, transfer ownership): npmjs.com/package/uspec-skills/access
- Architecture deep-dive for AI agents:
implementation.md, specifically the Skill Source and CLI section - Token / cross-reference convention (
{{skill:}},{{ref:}}):packages/cli/src/render.ts - Build script:
packages/cli/scripts/build.mjs - Registry safety guard:
packages/cli/scripts/check-registry.mjs - User-facing changelog:
docs/help/changelog.mdx