AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Ship

skill-amurthygithub-sharevalue-claude-skills-ship · by amurthygithub

Ship the current feature branch to <STAGING_BRANCH> — commit any uncommitted changes, push (triggers pre-push hook + agent review), open PR if missing, wait for consensus, merge to <STAGING_BRANCH> unless blockers or danger-zone hits.

No reviews yet
0 installs
19 views
0.0% view→install

Install

$ agentstack add skill-amurthygithub-sharevalue-claude-skills-ship

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-amurthygithub-sharevalue-claude-skills-ship)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ship? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

You are shipping the current feature branch to ``. Be autonomous on the happy path, halt and surface on anything risky.

Hard rules:

  • NEVER push to ` / ` directly. PRs only.
  • NEVER merge a PR into `. /promote` is the only path to prod and is user-invoked.
  • NEVER bypass the pre-push hook (--no-verify, CI_BYPASS=1, SKIP_AGENT_REVIEW=1).
  • NEVER force-push.

These four prohibitions are absolute. If a gate is genuinely broken, that is a separate ticket — it is never a /ship escape hatch.

Flags

NO_MERGE_FLAG=0
DRAFT_FLAG=0
echo "$ARGUMENTS" | grep -q -- '--no-merge' && NO_MERGE_FLAG=1
echo "$ARGUMENTS" | grep -q -- '--draft'    && DRAFT_FLAG=1

Step 1 — Validate

test -n "$_API_KEY" || { echo "❌ _API_KEY not set"; exit 1; }
command -v gh >/dev/null   || { echo "❌ gh CLI not found"; exit 1; }
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "❌ not in a git repo"; exit 1; }

BRANCH=$(git rev-parse --abbrev-ref HEAD)
case "$BRANCH" in
  ||HEAD)
    echo "❌ /ship from a protected branch is forbidden. Run /work-on first."
    exit 1
    ;;
esac
git fetch origin --quiet

Step 1.5 — Verify the branch lock (push interlock)

If your repo runs parallel agent sessions, a per-branch lock prevents two sessions from landing commits on the same PR. The push is the one place the lock is load-bearing — coding-time conflicts are advisory, but a push-time conflict lands your commits on someone else's branch. Refuse to ship if another live session owns this branch.

# CUSTOMIZE: point at your branch-lock helper, or delete this Step if you
# don't run parallel sessions. The helper should `verify ` with
# exit 0 = you own it (or it's free), non-zero = someone else owns it.
BRANCH_LOCK="$(git rev-parse --show-toplevel)/scripts/branch-lock.sh"
if [ -x "$BRANCH_LOCK" ] && ! "$BRANCH_LOCK" verify "$BRANCH"; then
  echo "🛑 Branch '$BRANCH' is locked by another live session."
  echo "   Refusing to push — commits would land on the wrong PR."
  echo "   Coordinate (have them /ship first) or force a takeover if it's dead."
  exit 1
fi

Step 2 — Determine the ticket

Priority order:

  1. .claude/active-ticket.md — top line # Active ticket: -NNN — .
  2. Branch name pattern /-... — derive ticket id (uppercased).
  3. Last commit's Refs: -NNN footer.

If none resolves a ticket, abort. Capture TICKET_ID, TICKET_TITLE, TICKET_URL.

Step 3 — Detect danger-zone touches BEFORE committing (advisory gate)

This is gate one of two. Source the canonical regex + scan helpers from a single shared script — do NOT inline the regex here. Keeping the danger-path definition in exactly one place is what lets CLAUDE.md §9.2, this Step, and the bypass-resistant Step 8 re-scan all agree.

# CUSTOMIZE: this helper is the single source of truth for the danger-path
# regex + the scan functions. Keep it in sync with CLAUDE.md §9.2.
. "$(git rev-parse --show-toplevel)/scripts/danger-zone-scan.sh"

# Scan the union of staged + unstaged + already-pushed
# (origin/..HEAD). The helper returns non-zero if
# origin/ is unreachable — fail CLOSED (halt), never
# fail open silently. A gate that can't run must not pretend it passed.
danger_zone_scan_full > /tmp/ship-danger.txt || {
  echo "🛑 Cannot verify danger-zone scan — origin/ unreachable."
  echo "   Run \`git fetch origin \` then retry /ship."
  exit 1
}

if [ -s /tmp/ship-danger.txt ]; then
  echo "🛑 Danger-zone files in this change set:"
  sed 's/^/  - /' /tmp/ship-danger.txt
  echo "These require manual review per CLAUDE.md §9. /ship will commit + push + run review,"
  echo "but will NOT auto-merge."
  DANGER_ZONE_HIT=1
