mirror of
https://github.com/warpdotdev/common-skills.git
synced 2026-09-14 19:03:05 +08:00
4b3668c60a
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>
138 lines
11 KiB
Markdown
138 lines
11 KiB
Markdown
# 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:
|
|
```sh
|
|
scripts/install_common_skills --project
|
|
```
|
|
Install common skills globally:
|
|
```sh
|
|
scripts/install_common_skills --global
|
|
```
|
|
Install only when the lock hash has changed or a locked skill is missing:
|
|
```sh
|
|
scripts/install_common_skills --project --if-needed --non-interactive
|
|
```
|
|
Verify the installed skills without installing:
|
|
```sh
|
|
scripts/install_common_skills --verify-only
|
|
```
|
|
Remove installed common skills:
|
|
```sh
|
|
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:
|
|
```sh
|
|
#!/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`:
|
|
```sh
|
|
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:
|
|
```sh
|
|
./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:
|
|
```sh
|
|
./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:
|
|
```sh
|
|
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:
|
|
```sh
|
|
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:
|
|
```sh
|
|
/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.
|