shipit — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited shipit (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Follow skills/progress-tracking/SKILL.md: this procedure has more than two steps — seed one todo item per step below before starting and mark each complete as you go.shipit lands a pull request that has already passed review: it pushes any unpushed local commits, waits for CI to go green, and squash-merges so the PR title lands as the commit subject on the base branch (if a project puts a version in the title, that version then shows up in git log). It finalizes an existing open PR — it never opens one. It is generic: it does no versioning, changelog editing, or release work. If a project assigns a version at land time, that happens in a separate project-specific step before /shipit (in this repo, the dev version-bump skill — see docs/versioning.md); shipit only cares that the branch is ready to land.
gh pr merge is irreversible, so this skill is user-invocable only (disable-model-invocation: true): a human runs it deliberately; the model never auto-fires it.
shipit lands the open PR for the current branch. Discover it with gh pr view --json baseRefName,number,state,title and a base-branch fallback; never hardcode the base branch. The title is captured here because step 5 lands it as the squash commit subject. Run this in one bash call (an agent thread resets cwd between calls):
PR_JSON=$(gh pr view --json number,baseRefName,state,title 2>/dev/null)
BASE=$(printf '%s' "$PR_JSON" | jq -r .baseRefName 2>/dev/null)
[ -z "$BASE" ] || [ "$BASE" = "null" ] && BASE=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
[ -z "$BASE" ] && BASE=main
echo "PR: $PR_JSON"
echo "BASE: $BASE"gh pr view finds none): **refuse witha clear message** and stop. shipit finalizes an existing PR — it does not open one. Tell the user to open the PR first.
refuse up front with a clear message before doing any work — there is nothing to land.
<pr-number> argument overrides the discovered PR.The steps below are the scriptable core. The one interactive confirmation (the pre-merge confirm in step 4) wraps it: a non-interactive caller passes --yes to skip the prompt, leaving a pure push → wait → merge sequence.
Before relying on --squash, read the repo's merge strategy and report whether squash merges are enabled. This is a read-only check, not enforcement:
gh repo view --json mergeCommitAllowed,rebaseMergeAllowed,squashMergeAllowedStop and report only if squashMergeAllowed is false — squash-merge is how the PR title (and any version it carries) lands as the commit subject, and it keeps linear history (a squash commit is a normal commit, not a merge commit), so it is the only acceptable strategy here. If squash merging is available, proceed regardless of which other methods (mergeCommitAllowed, rebaseMergeAllowed) are enabled.
The branch may carry commits made after the PR was opened (review fixups, a project-specific land-time commit). Push them so CI runs against what will land:
git pushIf the local branch and remote have diverged because the branch was rebased locally, see the force-with-lease guidance in step 5 — never a bare --force.
Poll the PR's checks with gh pr checks. The bound is mechanical, not prose: timeout enforces the total cap and --fail-fast exits the instant a check fails. Bounded, never infinite. Defaults (overridable so a future automation loop can tune them):
--interval 30)timeout 1800)timeout 1800 gh pr checks <pr-number> --watch --fail-fast --interval 30
status=$?--fail-fast returns non-zero the moment any check fails; timeout kills the watch and returns 124 when the 30-min cap is hit. Map the exit code to one of three outcomes:
--required to gate on required checks only; the default here gates on all checks so a failing optional check still halts the land — the conservative choice for an irreversible merge.)
Run gh pr checks <pr-number> to print the failing check, and report it by name. Leave the branch in place — the user fixes CI and re-runs /shipit. Do not merge.
report "CI wait timed out"; do not merge.
Re-entry after a CI fix: when re-running /shipit after fixing CI, the commits are already on the branch — shipit simply pushes any new ones, waits again, and merges. It is safe to re-run.
gh pr merge is irreversible. For a human operator, ask for an explicit confirmation before merging — "about to merge PR #N into <base> — proceed?" — and only merge on a yes. A non-interactive caller passes --yes to skip this prompt; the prompt wraps the scriptable core, it does not live inside it.
PR behind its base. Before merging, check whether the base branch advanced since CI last ran. If the PR is behind `<base>`, bring it up to date:
<base>.git push --force-with-lease the rebased branch — the force is requiredbecause the rebase rewrote history; --force-with-lease refuses if the remote moved underneath you (never a bare `--force`).
Merge with `gh pr merge --squash` (named explicitly — squash lands the PR title as the commit subject while keeping linear history, so it is the only acceptable merge strategy here). Build the subject explicitly from the PR title captured during discovery and append (#<number>) so every landed commit shows both the title (with any version it carries) and the PR number — exactly the git log shape the operator sees. Passing --subject is deliberate: it guarantees the PR title regardless of the repo's "default squash commit message" setting (an explicit --subject is not auto-suffixed with the PR number, so we add it ourselves):
TITLE=$(printf '%s' "$PR_JSON" | jq -r .title)
gh pr merge <pr-number> --squash --subject "$TITLE (#<pr-number>)"The squash body defaults to the concatenated commit messages — leave it as-is unless the operator asks otherwise.
verbatim to the user; never force the merge.
Report the merge result (or the failing-check / timeout / branch-protection reason if it stopped short). If the project publishes a release on merge, that runs asynchronously after the merge — point the operator at gh run watch (or gh run list) so they can observe it rather than assuming it is already done.
shipit touches no tracker or board — it stays generic. If the PR links a ticket (e.g. Closes #<n>), the tracker closes that ticket when the merge lands, and any board automation moves it to its done state on its own; that is a property of the link the PR phase added, not an action shipit performs.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.