Files
Oz 4b3668c60a Fail closed on an incomplete regen; add regression tests
Review of the previous revision found that pruning by checking whether
a copied path existed under the temp dir failed open: if skills add
exited 0 without actually copying anything (e.g. a broken CLI run),
every warpdotdev/common-skills entry looked stale and got deleted,
while the one entry from another source survived. That result is
worse than the original bug and would auto-merge into warp and
warp-server.

Restructure the fix: regenerate the warpdotdev/common-skills portion
of the lock from scratch in a clean, unseeded temp directory instead
of seeding it with the existing lock and pruning what didn't reappear.
skills add can then only ever emit entries for skills it actually
finds, so nothing stale can survive the merge in the first place.
Entries from any other source are carried over from the existing lock
unmodified. As a backstop, fail closed with a non-zero exit and leave
the existing lock untouched if regenerating produces no
warpdotdev/common-skills entries at all while the existing lock has
some - that is a sign the run didn't discover or copy anything, not
that every skill vanished upstream.

Add scripts/tests/update_common_skills_lock_test.sh, a self-contained
regression suite that runs the real script against a fake npx
(scripts/tests/fake-bin/npx) modeling `skills add ... --copy`
deterministically from a local fixture directory, with no network
access. Covers a rename, a plain deletion, foreign-source
preservation, idempotence, and the fail-closed guard. Wire it into a
new CI workflow that runs on push to main and on every pull request.

Co-Authored-By: Warp Agent <agent@warp.dev>
2026-08-13 15:45:21 +00:00
..

Common skills scripts

These scripts help consuming repositories install, remove, and verify shared agent skills from warpdotdev/common-skills.

Files

  • resolve_common_skills: resolves and executes scripts from this directory, a local override directory, or raw GitHub.
  • install_common_skills: installs or updates common skills, then verifies the installed contents.
  • update_common_skills_lock: regenerates an existing common-skills lockfile without installing skills.
  • remove_common_skills: removes installed common skills from a selected target.
  • tests/: regression tests for the scripts in this directory (currently update_common_skills_lock), run in CI.

Quick start

Install common skills into the current checkout:

scripts/install_common_skills --project

Install common skills globally:

scripts/install_common_skills --global

Install only when the lock hash has changed or a locked skill is missing:

scripts/install_common_skills --project --if-needed --non-interactive

Verify the installed skills without installing:

scripts/install_common_skills --verify-only

Remove installed common skills:

scripts/remove_common_skills --project

Invoking from client repositories

Client repositories do not need to vendor these scripts. They should keep a small script/resolve_common_skills shim that loads this repository's scripts/resolve_common_skills and forwards arguments to it. For example, a client repo's script/resolve_common_skills shim can look like this:

#!/usr/bin/env bash

set -eo pipefail

COMMON_SKILLS_REPO="warpdotdev/common-skills"
COMMON_SKILLS_REF="${WARP_COMMON_SKILLS_REF:-main}"
RAW_BASE_URL="${WARP_COMMON_SKILLS_RAW_BASE_URL:-https://raw.githubusercontent.com/${COMMON_SKILLS_REPO}/${COMMON_SKILLS_REF}/scripts}"

if [[ -n "${WARP_COMMON_SKILLS_SCRIPTS_DIR:-}" ]]; then
  exec "${WARP_COMMON_SKILLS_SCRIPTS_DIR%/}/resolve_common_skills" "$@"
fi

curl -fsSL "${RAW_BASE_URL%/}/resolve_common_skills" | bash -s -- "$@"

The shim's default path executes this repository's shared resolver through curl:

curl -fsSL "https://raw.githubusercontent.com/warpdotdev/common-skills/${WARP_COMMON_SKILLS_REF:-main}/scripts/resolve_common_skills" | bash -s -- install_common_skills -- --repo-root "${REPO_ROOT}" --if-needed --prompt-for-target

Consumer bootstrap and run scripts call the shim instead of calling curl directly:

./script/resolve_common_skills install_common_skills -- --repo-root "${REPO_ROOT}" --if-needed --prompt-for-target

With an explicit target, client repos pass the same installer flags they would pass locally:

./script/resolve_common_skills install_common_skills -- --repo-root "${REPO_ROOT}" --global --if-needed
./script/resolve_common_skills install_common_skills -- --repo-root "${REPO_ROOT}" --project --force

The resolver supports these development overrides:

  • WARP_COMMON_SKILLS_REF=<git-ref>: fetch scripts from a branch, tag, or commit in warpdotdev/common-skills; also forwarded to the installer so missing-lock creation and interactive lock update checks use the same ref.
  • WARP_COMMON_SKILLS_SCRIPTS_DIR=/path/to/common-skills/scripts: execute scripts from a local checkout or worktree instead of fetching from GitHub.
  • WARP_COMMON_SKILLS_RAW_BASE_URL=https://...: override the raw URL base used by the resolver.

Developing against these scripts from a client repo

For normal client-repo development, test the remote path first:

WARP_COMMON_SKILLS_REF=<your-common-skills-branch> ./script/bootstrap --install-common-skills-globally
WARP_COMMON_SKILLS_REF=<your-common-skills-branch> ./script/run --install-common-skills

Use WARP_COMMON_SKILLS_SCRIPTS_DIR when iterating on unpushed local script changes:

WARP_COMMON_SKILLS_SCRIPTS_DIR=/path/to/common-skills/scripts ./script/run --install-common-skills

Client repos should keep their own skills-lock.json checked in. Normal install flows use that lock as the source of truth; interactive flows may ask to update it when this repo would produce a different lock. Review and commit lock changes in the client repo separately from changes to this repo.

Install script

install_common_skills installs common agent skills from warpdotdev/common-skills. When skills-lock.json is missing, the script creates it by running the skills CLI against the source repo and selecting all valid skills. This is intentionally dynamic: the script should not hardcode a fixed list of common skill names. Set WARP_COMMON_SKILLS_REF=<git-ref> to create the lock from a branch, tag, or commit such as warpdotdev/common-skills#my-branch. When skills-lock.json already exists and the script is running interactively, it checks whether warpdotdev/common-skills would produce an updated lock before prompting for a project/global install target. If WARP_COMMON_SKILLS_REF is set, the check uses that branch, tag, or commit as the candidate source. If a different lock is available, it asks before updating skills-lock.json and reinstalling from the updated lock. Non-interactive and verify-only runs skip this upstream update prompt and use the existing lock. When skills-lock.json already exists, the script installs from the lock file:

  • Project target: npx --yes skills@1.5.6 experimental_install
  • Global target: npx --yes skills@1.5.6 add warpdotdev/common-skills --global --agent warp --skill <locked skills> --yes --copy The script supports:
  • --repo-root <path>: repository containing skills-lock.json.
  • --project: install into .agents/skills.
  • --global: install into ~/.agents/skills.
  • --if-needed: skip when the stamp matches the lock and locked skills are present.
  • --prompt-for-target: prompt for project/global when no explicit target is set.
  • --non-interactive: do not prompt; fail when no target is explicit.
  • --force: install even if already up to date.
  • --quiet: suppress no-op output.
  • --verify-only: verify installed skills match skills-lock.json without installing. Successful install and skip paths verify that exactly one install target contains the locked common skills and that each installed skill matches skills-lock.json. Project installs add local Git exclude entries for only the locked common-skill directories so unrelated project skills in .agents/skills remain visible to Git. Global installs are shared across client repositories. A second repo pinned to the same lock verifies and succeeds without unnecessarily reinstalling; a repo pinned to a different lock fails with a version-mismatch error instead of overwriting the shared global install.

Update lock script

