agentsclimarketplace

Ship

Skill slamb2k/mad-skills/skills/ship

Ship changes through the full PR lifecycle. Use after completing feature work to commit, push, create PR, wait for checks, and merge. Handles the entire workflow: syncs with main, creates feature branch if needed, groups commits logically with semantic messages, creates detailed PR, monitors CI, fixes issues, squash merges, and cleans up. Invoke when work is ready to ship.From its SKILL.md

Install
npx -y skills add slamb2k/mad-skills --skill ship

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

24.0 KB, as published. Nobody here has run it

Ship - Full PR Lifecycle

When this skill is invoked, IMMEDIATELY output the banner below before doing anything else. Pick ONE tagline at random β€” vary your choice each time. CRITICAL: Reproduce the banner EXACTLY character-for-character, including the diagonal / glyph before the name.

{tagline}

       /$$ /$$$$$$  /$$   /$$ /$$$$$$ /$$$$$$$ 
      /$$//$$__  $$| $$  | $$|_  $$_/| $$__  $$
     /$$/| $$  \__/| $$  | $$  | $$  | $$  \ $$
    /$$/ |  $$$$$$ | $$$$$$$$  | $$  | $$$$$$$/
   /$$/   \____  $$| $$__  $$  | $$  | $$____/ 
  /$$/    /$$  \ $$| $$  | $$  | $$  | $$      
 /$$/    |  $$$$$$/| $$  | $$ /$$$$$$| $$      
|__/      \______/ |__/  |__/|______/|__/      

Taglines:

  • 🚚 Special delivery!!!
  • πŸ“¦ If it compiles, it ships!
  • 🚒 Anchors aweigh!
  • πŸ™ git push and pray!
  • ⚑ Shipping faster than Amazon Prime!
  • πŸ€ Yeet the code into production!
  • πŸ“¬ Another one for the merge queue!
  • 🟒 LGTM β€” Let's Get This Merged!

Output Formatting

After the banner, display parsed input:

β”Œβ”€ Input ────────────────────────────────────────
β”‚  {Field}:  {value}
β”‚  Flags:    {parsed flags or "none"}
└────────────────────────────────────────────────

Pre-flight results:

── Pre-flight ───────────────────────────────────
  βœ… {dep}           {version or "found"}
  ⚠️ {dep}           not found β†’ {fallback detail}
  ❌ {dep}           missing β†’ stopping
──────────────────────────────────────────────────

Stage/phase headers: ━━ {N} Β· {Name} ━━━━━━━━━━━━━━━━━━━━━━━━━

Status icons: βœ… done Β· ❌ failed Β· ⚠️ degraded Β· ⏳ working Β· ⏭️ skipped


Ship changes through the complete PR lifecycle. Deterministic stages (sync, CI polling, merge) run as bash scripts for speed and reliability. Only stages that require reasoning (commit/PR authoring, CI fix analysis) use LLM subagents. Stage prompts are in references/stage-prompts.md.

Flags

Parse optional flags from the request:

  • --pr-only: Stop after creating the PR
  • --no-squash: Use regular merge instead of squash
  • --keep-branch: Don't delete the source branch after merge
  • --no-superpowers: Force standalone merge even when Superpowers is installed
  • --auto: Full autopilot (pr-first-autonomous-build.md REQ-013, superseding autonomous-execution-mode.md REQ-029) β€” CI-watch and fix-loop run exactly as interactive /ship (2-attempt cap unchanged), and on green checks it proceeds to merge (Stage 5) and post-merge sync/teardown (Stage 5b) just like interactive /ship, rather than stopping at the PR. The PR description body remains the durable report; see references/autonomous-report.md.

Platform Detection

Detect the hosting platform before pre-flight so dependency checks are platform-specific:

REMOTE_URL=$(git remote get-url origin 2>/dev/null)
if echo "$REMOTE_URL" | grep -qiE 'dev\.azure\.com|visualstudio\.com'; then
  PLATFORM="azdo"
elif echo "$REMOTE_URL" | grep -qi 'github\.com'; then
  PLATFORM="github"
