/ship-ci-review-loop
<ci-review-loop>
> /plugin marketplace add composio-community/awesome-claude-pluginsHow it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/ship-ci-review-loop
Context preview
What this command does when you run it.
<ci-review-loop>
Command definition
ship-ci-review-loop.md<ci-review-loop>
Phase 4: CI & Review Monitor Loop - Reference
This file contains detailed implementation for the CI & Review Monitor Loop phase of `/ship`.
**Parent document**: `ship.md`
---
<mandatory-requirements>
This Phase Is Mandatory
This is not optional. You must: 1. Wait the full 3 minutes for auto-reviewers 2. Run the monitor loop (not just check once) 3. Address all comments before merge </mandatory-requirements>
---
<pr-auto-review>
PR Auto-Review Process
PRs receive automatic reviews from configured auto-reviewers (Copilot, Gemini, CodeRabbit, etc.).
**Mandatory workflow:** 1. After PR creation, wait **at least 3 minutes** for first review round 2. Read **all comments** from all reviewers 3. Address **every comment** - no exceptions 4. Iterate until **zero unresolved threads** (typically 2-4 rounds)
**Rules:**
- Always address all comments, including "minor" or "nit" suggestions
- Do not skip a comment unless factually wrong or user-approved
- Treat all feedback as **required changes**, not suggestions
</pr-auto-review>
---
<overview>
Overview
The monitor loop must wait for: 1. CI to pass 2. All comments resolved (addressed or replied to) 3. No "changes requested" reviews remain
Why All Comments Matter
**Every comment must be addressed:**
- Critical/High issues: Fix immediately
- Medium issues: Fix (don't defer)
- Minor/Nit issues: Fix (shows attention to quality)
- Style suggestions: Fix (maintains codebase consistency)
- Questions: Answer with explanation
- False positives: Reply explaining why, then resolve
- Not relevant: Reply explaining why, then resolve
Do not ignore comments. Do not leave comments unresolved. A clean PR has zero unresolved conversations. </overview>
The Monitor Loop Algorithm
> **Note:** The JavaScript below is **conceptual pseudocode** showing the algorithm flow. > Implement using bash functions defined in this file.
const MAX_ITERATIONS = 10; // Safety limit
const INITIAL_WAIT_MS = 180000; // 3 minutes - wait for auto-reviews
const ITERATION_WAIT_MS = 30000; // 30 seconds between iterations
let iteration = 0;
while (iteration < MAX_ITERATIONS) {
iteration++;
console.log(`\n## CI & Review Monitor - Iteration ${iteration}`);
// Step 1: Wait for CI to complete
const ciStatus = await waitForCI();
if (ciStatus === 'failed') {
await fixCIFailures();
continue; // Push fix, re-run CI
}
// Step 1.5: First iteration only - wait for auto-reviews
if (iteration === 1) {
console.log("Waiting 3 minutes for auto-reviews...");
await sleep(INITIAL_WAIT_MS);
}
// Step 2: Check for PR comments and reviews
const feedback = await checkPRFeedback();
if (feedback.unresolvedCount === 0 && !feedback.changesRequested) {
console.log("[OK] CI passed, all comments resolved");
break; // Ready to merge!
}
// Step 3: Address ALL feedback
await addressAllFeedback(PR_NUMBER);
// Step 4: Push fixes
if (feedback.hasCodeChanges) {
await commitAndPush(`fix: address review feedback (iteration ${iteration})`);
}
// Step 5: Sleep before next check
await sleep(ITERATION_WAIT_MS);
}Step 1: Wait for CI
wait_for_ci() {
echo "Waiting for CI checks..."
while true; do
CHECKS=$(gh pr checks $PR_NUMBER --json name,state 2>/dev/null || echo "[]")
PENDING=$(echo "$CHECKS" | jq '[.[] | select(.state | IN("PENDING", "QUEUED", "IN_PROGRESS"))] | length')
FAILED=$(echo "$CHECKS" | jq '[.[] | select(.state | IN("FAILURE", "CANCELLED"))] | length')
PASSED=$(echo "$CHECKS" | jq '[.[] | select(.state=="SUCCESS")] | length')
if [ "$FAILED" -gt 0 ]; then
echo "[ERROR] CI failed ($FAILED checks)"
gh pr checks $PR_NUMBER
return 1
elif [ "$PENDING" -eq 0 ] && [ "$PASSED" -gt 0 ]; then
echo "[OK] CI passed ($PASSED checks)"
return 0
elif [ "$PENDING" -eq 0 ] && [ "$PASSED" -eq 0 ]; then
echo "[WARN] No CI checks found, proceeding..."
return 0
fi
echo " Waiting... ($PENDING pending, $PASSED passed)"
sleep 15
done
}Step 2: Check PR Feedback
check_pr_feedback() {
local pr_number=$1
echo "Checking PR feedback..."
# Extract owner and repo from git remote
REPO_INFO=$(gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"')
OWNER=$(echo "$REPO_INFO" | cut -d'/' -f1)
REPO=$(echo "$REPO_INFO" | cut -d'/' -f2)
# Get review state
REVIEWS=$(gh pr view $pr_number --json reviews --jq '.reviews')
CHANGES_REQUESTED=$(echo "$REVIEWS" | jq '[.[] | select(.state=="CHANGES_REQUESTED")] | length')
# Get unresolved review threads
# NOTE: Fetches first 100 threads. For PRs with >100 threads, implement pagination.
UNRESOLVED_THREADS=$(gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
}
}
}
}
}
' -f owner="$OWNER" -f repo="$REPO" -F pr=$pr_number \
--jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length')
echo " Unresolved threads: $UNRESOLVED_THREADS"
echo " Changes requested: $CHANGES_REQUESTED"
echo "{\"unresolvedThreads\": $UNRESOLVED_THREADS, \"changesRequested\": $CHANGES_REQUESTED}"
}Get Full Thread Details
get_unresolved_threads() {
local pr_number=$1
REPO_INFO=$(gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"')
OWNER=$(echo "$REPO_INFO" | cut -d'/' -f1)
REPO=$(echo "$REPO_INFO" | cut -d'/' -f2)
# NOTE: Fetches first 100 threads. For PRs with >100, implement pagination.
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {Read more
<ci-review-loop>
Phase 4: CI & Review Monitor Loop - Reference
This file contains detailed implementation for the CI & Review Monitor Loop phase of `/ship`.
**Parent document**: `ship.md`
---
<mandatory-requirements>
This Phase Is Mandatory
This is not optional. You must: 1. Wait the full 3 minutes for auto-reviewers 2. Run the monitor loop (not just check once) 3. Address all comments before merge </mandatory-requirements>
---
<pr-auto-review>
PR Auto-Review Process
PRs receive automatic reviews from configured auto-reviewers (Copilot, Gemini, CodeRabbit, etc.).
**Mandatory workflow:** 1. After PR creation, wait **at least 3 minutes** for first review round 2. Read **all comments** from all reviewers 3. Address **every comment** - no exceptions 4. Iterate until **zero unresolved threads** (typically 2-4 rounds)
**Rules:**
- Always address all comments, including "minor" or "nit" suggestions
- Do not skip a comment unless factually wrong or user-approved
- Treat all feedback as **required changes**, not suggestions
</pr-auto-review>
---
<overview>
Overview
The monitor loop must wait for: 1. CI to pass 2. All comments resolved (addressed or replied to) 3. No "changes requested" reviews remain
Why All Comments Matter
**Every comment must be addressed:**
- Critical/High issues: Fix immediately
- Medium issues: Fix (don't defer)
- Minor/Nit issues: Fix (shows attention to quality)
- Style suggestions: Fix (maintains codebase consistency)
- Questions: Answer with explanation
- False positives: Reply explaining why, then resolve
- Not relevant: Reply explaining why, then resolve
Do not ignore comments. Do not leave comments unresolved. A clean PR has zero unresolved conversations. </overview>
The Monitor Loop Algorithm
> **Note:** The JavaScript below is **conceptual pseudocode** showing the algorithm flow. > Implement using bash functions defined in this file.
const MAX_ITERATIONS = 10; // Safety limit
const INITIAL_WAIT_MS = 180000; // 3 minutes - wait for auto-reviews
const ITERATION_WAIT_MS = 30000; // 30 seconds between iterations
let iteration = 0;
while (iteration < MAX_ITERATIONS) {
iteration++;
console.log(`\n## CI & Review Monitor - Iteration ${iteration}`);
// Step 1: Wait for CI to complete
const ciStatus = await waitForCI();
if (ciStatus === 'failed') {
await fixCIFailures();
continue; // Push fix, re-run CI
}
// Step 1.5: First iteration only - wait for auto-reviews
if (iteration === 1) {
console.log("Waiting 3 minutes for auto-reviews...");
await sleep(INITIAL_WAIT_MS);
}
// Step 2: Check for PR comments and reviews
const feedback = await checkPRFeedback();
if (feedback.unresolvedCount === 0 && !feedback.changesRequested) {
console.log("[OK] CI passed, all comments resolved");
break; // Ready to merge!
}
// Step 3: Address ALL feedback
await addressAllFeedback(PR_NUMBER);
// Step 4: Push fixes
if (feedback.hasCodeChanges) {
await commitAndPush(`fix: address review feedback (iteration ${iteration})`);
}
// Step 5: Sleep before next check
await sleep(ITERATION_WAIT_MS);
}Step 1: Wait for CI
wait_for_ci() {
echo "Waiting for CI checks..."
while true; do
CHECKS=$(gh pr checks $PR_NUMBER --json name,state 2>/dev/null || echo "[]")
PENDING=$(echo "$CHECKS" | jq '[.[] | select(.state | IN("PENDING", "QUEUED", "IN_PROGRESS"))] | length')
FAILED=$(echo "$CHECKS" | jq '[.[] | select(.state | IN("FAILURE", "CANCELLED"))] | length')
PASSED=$(echo "$CHECKS" | jq '[.[] | select(.state=="SUCCESS")] | length')
if [ "$FAILED" -gt 0 ]; then
echo "[ERROR] CI failed ($FAILED checks)"
gh pr checks $PR_NUMBER
return 1
elif [ "$PENDING" -eq 0 ] && [ "$PASSED" -gt 0 ]; then
echo "[OK] CI passed ($PASSED checks)"
return 0
elif [ "$PENDING" -eq 0 ] && [ "$PASSED" -eq 0 ]; then
echo "[WARN] No CI checks found, proceeding..."
return 0
fi
echo " Waiting... ($PENDING pending, $PASSED passed)"
sleep 15
done
}Step 2: Check PR Feedback
check_pr_feedback() {
local pr_number=$1
echo "Checking PR feedback..."
# Extract owner and repo from git remote
REPO_INFO=$(gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"')
OWNER=$(echo "$REPO_INFO" | cut -d'/' -f1)
REPO=$(echo "$REPO_INFO" | cut -d'/' -f2)
# Get review state
REVIEWS=$(gh pr view $pr_number --json reviews --jq '.reviews')
CHANGES_REQUESTED=$(echo "$REVIEWS" | jq '[.[] | select(.state=="CHANGES_REQUESTED")] | length')
# Get unresolved review threads
# NOTE: Fetches first 100 threads. For PRs with >100 threads, implement pagination.
UNRESOLVED_THREADS=$(gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {
isResolved
}
}
}
}
}
' -f owner="$OWNER" -f repo="$REPO" -F pr=$pr_number \
--jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)] | length')
echo " Unresolved threads: $UNRESOLVED_THREADS"
echo " Changes requested: $CHANGES_REQUESTED"
echo "{\"unresolvedThreads\": $UNRESOLVED_THREADS, \"changesRequested\": $CHANGES_REQUESTED}"
}Get Full Thread Details
get_unresolved_threads() {
local pr_number=$1
REPO_INFO=$(gh repo view --json owner,name --jq '"\(.owner.login)/\(.name)"')
OWNER=$(echo "$REPO_INFO" | cut -d'/' -f1)
REPO=$(echo "$REPO_INFO" | cut -d'/' -f2)
# NOTE: Fetches first 100 threads. For PRs with >100, implement pagination.
gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes {A curated list of Plugins that let you extend Claude Code with custom commands, agents, hooks, and MCP servers through the plugin system.
Other commands on awesome-claude-plugins.
audit-project-agents
This file contains detailed agent coordination for `/audit-project`.
audit-project-github
This file contains GitHub integration for `/audit-project`.
bug-fix
Streamlines bug fixing by creating a GitHub issue first, then a feature branch for implementing and thoroughly testing the…