update_common_skills_lock non-interactively regenerates an existing skills-lock.json from the default branch of warpdotdev/common-skills without installing skills into the target repository. It regenerates the warpdotdev/common-skills portion of the lock from scratch in a clean, unseeded temporary directory, merges in any entries from other sources unchanged, and only then copies back a changed skills-lock.json. Generator failures and missing candidate output fail the command instead of retaining the existing lock. The skills CLI merges into whatever lock it finds and never removes an entry for a skill that disappeared from the source, so seeding the temporary directory with the existing lock (as an earlier version of this script did) let a renamed or deleted skill leave a stale entry behind. Regenerating in a clean directory removes that failure mode structurally: skills add can only ever emit entries for skills it actually finds in the source, so nothing stale can survive the merge. Entries whose source is not warpdotdev/common-skills, including hand-added ones, are always carried over from the existing lock untouched. The script also fails closed: if regenerating produces no warpdotdev/common-skills entries at all while the existing lock has some, it aborts with a non-zero exit and leaves skills-lock.json untouched, rather than writing a lock that silently dropped every locked skill. Run it from a consuming repository:

/path/to/common-skills/scripts/update_common_skills_lock --repo-root /path/to/consumer

The downstream lockfile update workflow uses this command before opening lockfile-only pull requests.

Tests

scripts/tests/update_common_skills_lock_test.sh is a self-contained regression suite for this script. It runs the real script against scripts/tests/fake-bin/npx, a fake npx that deterministically models skills add ... --copy from a local fixture directory instead of the network, and covers a rename, a plain deletion, foreign-source preservation, idempotence (a second run is byte-identical and reports up to date), and the fail-closed guard above. .github/workflows/test-scripts.yml runs it on every push to main and on every pull request.

Downstream lockfile workflow

.github/workflows/update-downstream-skill-locks.yml runs after every push to main and opens lockfile-only pull requests against warpdotdev/warp and warpdotdev/warp-server when their locks change. Each pull request requests the author of the originating common-skills pull request when possible and enables squash auto-merge. Direct pushes and authors who cannot review a target repository do not prevent pull request creation. The workflow requires a dedicated GitHub App installed on both downstream repositories with contents and pull-request write access. Configure its App ID as the COMMON_SKILLS_SYNC_APP_ID Actions variable and its private key as the COMMON_SKILLS_SYNC_APP_PRIVATE_KEY Actions secret in common-skills.

Remove script

remove_common_skills removes common agent skills listed in skills-lock.json. The script supports:

  • --repo-root <path>: repository containing skills-lock.json.
  • --project: remove from .agents/skills.
  • --global: remove from ~/.agents/skills.
  • --clear-lock: remove skills-lock.json after removing locked skills. The remove script only deletes paths derived from the lock file and includes path checks before removing project skill directories.

Verification

install_common_skills --verify-only checks that exactly one install target contains the locked common skills and that each installed skill matches skills-lock.json. Verification also runs after successful install and skip paths. Verification fails if:

  • skills-lock.json is missing.
  • Locked skills are installed in both project and global targets.
  • Locked skills are missing from both targets.
  • Any installed skill directory hash differs from the lock file.

Environment variables

  • WARP_SKIP_COMMON_SKILLS_INSTALL=1: skip installation.
  • WARP_COMMON_SKILLS_INSTALL_TARGET=project|global: explicit install or removal target.
  • WARP_COMMON_SKILLS_TARGET_REPO_ROOT=/path/to/repo: repository containing skills-lock.json and project-local .agents/skills.
  • WARP_COMMON_SKILLS_REF=<git-ref>: use a specific warpdotdev/common-skills branch, tag, or commit when creating a missing lock or checking interactively for lock updates.

Lock hash stamps

After a successful install, install_common_skills writes a stamp with the current skills-lock.json hash.

  • Project installs use git path warp/common-skills-lock.hash, or .agents/skills/.common-skills-lock.hash when git metadata is unavailable.
  • Global installs use ~/.agents/warp/common-skills-lock.hash. The stamp lets --if-needed avoid reinstalling when the lock file and installed skills are already current.