else
  PLATFORM="github"   # default fallback
fi

Pass {PLATFORM} into all stage prompts. Each stage uses the appropriate CLI tool: gh for GitHub, az repos/az pipelines for Azure DevOps.

Display the detected platform to the user immediately after detection:

βš™οΈ Platform: GitHub (github.com)

or:

βš™οΈ Platform: Azure DevOps ({AZDO_ORG}/{AZDO_PROJECT})

Pre-flight

Before starting, check all dependencies in this table. The table contains all dependencies β€” some are platform-conditional (see notes after table).

DependencyTypeCheckRequiredResolutionDetail
gitcligit --versionyesstopInstall from https://git-scm.com
ghcligh --versionyesurlhttps://cli.github.com
az devopscliaz devops -h 2>/dev/nullnofallbackFalls back to REST API with PAT; see AzDO tooling below
superpowerspluginon-disk glob via scripts/lib/superpowers.jsnofallbackReplaces final merge with superpowers:finishing-a-development-branch when present; see references/superpowers-deferral.md

Platform-conditional rules:

  • gh: Only required when PLATFORM == github. Skip for AzDO repos.
  • az devops: Only checked when PLATFORM == azdo. Skip for GitHub repos.

For each applicable row, in order:

  1. Skip rows that don't apply to the detected {PLATFORM}
  2. Run the Check command (for cli/npm) or test file existence (for agent/skill)
  3. If found: continue silently
  4. If missing: apply Resolution strategy
    • stop: notify user with Detail, halt execution
    • url: notify user with Detail (install link), halt execution
    • install: notify user, run the command in Detail, continue if successful
    • ask: notify user, offer to run command in Detail, continue either way (or halt if required)
    • fallback: notify user with Detail, continue with degraded behavior
  5. After all checks: summarize what's available and what's degraded
  6. Always show the commit/PR agent: βœ… commit agent general-purpose

Azure DevOps Setup (AzDO only)

When PLATFORM == azdo, run the AzDO tooling detection and configuration validation in references/azdo-setup.md β€” it sets AZDO_MODE (cli vs REST fallback), extracts AZDO_ORG/AZDO_PROJECT/AZDO_ORG_URL/AZDO_PROJECT_URL_SAFE from the remote URL, configures az devops defaults (or the REST base URL + auth header), and reports the azdo context. GitHub repos skip this section.

Pass {AZDO_MODE}, {AZDO_ORG}, {AZDO_PROJECT}, {AZDO_PROJECT_URL_SAFE}, {AZDO_ORG_URL} into all stage prompts alongside {PLATFORM}.

Read default_branch and remote from Stage 1's SYNC_REPORT. These are substituted into all stage prompts as {REMOTE} and {DEFAULT_BRANCH}.


Pre-Ship Location Check

Before syncing, run the shared root-mismatch check from references/location-check.md ({caller} = "before syncing").


Stage 1: Sync

Run the sync script directly (no LLM needed):

SKILL_ROOT="<resolved plugin root>"
bash "$SKILL_ROOT/skills/sync/scripts/sync.sh" "{REMOTE}" "{DEFAULT_BRANCH}"

Parse SYNC_REPORT from output markers. Extract remote and default_branch. Abort if exit code is 1 (fatal).

If worktree_mode=true and worktree_removed is an actual path (not none or a skipped … reason), apply the same worktree-mode session-return handling as Stage 5b before any further git command β€” this means the branch was already merged; report that to the user and stop rather than proceeding to commit on a now-gone branch.


Stage 1b: Follow-ups Ledger (REQ-010/044)

Capture any follow-up ideas, deferred fixes, or open questions surfaced during this work into the committed ledger so they survive /clear. Capture is automatic and deduped β€” don't ask permission to capture, only decide what to do with the items.

This runs before Stage 2, and that placement is the point. The ledger is a committed file, so a ledger write needs a commit to carry it. Every stage from here on has one; the post-merge report does not. Capturing here means the entries ride the same commit series as the work, land in the same PR, get reviewed alongside it, and never leave the default branch dirty. Capturing after the merge β€” where this used to live β€” writes to a freshly-clean default branch with nothing left to pick it up.