fi

DANGER_ZONE_HIT is consulted at the merge gate. This Step is advisory — commit + push + review still proceed; only the merge is blocked.

Step 4 — Stage and commit any uncommitted changes

If working tree is clean, skip.

git add -A
# Belt-and-suspenders unstage of obvious secrets the user may have left in.
git reset HEAD -- '*.env' '.env*' '*.key' '*.pem' '*credentials*' 2>/dev/null || true

Build the commit message:

  • Subject: pick a Conventional Commits prefix (feat|fix|chore|refactor|docs|perf|test|ci|build) from `, else infer from TICKET_TITLE. Default: chore`.
  • Scope: pick from CLAUDE.md §3 allowed list (``). Omit if unsure.
  • Subject body: `` truncated to 72 chars.

Footer (always):

Refs: 

Co-Authored-By: 

If the commit-msg hook rejects it, fix and retry once. If still failing, surface the error and exit 1 — do NOT use --no-verify.

Step 5 — Push

git push -u origin "$BRANCH"

The pre-push hook will:

  1. Run ci-local.sh --quick. Non-zero → push aborted; surface the failing output.
  2. If a PR exists for this branch, fire /agentreview in the background through

a wrapper that scrubs the environment before exec — dropping every var not on a tight allowlist so the reviewer can't read your shell secrets (any *_SECRET / *_TOKEN / *_API_KEY / *_PASSWORD the shell exports). The reviewer keeps only what it needs: the gh auth token, its own model API key, and the `` key.

If push fails for any reason, surface the error and exit 1.

Step 6 — Open the PR if it doesn't exist + handle the first-push race

The pre-push hook only fires /agentreview when a PR already exists. On the first /ship for a new branch, the push happens before the PR — so we explicitly fire the review after opening the PR.

NEWLY_OPENED=0
PR=$(gh pr view "$BRANCH" --json number -q .number 2>/dev/null || echo "")

# Early-exit if the PR is already merged — a second /ship would push a no-op
# and poll forever for a review that never comes. Saves the full poll window.
if [ -n "$PR" ]; then
  PR_STATE=$(gh pr view "$PR" --json state -q .state 2>/dev/null)
  if [ "$PR_STATE" = "MERGED" ]; then
    echo "✅ PR #$PR is already MERGED. Nothing to ship."
    exit 0
  fi
fi

if [ -z "$PR" ]; then
  NEWLY_OPENED=1
  DRAFT_FLAG_ARG=""
  [ "$DRAFT_FLAG" = "1" ] && DRAFT_FLAG_ARG="--draft"
  gh pr create \
    --base  \
    --head "$BRANCH" \
    --title "" \
    $DRAFT_FLAG_ARG \
    --body "$(cat 

##  ticket
[$TICKET_ID]($TICKET_URL) — $TICKET_TITLE

## Test plan
- [ ] CI green
- [ ] (manual) feature working as described in ticket

🤖 Auto-opened by \`/ship\` — agent review will run automatically.
EOF
)"
  PR=$(gh pr view "$BRANCH" --json number -q .number)
fi

# Just-opened PRs missed the hook's review trigger. Spawn it explicitly,
# mirroring the hook's wiring: a per-PR lock + kill-existing-PID debounce so
# re-running /ship doesn't stack two reviews on the same SHA. Use the SHARED
# .git common-dir (not $REPO_ROOT/.git) so this works from a worktree, where
# $REPO_ROOT/.git is a file, not a directory.
if [ "$NEWLY_OPENED" = "1" ]; then
  GIT_COMMON_DIR=$(git rev-parse --git-common-dir)
  case "$GIT_COMMON_DIR" in /*) ;; *) GIT_COMMON_DIR="$(git rev-parse --show-toplevel)/$GIT_COMMON_DIR" ;; esac
  LOG="$GIT_COMMON_DIR/.agent-review-pr${PR}.log"
  LOCK="$GIT_COMMON_DIR/.agent-review-pr${PR}.pid"
  if [ -f "$LOCK" ]; then
    OLD_PID="$(cat "$LOCK" 2>/dev/null)"
    case "$OLD_PID" in ''|*[!0-9]*) OLD_PID="" ;; esac
    [ -n "$OLD_PID" ] && kill -0 "$OLD_PID" 2>/dev/null && kill "$OLD_PID" 2>/dev/null || true
  fi
  # CUSTOMIZE: point at your env-scrubbing review-spawn wrapper. `env -u
  # BASH_ENV -u ENV` closes the startup-injection window BEFORE bash starts
  # the wrapper; `"$LOG" 2>&1 ) &
  echo "$!" > "$LOCK"
  disown 2>/dev/null || true
