CtrlK
BlogDocsLog inGet started
Tessl Logo

dryrun-pr-review

Use when the user asks to create a pull request or merge request, submit changes for review, push for review, or open a pull request.

61

Quality

72%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Low

Low-risk findings worth noting

Fix and improve this skill with Tessl

tessl review fix ./plugins/dryrun-pr-review/skills/dryrun-pr-review/SKILL.md
SKILL.md
Quality
Evals
Security

DryRunSecurity PR Review Workflow

Full PR lifecycle: detect platform, branch, commit, open a PR, poll for DryRunSecurity comments, present findings to the user.

Platform Detection & Repo Info

Run this and read the output to determine platform and repo coordinates:

git remote get-url origin

From the URL:

  • Contains github.com → GitHub; use gh CLI. Extract OWNER and REPO from the URL path.
  • Otherwise → GitLab; use glab CLI. Extract the project path and URL-encode it (replace / with %2F) for API calls.

All subsequent steps reference PLATFORM, OWNER, REPO (GitHub) or PROJECT (GitLab), and PR_NUMBER.

Workflow

1. Branch

If on main/master, create a new branch following this repo's naming conventions. Otherwise use the existing feature branch.

2. Stage & Commit

git status
git add <files>   # selective — never commit secrets or generated files
git commit -m "<message following this repo's commit style>

Co-Authored-By: DryRun Security <noreply@dryrun.security>"

3. Push & Open PR

git push -u origin <branch-name>

Check whether a pull request already exists for this branch before creating one:

# GitHub — reuse existing PR if present
EXISTING=$(gh pr view --json number --jq '.number' 2>/dev/null)
if [ -n "$EXISTING" ]; then
    echo "Using existing PR #$EXISTING"
    PR_NUMBER=$EXISTING
else
    gh pr create --title "<title>" --body "<body>"
    # capture PR_NUMBER from the URL in the output (last path segment)
fi

# GitLab — reuse existing MR if present
BRANCH=$(git rev-parse --abbrev-ref HEAD)
EXISTING=$(glab mr list --source-branch "$BRANCH" 2>/dev/null | awk 'NR==2{print $1}' | tr -d '!')
if [ -n "$EXISTING" ]; then
    echo "Using existing PR !$EXISTING"
    PR_NUMBER=$EXISTING
else
    glab mr create --title "<title>" --description "<body>"
    # capture PR_NUMBER from the output
fi

Store the result as PR_NUMBER.

4. Poll for DryRunSecurity Review Comments

Poll for up to 10 minutes (every 30 seconds). Use timestamp-based polling — not count-based. Comments can be edited/replaced.

Exit as soon as DRS activity is detected — a DryRunSecurity comment means the review is complete.

START_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
START_EPOCH=$(date +%s)
TIMEOUT=600
POLL_INTERVAL=30

while true; do
    ELAPSED=$(( $(date +%s) - START_EPOCH ))
    if [ $ELAPSED -ge $TIMEOUT ]; then
        echo "Timed out after ${ELAPSED}s with no DryRunSecurity activity."
        break
    fi

    if [ "$PLATFORM" = "github" ]; then
        DRS_NEW=$(gh api repos/${OWNER}/${REPO}/issues/${PR_NUMBER}/comments \
          --jq "[.[] | select((.user.login == \"dryrunsecurity\" or .user.login == \"dryrunsecurity[bot]\") and .created_at > \"${START_TIME}\")] | length")

        DRS_REVIEWS=$(gh api repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/reviews \
          --jq "[.[] | select((.user.login | test(\"dryrunsecurity\"; \"i\")) and .submitted_at > \"${START_TIME}\")] | length")

        TOTAL=$(( DRS_NEW + DRS_REVIEWS ))
        echo "Waiting for DryRunSecurity review... (${ELAPSED}s elapsed)"
        [ "$TOTAL" -gt 0 ] && echo "DryRunSecurity review received: ${DRS_NEW} comment(s), ${DRS_REVIEWS} review(s)." && break
    else
        DRS_NEW=$(glab api projects/${PROJECT}/merge_requests/${PR_NUMBER}/notes \
          | jq "[.[] | select(.author.username == \"dryrunsecurity\" and .created_at > \"${START_TIME}\")] | length")

        echo "Waiting for DryRunSecurity review... (${ELAPSED}s elapsed)"
        [ "$DRS_NEW" -gt 0 ] && echo "DryRunSecurity review received: ${DRS_NEW} note(s)." && break
    fi

    sleep $POLL_INTERVAL
done

If the loop timed out with no DRS activity, inform the user: the DryRunSecurity review period is complete.

5. Present DryRunSecurity Comments to User

Fetch all DryRunSecurity comments and present them to the user — do not fix automatically.

# GitHub
gh api repos/${OWNER}/${REPO}/issues/${PR_NUMBER}/comments \
  --jq '.[] | select(.user.login == "dryrunsecurity" or .user.login == "dryrunsecurity[bot]") | {id: .id, body: .body}'

gh api repos/${OWNER}/${REPO}/pulls/${PR_NUMBER}/reviews \
  --jq '.[] | select(.user.login | test("dryrunsecurity"; "i")) | {id: .id, body: .body, state: .state}'

# GitLab
glab api projects/${PROJECT}/merge_requests/${PR_NUMBER}/notes \
  | jq '.[] | select(.author.username == "dryrunsecurity") | {id: .id, body: .body}'

For each comment, present:

  • File:line (if applicable)
  • Summary of the security finding
  • Your suggested fix or reason it's a false positive

Then ask the user which comments to address and how.

6. Act on User Decisions

For comments the user wants fixed:

# Make code changes, then:
git add <files>
git commit -m "<message following this repo's commit style — addressing DryRunSecurity finding>

Co-Authored-By: Claude <noreply@anthropic.com>"

For comments the user wants to decline, post a new comment on the PR thread explaining why:

# GitHub — DryRunSecurity posts on the PR thread (not inline), so reply via issue comments
gh api repos/${OWNER}/${REPO}/issues/${PR_NUMBER}/comments \
  -f body="Not addressing DryRunSecurity finding: <explanation>"

# GitLab
glab api projects/${PROJECT}/merge_requests/${PR_NUMBER}/notes \
  --method POST -f body="Not addressing DryRunSecurity finding: <explanation>"

7. Push & Re-poll

git push

Return to Step 4 using the current time as the new START_TIME. Continue until polling times out with no new comments.

Important Notes

  • Never force push unless explicitly requested
  • Always read files before suggesting changes
  • Use gh for GitHub repos, glab for GitLab repos — detect via git remote get-url origin
  • Timestamp-based polling only — never count-based
  • Minimize Bash invocations — consolidate related commands into single scripts (e.g., combine git add + git commit + git push into one call). Each separate Bash call is a potential permission prompt for the user.
  • The polling loop in Step 4 must remain a single Bash invocation — do not break it into multiple calls
Repository
DryRunSecurity/external-plugin-marketplace
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.