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>
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 (currentlyupdate_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 inwarpdotdev/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 --copyThe script supports: --repo-root <path>: repository containingskills-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 matchskills-lock.jsonwithout installing. Successful install and skip paths verify that exactly one install target contains the locked common skills and that each installed skill matchesskills-lock.json. Project installs add local Git exclude entries for only the locked common-skill directories so unrelated project skills in.agents/skillsremain 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 containingskills-lock.json.--project: remove from.agents/skills.--global: remove from~/.agents/skills.--clear-lock: removeskills-lock.jsonafter 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.jsonis 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 containingskills-lock.jsonand project-local.agents/skills.WARP_COMMON_SKILLS_REF=<git-ref>: use a specificwarpdotdev/common-skillsbranch, 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.hashwhen git metadata is unavailable. - Global installs use
~/.agents/warp/common-skills-lock.hash. The stamp lets--if-neededavoid reinstalling when the lock file and installed skills are already current.