# Pick Up Issue

> Pick up an unassigned issue from a repo, implement it, and shepherd the PR to merge Use when this capability is needed.

- Skill: `tomevault-io/pick-up-issue-2` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add tomevault-io/pick-up-issue-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tomevault-io/pick-up-issue-2/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: tomevault-io (https://skillmd.com/u/tomevault-io)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tomevault-io/pick-up-issue-2

---


# Pick Up Issue

Find an unassigned, open issue in a repository, claim it, implement the fix or feature, open a PR,
shepherd it to merge, and confirm the issue is closed. This skill orchestrates the full lifecycle
from issue selection through cleanup.

**Multi-agent safety:** Multiple agents may run this skill in parallel across different worktrees.
Each agent works on its own issue and branch. The cleanup step at the end is session-scoped and will
not interfere with branches or worktrees used by other agents.

## Input

`$ARGUMENTS` should contain:

- **Required:** A repo reference in `owner/repo` format (e.g., `my-org/my-app`).
- **Optional:** An issue filter — a label name, milestone, or search term to narrow which issues to
  consider. If omitted, all open issues are candidates.

Examples:

- `/pick-up-issue my-org/my-app` — pick any open, unassigned issue
- `/pick-up-issue my-org/my-app bug` — prefer issues labeled `bug`
- `/pick-up-issue my-org/my-app "good first issue"` — prefer issues labeled `good first issue`

If `$ARGUMENTS` is empty, attempt to derive `owner/repo` from the current git remote. If not in a
git repo, ask the user which repo to target.

## Prerequisites

- You must be inside the target repository (or it will be derived from git context).
- The `gh` CLI must be authenticated.
- The `/shepherd-to-merge` and `/cleanup` skills must be available.

## Detecting Context

1. **Owner and repo:** Parse from the first positional argument in `$ARGUMENTS`, or derive from
   `git remote get-url origin`.
2. **Issue filter:** Everything after the `owner/repo` argument (may be empty).

## Steps

### 0. Worktree safety checks

If the current working directory is inside a git worktree (path contains
`.claude/worktrees/<name>/` or `.codex/worktrees/<name>/`),
perform these checks before proceeding:

1. **Detect worktree name:** Extract `<name>` from the path.

2. **Check for active sessions:** Count JSONL transcript files modified in the last 5 minutes for
   this worktree. The current session will have its own transcript, so only flag a conflict when
   **2 or more** recent transcripts exist (indicating another session is also active):

   ```sh
   runtime=$(echo "$PWD" | sed -n 's|.*/\.\(claude\|codex\)/worktrees/.*|\1|p')
   worktree_name=$(echo "$PWD" | sed -n 's|.*/\.\(claude\|codex\)/worktrees/\([^/]*\)\(/.*\)\{0,1\}$|\2|p')
   if [ -n "$worktree_name" ]; then
     for projects_root in "$HOME/.claude/projects" "$HOME/.codex/projects"; do
       [ -d "$projects_root" ] || continue
       project_dir=$(find "$projects_root" -maxdepth 1 -type d -name "*worktrees-${worktree_name}" | head -1)
       [ -n "$project_dir" ] && break
     done
     if [ -n "$project_dir" ]; then
       active_count=$(find "$project_dir" -name '*.jsonl' -mmin -5 2>/dev/null | wc -l | tr -d ' ')
     fi
   fi
   ```

   If `active_count` is 2 or more, **STOP with error**: "Worktree `<name>` has an active session
   (multiple transcripts modified within 5 minutes). Refusing to dispatch — wait for the session
   to finish or use a different worktree."

3. **Record current branch:** The cleanup skill will independently return to
   `<runtime>/<name>` (`claude/<name>` or `codex/<name>`) in a worktree context. Verify the
   current branch matches this convention:

   ```sh
   current_branch=$(git branch --show-current)
   expected_branch="${runtime}/${worktree_name}"
   ```

   If `current_branch` does not match `expected_branch`, warn but proceed.

4. **Ensure worktree branch is current with main:** Before creating a feature branch, rebase the
   worktree's designated branch onto the latest default branch to prevent stale base commits:

   ```sh
   git fetch origin
   default=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
   [ -z "$default" ] && default="main"
   behind=$(git rev-list --count HEAD..origin/$default 2>/dev/null || echo 0)
   if [ "$behind" -gt 0 ]; then
     git rebase "origin/$default"
   fi
   ```

   If the rebase fails with conflicts, abort and reset the parking branch:

   ```sh
   git rebase --abort
   git reset --hard "origin/$default"
   ```

### 1. Find a candidate issue

Search for open issues that are not assigned to anyone:

```sh
gh issue list -R <owner>/<repo> --state open --assignee "" --limit 30 --json number,title,labels,milestone,body,assignees
```

If an issue filter was provided, narrow the results:

- If the filter matches a label name, add `--label "<filter>"` to the query.
- If doing a text search instead, replace `--assignee ""` with `--search "<filter> no:assignee"`
  since `--search` and `--assignee` cannot be combined.

From the results, exclude issues that:

- Are already assigned (verify the `assignees` array is empty) — another agent may have just
  claimed an issue, so always re-check assignment before claiming.
- Have a `wontfix`, `duplicate`, or `invalid` label.
- Have a `status:in-progress` label (likely being worked on by another agent).
- Are pull request references (some repos track PRs as issues).

Present the top 5 candidates to the user and ask which one to work on. Include the issue number,
title, labels, and a one-line summary of the body. If the user has a preference, respect it. If they
say "any" or "you pick", choose the highest-priority candidate using this heuristic:

1. Issues with a milestone sort first.
2. Issues with `priority` or `urgent` labels sort next.
3. Issues with `bug` labels sort before `enhancement` or `feature`.
4. Older issues (lower number) sort before newer ones.

### 2. Claim the issue

**Race-condition guard:** Before claiming, re-fetch the issue to confirm it is still unassigned.
Another agent running in parallel may have claimed it between step 1 and now:

```sh
gh issue view <number> -R <owner>/<repo> --json assignees --jq '.assignees | length'
```

If the count is > 0, **skip this issue** and try the next candidate from the list gathered in
step 1. If all candidates have been claimed, re-run the search query from step 1. If the re-query
also returns no unassigned issues, inform the user that no unclaimed issues are available.

Assign yourself to the issue and add an "in progress" signal:

```sh
gh issue edit <number> -R <owner>/<repo> --add-assignee "@me"
```

If assignment fails (e.g., insufficient permissions), proceed anyway — note it in the comment
instead.

Check if the repo has a `status:in-progress` label:

```sh
gh label list -R <owner>/<repo> --search "status:in-progress" --json name --jq '.[].name'
```

If it exists, apply it:

```sh
gh issue edit <number> -R <owner>/<repo> --add-label "status:in-progress"
```

If not, skip — don't create labels automatically.

Leave a comment so others know the issue is being worked on:

```sh
gh issue comment <number> -R <owner>/<repo> --body "Picking this up — working on a fix now."
```

### 3. Preflight validation

Before writing any code, verify the environment is correct. Wrong repo/branch targeting is the #1
source of wasted work.

1. **Confirm the remote matches the intended repo.** Parse the actual owner/repo from the git
   remote and compare to the target:
   ```sh
   actual_remote=$(git remote get-url origin | sed -E 's|.*github\.com[:/]||; s|\.git$||')
   ```
   If `actual_remote` does not match `<owner>/<repo>`, **stop and error** — you're in the wrong
   repo. Do not proceed.

2. **Check CI on the base branch.** Ensure the base branch isn't broken before building on it:
   ```sh
   gh run list -R <owner>/<repo> --branch main --limit 1 --json conclusion --jq '.[0].conclusion'
   ```
   If the latest run failed, warn the user — your PR may inherit pre-existing CI failures.

### 4. Set up a branch

Fetch latest and create a feature branch. Use the human's GitHub username as the branch prefix.

**In a worktree, NEVER switch to the default branch (e.g., `main` or `master`) using `git switch`
or `git checkout`.** The default branch may be checked out in another worktree, and switching to it
would fail with `fatal: 'main' is already used by worktree at ...`. Instead, always create the
feature branch directly from `origin/<default>`:

```sh
USERNAME=$(gh api user --jq .login)
git fetch origin
default=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's|refs/remotes/origin/||')
[ -z "$default" ] && default="main"
git switch -c "${USERNAME}/<issue-slug>" "origin/${default}"
```

This creates the branch from the remote ref without needing to check out the default branch locally.

For the issue slug, derive a short kebab-case name from the issue title (e.g., issue #42 "Fix login
timeout" becomes `fix-login-timeout`). Keep it under 50 characters.

### 5. Start session memory (if available)

If the repo has a `docs/agent-sessions/` directory, run:

```
/session-memory start
```

Update `memory.md` with the issue reference and goal throughout the implementation.

### 5.5. Check for prior context

Before implementing, check whether past sessions touched related code. This prevents re-discovering
solutions or repeating failed approaches.

1. **Run `/recall`** to gather prior context from layered memory and archived transcripts:
   ```
   /recall <owner>/<repo> <issue-keyword>
   ```
   Review the returned decisions, pitfalls, and follow-ups before writing code.

2. **Fallback (if `/recall` is unavailable):**
   - Search `docs/agent-sessions/` for related `memory.md` files and read relevant ones.
   - If `transcript-archive` is available, run:
     ```sh
     transcript-archive search --repo <owner>/<repo> --limit 5 --query "<issue-keyword>" 2>/dev/null
     ```
   Use findings to avoid repeating prior failed approaches.

### 6. Implement the fix

Read the issue body carefully. Understand the requirements, acceptance criteria, and any linked
discussions or references.

**Before writing code:**

1. Read the relevant parts of the codebase — grep for related functions, types, and tests.
2. Check for a `CONTRIBUTING.md`, `AGENTS.md`, or `CLAUDE.md` in the repo for project-specific
   conventions.
3. Identify the files that need to change and the expected impact.
4. Plan the implementation.

**While implementing:**

1. Make minimal, focused changes — only what the issue requires.
2. Follow existing code style and conventions.
3. Write or update tests to cover the change.
4. Commit incrementally with clear messages referencing the issue:
   ```
   fix(scope): short description

   Resolves #<number>
   ```

**After implementing:**

1. Run the project's test suite, linter, and type checker.
2. Fix any failures before proceeding.

### 7. Verify and push

Before pushing, confirm you're on the correct branch and it tracks the right remote:

```sh
git branch -vv | grep '^\*'
```

Verify the output shows your feature branch tracking `origin/<your-branch>`, not `main` or another
branch. If something looks wrong, **stop and fix it** before pushing.

Then push:

```sh
git push -u origin HEAD
```

### 8. Open a pull request

Create a PR that links to the issue:

```sh
gh pr create -R <owner>/<repo> --title "<concise title>" --body "$(cat <<'EOF'
## Summary

<1-3 bullet points describing the change>

## Issue

Closes #<number>

## Test plan

- [ ] <how to verify the change>

🤖 Generated with Claude Code or Codex
EOF
)"
```

Use `Closes #<number>` in the body so GitHub auto-closes the issue when the PR merges.

### 9. Finalize session memory before shepherding

Before shepherding, ensure the session memory is finalized and included in the PR's commit chain.
Session memories committed after the PR is created (or on a different branch) can get stranded when
the PR squash-merges.

If a session memory was started in step 5:

1. Run `/session-memory finalize` to complete and stage the memory file.
2. Commit the session memory to the **PR branch** (the current feature branch):
   ```sh
   git add docs/agent-sessions/
   git commit -m "docs: finalize session memory for issue #<number>"
   ```
3. Push the commit so it's part of the PR:
   ```sh
   git push
   ```

This ensures the session memory is included in the squash-merge commit when the PR lands on main.

### 10. Shepherd the PR to merge

Invoke the shepherd skill to handle review, feedback, CI, and merge:

```
/shepherd-to-merge <owner>/<repo>#<pr-number>
```

The shepherd skill requires a **public inline reply on each review comment** (what you fixed or why
not) before resolving threads; follow that workflow end to end.

Wait for the shepherd process to complete. If it encounters issues it cannot resolve, surface them
to the user.

### 11. Confirm the issue is closed

After the PR is merged, verify the issue was automatically closed:

```sh
gh issue view <number> -R <owner>/<repo> --json state --jq .state
```

If the state is not `CLOSED`, close it manually with a reference to the PR:

```sh
gh issue close <number> -R <owner>/<repo> --comment "Resolved in #<pr-number>."
```

### 12. Remove in-progress signals

If a `status:in-progress` label was added in step 2, remove it:

```sh
gh issue edit <number> -R <owner>/<repo> --remove-label "status:in-progress"
```

If no status label was added, skip this step.

### 13. Clean up

Run the cleanup skill to prune branches and check for loose work. If running in a worktree,
`/cleanup` will attempt to return to `claude/<worktree-name>` or `codex/<worktree-name>` instead
of the default branch. If
that branch does not exist or switching fails, it will skip changing branches.

```
/cleanup
```

### 14. Print summary

Display:

- **Issue:** number, title, and URL
- **Branch:** name of the feature branch (now deleted or merged)
- **PR:** number, title, URL, and merge status
- **Issue status:** confirmed closed
- **Cleanup:** workspace tidied

---
> Converted and distributed by [TomeVault](https://tomevault.io/claim/bkonkle-dev) — claim your Tome and manage your conversions.
<!-- tomevault:4.0:skill_md:2026-04-13 -->