Because no PR exists yet, source is the branch name (/ship <branch>), not /ship #<pr>. That is provenance, not data: the branch is equally traceable from the merge commit, and it is knowable at the only moment the write is free.

--auto runs this identically. On a headless run, skip the breach-time AskUserQuestion prompt below and go straight to the real capture (the silent safety net, matching /build Stage 10's headless path); the interactive per-candidate prompt only runs when a live session is watching. Capture itself always runs, in both modes.

Preview before capturing (REQ-006/008). Call the non-mutating preview with the same items about to be captured, so a would-be cap breach surfaces before anything relocates:

_R="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/slamb2k}"
node "$_R/hooks/session-guard.cjs" logbook-capture-preview \
  '[{"title":"…","category":"ideas","source":"/ship <branch>"}, …]'

Parse would_relocate from the LOGBOOK_CAPTURE_PREVIEW_BEGIN…END block.

  • If would_relocate: none β†’ skip straight to the real capture below.
  • If would_relocate lists candidates β†’ present each one individually via AskUserQuestion (mirroring Stage 10's "Let me choose per item" pattern) before the real capture runs. Address each candidate by its title text, not the preview list's ordinal β€” the preview numbers candidates in victim-selection order (lowest priority, then oldest date), which does not match logbook-resolve/logbook-dismiss's plain-number selector (hot-file display order); the title is matched by substring regardless of ordering, so it's the only selector guaranteed to hit the right item. Options per candidate:
    • "Resolve now" β†’ node "$_R/hooks/session-guard.cjs" logbook-resolve "<title>"
    • "Dismiss" β†’ node "$_R/hooks/session-guard.cjs" logbook-dismiss "<title>"
    • "Leave it" β†’ no action; it relocates to LOGBOOK-ARCHIVE.md when the real capture runs

Where the items come from. The same sources the "What's Next?" section reads after the merge β€” TaskList, the session so far, and Claude Code's auto-memory β€” plus anything this session deferred, skipped, or flagged as a known gap. All of that is knowable now; only the PR number is not. Anything "What's Next?" would have listed but that isn't being acted on belongs here.

Check for entries already in the tree first. /build Stage 10 captures to the same ledger at the end of a build, so LOGBOOK.md is often already dirty when /ship starts. Run git diff --stat LOGBOOK.md before capturing: an item /build already recorded is present with different wording, which dedup will not catch, and capturing it again mints a near-duplicate. Capture only what is genuinely new to this session.

Then run the real capture:

node "$_R/hooks/session-guard.cjs" logbook-capture \
  '[{"title":"…","category":"ideas","source":"/ship <branch>"}, …]'

If a captured item is also turned into a TaskCreate task, re-capture it with a "link":"task#<id>" so it auto-resolves when the task completes (REQ-012). logbook-capture's output reports relocated:[…] titles β€” those items moved to the archive file (LOGBOOK-ARCHIVE.md), never dismissed or resolved; they're still open and still addressable via /logbook archive. Report them to the user (never silent, GUD-002). Do NOT show the full ledger here β€” that comes with the final report, after the merge.

If no follow-ups were surfaced and the ledger is empty, do nothing (AC-008) and proceed to Stage 2.


Stage 2: Commit, Push & Create PR

This stage needs to read and understand code to write good commit messages and PR descriptions β€” it's one of the few stages that requires an LLM.

Launch general-purpose subagent (reads diffs + source files):

Task(
  subagent_type: "general-purpose",
  description: "Analyze, commit, push, and create PR",
  prompt: <read from references/stage-prompts.md#stage-2>
)

Substitute {USER_INTENT}, {FILES_TO_INCLUDE}, {FILES_TO_EXCLUDE}, {REMOTE}, {DEFAULT_BRANCH}, {PLATFORM}, {AZDO_MODE}, {AZDO_ORG}, {AZDO_PROJECT}, {AZDO_PROJECT_URL_SAFE}, {AZDO_ORG_URL}, {PAT}, {SKILL_ROOT} into the prompt.

Parse SHIP_REPORT. Abort if failed.

Rollback: PR creation runs through scripts/create-pr.sh, which is idempotent β€” it reuses an already-open PR on the branch (reused=true) instead of erroring, so that is never a failure. If push succeeds but create-pr.sh reports status=failed, report the error from its errors field and suggest the manual PR creation command. Do NOT revert the push.

  • GitHub: gh pr create --head {branch}
  • Azure DevOps (cli): az repos pr create --source-branch {branch} --target-branch {DEFAULT_BRANCH} --org {AZDO_ORG_URL} --project {AZDO_PROJECT}
  • Azure DevOps (rest): Create PR via {AZDO_ORG_URL}/{AZDO_PROJECT}/_apis/git/repositories/{repo}/pullrequests?api-version=7.0

If --pr-only flag: Stop here and report PR URL to user.


Stage 3 + 4: CI Watch & Fix Loop

Run CI monitoring in the foreground so failures are caught and fixed immediately. This stage loops: watch β†’ detect failure β†’ fix β†’ push β†’ watch again.

Maximum 2 fix attempts. If CI still fails after 2 rounds, report to user and stop.

--auto mode: CI-watch/fix-loop behavior is unchanged from interactive /ship β€” the 2-attempt cap already matches --auto's bound (REQ-031). No dispatch needed here.

Watch

Run the CI watch script directly (no LLM needed β€” just polling):

SKILL_ROOT="<resolved plugin root>"
bash "$SKILL_ROOT/skills/ship/scripts/ci-watch.sh" \
  "{PLATFORM}" "{PR_NUMBER}" "{BRANCH}" \
  --azdo-mode="{AZDO_MODE}" --azdo-org-url="{AZDO_ORG_URL}" \
  --azdo-project="{AZDO_PROJECT}" --azdo-project-url-safe="{AZDO_PROJECT_URL_SAFE}"

Briefly inform the user: ⏳ Watching CI for PR #{pr_number}...

Parse CHECKS_REPORT from output markers. Exit code 0=passed, 1=failed, 2=timeout.

Fix (if needed)

If CHECKS_REPORT shows some_failed, immediately launch a fix subagent β€” do not wait, do not ask the user:

Task(
  subagent_type: "general-purpose",
  description: "Fix CI failures",
  prompt: <read from references/stage-prompts.md#stage-4>
)

Substitute {PR_NUMBER}, {BRANCH}, {FAILING_CHECKS}, {PLATFORM}, {AZDO_MODE}, {AZDO_ORG}, {AZDO_ORG_URL}, {AZDO_PROJECT}, {PAT} into the prompt.

The fix subagent MUST commit and push before returning. Once it returns, immediately loop back to Watch to re-check CI.

Loop summary

attempt = 0
while attempt < 2:
  CHECKS = run_watch()
  if CHECKS.status == "all_passed":
    break  β†’ proceed immediately to Stage 5 (do NOT ask user to confirm merge)
  if CHECKS.status == "no_checks":
    β†’ prompt user via AskUserQuestion:
      "No CI checks found for PR #{PR_NUMBER} after waiting 30 seconds.
       The repository may not have CI configured, or checks may still be registering."
      Options:
      - "Merge without checks (Recommended)" β†’ break to Stage 5
      - "Wait another 30 seconds" β†’ re-run the watch (do not increment attempt)
      - "Cancel" β†’ stop /ship and display failure banner
    break (if user chose merge or after re-wait resolves)
  attempt += 1
  run_fix(CHECKS.failing_checks)
  β†’ loop back to watch

if attempt == 2 and still failing:
  β†’ display failure banner and stop (see Failure Handling below)

Failure Handling

When /ship fails at ANY point (CI exhausts fix attempts, merge fails, post-merge verification fails), display the failure banner and STOP:

β”Œβ”€ Ship Β· FAILED ──────────────────────────────────
β”‚
β”‚  ❌ PR #{PR_NUMBER} was NOT merged
β”‚
β”‚  Reason: {specific failure reason}
β”‚  Branch: {BRANCH} (still active)
β”‚
β”‚  ⚠️  You are still on branch '{BRANCH}'.
β”‚     Run /sync to return to main before starting new work.
β”‚
└───────────────────────────────────────────────────

Critical rules on failure:

  • Do NOT proceed to "What's Next?"
  • Do NOT suggest next tasks or follow-up work
  • Do NOT invoke /sync or any other skill
  • Do NOT use language like "will be auto-merged" or "PR is pending"
  • The failure banner is the LAST output β€” nothing follows it

Stage 5: Merge & Final Sync

--auto proceeds through this stage (pr-first-autonomous-build.md REQ-013, superseding REQ-029's stop-at-PR): on green checks it merges and runs Stage 5b teardown exactly as interactive /ship does β€” it does not stop at the open PR. The PR description body remains the durable report (references/autonomous-report.md), now a record of what was merged. The completion-mode opt-in that authorizes an unattended merge is set earlier, on the spec (completion_mode: auto-ship), not by this flag.

Once checks pass, immediately proceed to merge β€” do not ask the user for confirmation. The user invoked /ship expecting the full lifecycle; stopping to ask defeats the purpose. Squash merge and delete the source branch are the defaults (override via --no-squash and --keep-branch flags only).

5a. Merge the PR

Superpowers deferral: When Superpowers is detected and --no-superpowers is not set, after CI is green and auto-fix, announce ⚑ Superpowers detected β€” deferring final integration to superpowers:finishing-a-development-branch and invoke that skill to present merge/PR/cleanup options instead of calling merge.sh directly (CI-poll + auto-fix and the no-manual-CI-trigger rule still apply); otherwise run merge.sh below.

Run the merge script directly (no LLM needed):

SKILL_ROOT="<resolved plugin root>"
bash "$SKILL_ROOT/skills/ship/scripts/merge.sh" \
  "{PLATFORM}" "{PR_NUMBER}" \
  {MERGE_FLAGS} {BRANCH_FLAGS} \
  --azdo-mode="{AZDO_MODE}" --azdo-org-url="{AZDO_ORG_URL}" \
  --azdo-project="{AZDO_PROJECT}" --azdo-project-url-safe="{AZDO_PROJECT_URL_SAFE}"

Parse LAND_REPORT from output markers. Exit code 0=merged, 1=failed.

5a-verify. Verify merge completed

After merge.sh reports success, verify the PR actually merged:

GitHub:

PR_STATE=$(gh pr view "$PR_NUMBER" --json state --jq '.state')

Expected: "MERGED"

AzDO CLI:

PR_STATE=$(az repos pr show --id "$PR_NUMBER" --query status -o tsv)

Expected: "completed"

AzDO REST:

PR_RESP=$(curl -s -H "$AUTH" "${AZDO_ORG_URL}/${AZDO_PROJECT_URL_SAFE}/_apis/git/repositories/${REPO_ID}/pullRequests/${PR_NUMBER}?api-version=7.1")
PR_STATE=$(echo "$PR_RESP" | jq -r '.status')

Expected: "completed"

If the PR is NOT in the expected merged/completed state, treat as a failure β€” display the failure banner (see Failure Handling) with reason "PR merge was accepted but PR is still in '{PR_STATE}' state. The merge may be queued or deferred." and stop.

5b. Sync local repo

After the merge script succeeds, run the sync script to checkout the default branch, pull the merge commit, and clean up stale branches:

bash "$SKILL_ROOT/skills/sync/scripts/sync.sh" "{REMOTE}" "{DEFAULT_BRANCH}"

Parse the SYNC_REPORT block from the output for worktree_mode and worktree_removed.

Worktree mode (worktree_mode=true). Skip the verification block below entirely in ALL worktree_mode=true cases β€” regardless of whether worktree_removed is an actual path, none, or a skipped … reason (dirty, unfinished, etc.) β€” sync.sh already synced main in the primary (main_sync) and, by design, never checks out the default branch in a worktree.

Additionally, if worktree_removed is an actual path (not none or a skipped … reason), the session's cwd no longer exists β€” return to primary_path (from the same report) BEFORE any further git command: use the native ExitWorktree tool if the session entered this worktree via EnterWorktree and the tool is available, otherwise run the next Bash command with cd "<primary_path>" first.

Non-worktree mode (worktree_mode=false). Verify the working tree is on the default branch:

CURRENT=$(git branch --show-current)
if [ "$CURRENT" != "{DEFAULT_BRANCH}" ]; then
  git checkout {DEFAULT_BRANCH}
  git pull {REMOTE} {DEFAULT_BRANCH}
fi

What's Next?

Only run this section if /ship succeeded (PR is merged). If any failure occurred, the failure banner was already displayed and nothing should follow it.

After a successful merge, determine what work comes next by checking these sources (in priority order):

  1. Active tasks β€” check TaskList for any in-progress or pending tasks in the current session
  2. Session context β€” review the conversation so far for any stated plans, follow-up items, or deferred work the user mentioned
  3. Memory β€” check Claude Code's built-in auto-memory (~/.claude/projects/<project>/memory/MEMORY.md) for recent checkpoints or plans related to this project

Summarize the result as 1–3 short bullet points for the ⚑ Next section of the report. If nothing is found, omit the section entirely β€” do not fabricate next steps.


Final Report to User

Compile all stage reports into a summary:

β”Œβ”€ Ship Β· Report ────────────────────────────────
β”‚
β”‚  βœ… Ship complete
β”‚
β”‚  🌿 Branch:  {branch}
β”‚  πŸ”— PR:      {pr_url}
β”‚  πŸ”€ Merged:  {merge_commit} ({merge_type})
β”‚  🌿 Now on: {DEFAULT_BRANCH} (up to date)
β”‚
β”‚  πŸ“ Commits
β”‚     β€’ {commit message 1}
β”‚     β€’ {commit message 2}
β”‚
β”‚  πŸ“Š {count} files changed ({diff_summary})
β”‚
β”‚  ⚑ Next
β”‚     β€’ {next item 1}
β”‚     β€’ {next item 2}
β”‚
└─────────────────────────────────────────────────

If nothing was found for "What's Next?", omit the ⚑ Next section.

If any stage failed, add:

β”‚  ❌ Failed at: {stage name}
β”‚     {error description}
β”‚     {suggested resolution}

After presenting the report (PR merged, tree clean, not an active cycle), resurface any deferred lifecycle step:

_R="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/slamb2k}"
node "$_R/hooks/session-guard.cjs" lifecycle-checkpoint

If the command prints a LIFECYCLE_OFFER_BEGIN…END block, present that offer to the user via AskUserQuestion as instructed inside the block. If it prints nothing (or LIFECYCLE_OFFER_NONE), do not mention the lifecycle engine.

Follow-ups Ledger β€” post-merge net (REQ-010/044)

The bulk of capture already happened at Stage 1b, before the commit, so those entries are in the merge commit. This is only the net for follow-ups that could not have been known then β€” ones the shipping itself produced: a CI failure and its fix, a merge surprise, something learned from the deploy.

Show the ledger either way, so the report ends with what is outstanding:

_R="${CLAUDE_PLUGIN_ROOT:-$HOME/.claude/plugins/marketplaces/slamb2k}"
node "$_R/hooks/session-guard.cjs" logbook-list

If nothing new arose after Stage 1b β€” the common case β€” stop here. If the ledger is empty and nothing was surfaced, show nothing at all (AC-008).

If shipping genuinely produced new follow-ups, capture them with source: "/ship #<pr>" (the PR number is known now), using the same preview-then-capture flow as Stage 1b. Then tell the user plainly:

The ledger now has uncommitted changes on {DEFAULT_BRANCH} β€” captured after the merge, so no commit was left to carry them.

Offer, do not assume: commit in place on the default branch, or leave them for the next ship's Stage 1b to fold into that branch. Both are reasonable; a docs-only PR for one ledger line usually is not. Never leave the default branch dirty without saying so β€” a silently modified ledger looks like a clean tree to the next session, and /sync will stash it.

Keep looking

Skills are one crate of 627,801. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.