Skip to content

Latest commit

 

History

History
428 lines (318 loc) · 23 KB

maintaining.md

File metadata and controls

428 lines (318 loc) · 23 KB

Maintaining uSpec

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-skills npm package that strangers install with npx 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.

Mental model in one diagram

┌────────────────────────────────────────────────────────────────────┐
│  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.

Folder map

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.

How the install actually works

When a user runs npx uspec-skills@latest init in some random folder, here's the chain:

  1. npx queries registry.npmjs.org for the package called uspec-skills, gets the version tagged latest (0.3.3 for the V2.7 release), and downloads the tarball.
  2. The tarball contains dist/index.js (the bundled CLI) and templates/ (the skills + references that ship with this version).
  3. npx runs the bin defined in package.json (uspec-skills → ./dist/index.js).
  4. The CLI walks up from the user's current directory looking for a project marker (.git/, package.json, or uspecs.config.json). If none is found, it bootstraps into the current directory (this is the fix that shipped in 0.1.1).
  5. The CLI's render engine reads each SKILL.md from templates/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).
  6. It copies templates/references/ verbatim into ./references/ in the user's project.
  7. It writes uspecs.config.json with 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.

What changes in this repo, and how each kind of change ships

Editing a skill or reference file

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 version

That'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.

Editing the CLI itself (commands, render engine, error messages)

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.

Adding a new skill

  1. Create skills/<new-skill-name>/SKILL.md following the structure of an existing one. Use tokens for cross-references.
  2. If it needs new shared docs, create references/<area>/*.md and link to them from the SKILL.md via {{ref:area/file.md}}.
  3. 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.
  4. Update the skills tables in implementation.md, CLAUDE.md, and AGENTS.md.
  5. Bump version, rebuild, publish.

Editing the Figma plugin (figma-plugin/)

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:

  1. Edit files under figma-plugin/src/.
  2. Build and test locally (see figma-plugin/README.md).
  3. Commit and push to GitHub.
  4. 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.

Editing docs (docs/)

The Mintlify docs at uspec.design auto-deploy from main:

  1. Edit .mdx files under docs/.
  2. Push to main.
  3. 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.

Versioning rules of thumb

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.

Publishing in detail

You'll do this every time you ship. This is the canonical sequence.

Prerequisites (one-time)

  • An npm account with publish rights to uspec-skills. You're iguisard.
  • 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 --otp flag is needed.
  • Logged in: npm whoami --registry=https://registry.npmjs.org/ should print iguisard. If it doesn't, run npm login --registry=https://registry.npmjs.org/.

The publish sequence

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.

GitHub tag and release

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 \
  --latest

Write /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.md

You'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.

Layered registry safety

Before any npm publish runs, three layers of protection check that you're publishing to public npm.

  1. packages/cli/package.json → publishConfig.registry pins https://registry.npmjs.org/ for this package.
  2. packages/cli/.npmrc sets registry=https://registry.npmjs.org/ for this directory, overriding any user-level or environment registry config.
  3. packages/cli/scripts/check-registry.mjs runs as prepublishOnly and 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.

Smoke testing before publish

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-fresh

If init and doctor succeed in the sandbox, you're safe to publish.

When something goes wrong

"I published a broken version"

You can't unpublish a version older than 72 hours from npm without contacting support. Don't try. Instead, ship a patch.

  1. Edit the bug.
  2. Bump the patch version (0.2.0 → 0.2.1).
  3. Rebuild and publish.
  4. Tell users in the changelog. The bad version is forever in the version list, but latest now points at the fix, so anyone running npx uspec-skills@latest gets 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 keeps using the old version"

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/_npx

"publish failed with EOTP"

npm 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 public

If 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).

"publish failed with E403 / Forbidden"

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-skills since you own it).
  • The version already exists on npm. You can't republish the same version. Bump and try again.

"publish failed with ABORTED: wrong registry"

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 to

If 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.

Quick reference

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> ...

Useful pointers