fi

For subsequent /ship runs (PR already exists), the pre-push hook handles the review trigger. Same detached shape. Optionally attach a heartbeat watcher (Monitor against a watch script) so you get periodic status without re-typing check — the watcher must emit on every terminal state (verdict / stall / failure / timeout), not just the happy path.

Step 7 — Wait for agent-review consensus

Poll up to 5 minutes (timeout = halt for user). Match the latest comment that (a) starts with ## 🤖 Agent Consensus Review, (b) stamps the current HEAD short SHA, AND (c) is authored by a trusted author. All three conditions are load-bearing:

  • SHA anchor ties the verdict to the exact commit under review, so a stale

APPROVE from an earlier push can't gate a newer one. Match the orchestrator's stamp width exactly (it writes a 7-char SHA; an 8-char contains() anchor never substring-matches → silent timeout).

  • Trusted-author filter is the anti-spoof gate. The comment is posted from

the orchestrator's authenticated gh shell, so only that identity can produce one /ship will trust. Without this filter, ANY PR commenter could paste a fake APPROVED body and walk the merge gate open.

HEAD_SHORT=$(git rev-parse HEAD | cut -c1-7)   # MUST match the orchestrator's stamp width
DEADLINE=$(($(date +%s) + 300))
VERDICT=""; BLOCKERS=0; COMMENT_URL=""

# CUSTOMIZE: the gh login(s) the orchestrator posts under. Space-separated.
AGENTREVIEW_TRUSTED_AUTHORS=''

while [ $(date +%s) -lt $DEADLINE ]; do
  # pipefail scoped to the subshell so a transient gh 401/404 trips the `||`
  # (retry) instead of feeding empty stdout to jq → silent timeout.
  COMMENT=$(set -o pipefail; gh pr view "$PR" --json comments \
    | jq --arg authors "$AGENTREVIEW_TRUSTED_AUTHORS" --arg sha "$HEAD_SHORT" '
        .comments[-20:] | reverse
        | map(select(.author.login | IN($authors | split(" ") | .[])))
        | map(select(.body | startswith("## 🤖 Agent Consensus Review")))
        | map(select(.body | contains($sha)))
        | .[0]
      ') || { sleep 15; continue; }
  if [ "$COMMENT" != "null" ] && [ -n "$COMMENT" ]; then
    BODY=$(jq -r '.body' 1).
    BLOCKERS=$(grep -cE '^- \[SEVERITY: blocker\]'  0` (any verdict) | Halt regardless. Print blockers. |

**Iteration cap.** Persist a per-PR round counter (e.g. under the shared
`.git` common-dir). Each fresh push triggers a fresh review that surfaces
*new* nits indefinitely — past ~3 rounds the loop saturates (rounds 2 and 4
occasionally catch a real bug; the rest are nits). Cap at 3 and stop iterating.

## Step 8 — Surface merge gate (explicit user confirmation)

The merge is **always explicit**. /ship does NOT auto-merge even on a clean
APPROVED + 0 blockers — each merge is a per-action, present-tense decision.
Earlier iterations auto-merged; that path repeatedly degenerated into a
nit-iteration loop because the post-verdict prompt was treated as "address
findings first," which triggered a fresh review round, which surfaced fresh
nits, forever. The prompt now offers EXACTLY TWO outcomes: merge, or stop.

Conditions for the prompt to fire (anything else halts before it):

- Verdict in {`✅ APPROVED`, `✅ APPROVED WITH NOTES`}
- `BLOCKERS == 0`
- `DANGER_ZONE_HIT` unset by Step 3 AND the re-scan below comes back clean
- `--no-merge` flag NOT set
- All required PR checks green (`gh pr checks "$PR"`)

**Independent hard gate — this is gate two of two** (does NOT trust agent text
verdicts): immediately before the prompt, re-scan the merged-target diff
(`origin/..HEAD`) against the danger paths via the SAME shared
helper. This defends against a prompt-injected or compromised reviewer
producing a fake APPROVE on a privileged path (migrations, `.claude/skills/`,
CI workflows, etc.). It is fail-closed and bypass-resistant by design — agent
verdicts can never override §9.

```bash
# Re-source defensively; cross-Step bash state is fragile.
. "$(git rev-parse --show-toplevel)/scripts/danger-zone-scan.sh"
DANGER_HITS=$(danger_zone_scan) || {
  echo "🛑 Cannot verify pre-merge danger-zone scan — origin/ unreachable."
  echo "   Fail-closed by design: the gate that stops agent verdicts from"
  echo "   overriding §9 must not run silently. Fetch then retry /ship."
  exit 1
}
if [ -n "$DANGER_HITS" ]; then
  echo "🛑 Pre-merge danger-zone re-scan caught privileged paths even though the verdict was APPROVE."
  echo "   This is the bypass-resistant gate — agent verdicts cannot override §9."
  echo "$DANGER_HITS" | sed 's/^/  - /'
  echo "   Merge manually AFTER human review (option 1 of 3): gh pr merge $PR --squash --delete-branch"
  exit 0
fi

if [ "$VERDICT" = "⚠️ APPROVED-WITH-DISSENT" ]; then
  echo "🟡 Verdict is APPROVED-WITH-DISSENT — refusing to auto-merge. Read the dissent first."
  exit 0
fi

The merge prompt — exactly two outcomes

After the gates pass, surface (substituting $PR, $VERDICT, $COMMENT_URL):

✅ PR #$PR is ready to merge.
   Verdict:  $VERDICT
   Blockers: 0
   Danger:   none
   Review:   $COMMENT_URL

Any non-blocker findings (minor + nit) are NOT cause to delay. The
"nits-never-gate-merge" rule: first APPROVED + 0 blockers = ship.

Type 'merge' (or 'ship' / 'yes') to squash-merge to .
Anything else halts without merging.

DO NOT iterate from here to address findings — each fix triggers a fresh
review, surfacing new nits indefinitely. If you genuinely must fix something,
sweep ALL findings in ONE commit outside /ship, then re-invoke.

Wait for explicit confirmation. ONLY merge / ship / yes (case-insensitive) proceed. Anything else halts with Halted at merge gate by user. PR #$PR left open.

On confirmation, merge. Try the standard protected-branch path first — if the review bot posted a formal GitHub approval, branch protection's required- approvals gate is satisfied and the merge logs as a normal merge, not an admin bypass. Only fall back to --admin if the clean merge fails (branch protection unconfigured, flaky checks, or the bot approval wasn't posted).

if gh pr merge "$PR" --squash --delete-branch 2>/tmp/ship-merge-err; then
  echo "✓ Merged via standard protected-branch path (bot approval satisfied gate)"
else
  echo "🛈 Non-admin merge failed; falling back to --admin. Reason:"
  sed 's/^/    /' /tmp/ship-merge-err
  gh pr merge "$PR" --admin --squash --delete-branch
fi

# Release the branch lock — branch is squash-merged and gone upstream.
if [ -x "$BRANCH_LOCK" ]; then "$BRANCH_LOCK" release "$BRANCH" 2>/dev/null || true; fi

After merge, update the tracker: `In Review (the staging deploy is "in review" until /promote` moves it to Done).

Step 9 — Final report

✅ /ship complete

Ticket:    $TICKET_ID — $TICKET_TITLE
Branch:    $BRANCH (deleted) → 
PR:        #$PR
Verdict:   $VERDICT
Tracker:   In Progress → In Review

Next: visually verify on . When ready, /promote to ship to prod.

(For halts, show what's pending instead of complete.)

Step 10 — Write a runlog shard

Write a per-invocation audit shard, NOT an append to a shared log file — parallel ships from different branches would otherwise conflict on the same file. Each invocation gets its own uniquely-named file under a dated dir.

# CUSTOMIZE: your per-invocation audit-shard helper.
./scripts/runlog.sh append ship "$TICKET_ID" \
  " | pr=$PR | branch=$BRANCH | merged="

Failure modes that halt and surface (NOT errors)

  • Pre-push hook failed (CI quick-gates not green) — user fixes locally, re-runs.
  • Branch lock held by another session — coordinate or take over.
  • Agent review timed out — re-run after it lands, or fire /agentreview .
  • Verdict is ❌ NEEDS CHANGES / ⚠️ APPROVED-WITH-DISSENT / 🟦 SKIPPED — push a fix outside /ship, then re-invoke.
  • Blockers present — address and re-run.
  • Danger-zone files touched — merge manually after human review.
  • Iteration cap hit — accept the verdict and merge, or push an empty re-review commit.
  • --no-merge was passed.
  • User did not type merge / ship / yes at the prompt.

What /ship does NOT do

  • Does not author code.
  • Does not push to ``. Ever.
  • Does not run --no-verify or any hook bypass.
  • Does not promote to prod (use /promote).
  • Does not auto-merge — Step 8 always prompts for explicit confirmation.
  • Does not offer "address findings" at the merge prompt —

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.