Close GitHub issue with PR workflow
Creates a PR that auto-closes a GitHub issue when merged. Use this instead of manually closing issues to maintain proper workflow tracking.
/plugin marketplace add atxtechbro/dotfiles/plugin install dotfiles-commands@dotfiles-marketplace--dry-run: Preview execution plan without performing git operations (shows issue details, worktree location, commit preview, PR preview)--json: Output in machine-readable JSON format (useful with --dry-run for tooling)--no-worktree: Work in main repo instead of creating isolated worktree (overrides config)--skip-retro: Skip retro step entirely (overrides config)Examples:
Provider Notes:
Complete and implement GitHub issue #{{ ISSUE_NUMBER }}.
{{ KNOWLEDGE_BASE }}
<!-- Note: If you see "{{ KNOWLEDGE_BASE }}" above as literal text, you're running locally and knowledge is already preloaded -->Load workflow preferences from .agent-config.yml (Config in Environment principle):
!# YAML config parser with nested key support
!CONFIG_FILE="${DOTFILES_ROOT:-.}/.agent-config.yml"
!
!# Function to extract nested YAML values
!# Supports paths like: "agents.close-issue.git.use_worktree"
!get_config() {
! local path="$1"
! local default="$2"
!
! if [ ! -f "$CONFIG_FILE" ]; then
! echo "$default"
! return
! fi
!
! # Try Python with PyYAML for robust parsing (handles nested keys)
! if command -v python3 &>/dev/null; then
! python3 -c "
!import sys
!try:
! import yaml
! with open('$CONFIG_FILE') as f:
! config = yaml.safe_load(f) or {}
!
! # Navigate nested path
! value = config
! for key in '$path'.split('.'):
! if isinstance(value, dict) and key in value:
! value = value[key]
! else:
! print('$default')
! sys.exit(0)
!
! # Variable substitution
! if isinstance(value, str):
! import os
! result = value.replace('${HOME}', os.path.expanduser('~'))
! # Substitute user.* references
! if '${user.' in result:
! user = config.get('user', {})
! result = result.replace('${user.github_username}', user.get('github_username', ''))
! result = result.replace('${user.name}', user.get('name', ''))
! print(result)
! else:
! print(value)
!except ImportError:
! # PyYAML not available, use fallback
! sys.exit(1)
!except Exception:
! print('$default')
!" 2>/dev/null && return
! fi
!
! # Fallback: simple grep for top-level keys only
! echo "⚠️ Warning: PyYAML not available, using simple config parser (top-level keys only)" >&2
! local simple_key="${path##.}" # Get last component
! grep "^[[:space:]]${simple_key}:" "$CONFIG_FILE" 2>/dev/null |
! sed 's/.:[[:space:]]//' |
! tr -d '"' || echo "$default"
!}
!
!# Load configuration with graceful defaults
!CONFIG_USE_WORKTREE=$(get_config "agents.close-issue.git.use_worktree" "true")
!CONFIG_WORKTREE_BASE=$(get_config "agents.close-issue.git.worktree_base" "${HOME}/worktrees")
!CONFIG_SKIP_RETRO=$(get_config "agents.close-issue.workflow.skip_retro" "true")
!
!echo "📋 Configuration loaded:"
!echo " Use worktree: $CONFIG_USE_WORKTREE"
!echo " Worktree base: $CONFIG_WORKTREE_BASE"
!echo " Skip retro: $CONFIG_SKIP_RETRO"
!echo ""
If .agent-config.yml doesn't exist or PyYAML unavailable, gracefully falls back to defaults.
Detect dry-run, JSON output, and workflow override flags from user input:
!# Parse flags from the user's command invocation !# Supports --dry-run, --json, --no-worktree, --skip-retro, and natural language variants !DRY_RUN=false !JSON_OUTPUT=false !USE_WORKTREE="$CONFIG_USE_WORKTREE" # Start with config value !SKIP_RETRO="$CONFIG_SKIP_RETRO" # Start with config value ! !# Get the full user input (this variable is provided by the LLM context) !USER_INPUT="${USER_INPUT:-$*}" ! !# Check for dry-run flag variants !if echo "$USER_INPUT" | grep -qiE '(--dry-run|dry.run|preview|show.me.what.would.happen)'; then ! DRY_RUN=true !fi ! !# Check for JSON output flag !if echo "$USER_INPUT" | grep -qiE '(--json)'; then ! JSON_OUTPUT=true !fi ! !# Check for workflow override flags (override config) !if echo "$USER_INPUT" | grep -qiE '(--no-worktree|no.worktree)'; then ! USE_WORKTREE=false !fi ! !if echo "$USER_INPUT" | grep -qiE '(--skip-retro|skip.retro)'; then ! SKIP_RETRO=true !fi ! !# If dry-run mode detected, show banner !if [ "$DRY_RUN" = "true" ]; then ! if [ "$JSON_OUTPUT" = "true" ]; then ! echo '{"dry_run": true, "mode": "json", "command": "close-issue"}' ! else ! echo "🧠 Dry Run Mode: Close Issue" ! echo "──────────────────────────────────────" ! echo "Preview mode - no git operations will be performed" ! echo "" ! fi !fi
The dry-run mode will:
Fetch issue context from GitHub (safe read-only operation, runs in both normal and dry-run modes):
!ISSUE_NUMBER="{{ ISSUE_NUMBER }}" !ISSUE_NUMBER="{{ ISSUE_NUMBER }}" !if ! ISSUE_DATA=$(gh issue view "$ISSUE_NUMBER" --json title,body,labels,number 2>/dev/null); then ! echo "Error: Could not fetch issue #$ISSUE_NUMBER. Please check:" ! echo " - Issue number is correct" ! echo " - You have access to the repository" ! echo " - GitHub CLI is authenticated (run 'gh auth status')" ! exit 1 !fi !ISSUE_TITLE=$(echo "$ISSUE_DATA" | jq -r '.title') !ISSUE_BODY=$(echo "$ISSUE_DATA" | jq -r '.body') ! !echo "Fetched issue #$ISSUE_NUMBER: $ISSUE_TITLE"
If dry-run mode is active, show the execution plan and exit:
!if [ "$DRY_RUN" = "true" ]; then ! # Calculate planned values using simpler bash to avoid escaping issues ! # Convert to lowercase and slugify ! ISSUE_SLUG=$(echo "$ISSUE_TITLE" | awk '{print tolower($0)}' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | cut -c1-50) ! # Ensure slug is not empty and has minimum length ! if [ -z "$ISSUE_SLUG" ] || [ ${#ISSUE_SLUG} -lt 3 ]; then ! ISSUE_SLUG="issue-implementation" ! fi ! BRANCH_NAME="issue-${ISSUE_NUMBER}-${ISSUE_SLUG}" ! WORKTREE_PATH="$CONFIG_WORKTREE_BASE/issue-${ISSUE_NUMBER}" ! ! if [ "$JSON_OUTPUT" = "true" ]; then ! # Calculate boolean values for JSON ! RETRO_ENABLED=$([ "$SKIP_RETRO" = "false" ] && echo true || echo false) ! WORKTREE_CMD=$([ "$USE_WORKTREE" = "true" ] && echo ""git worktree add $WORKTREE_PATH -b $BRANCH_NAME"," || echo ""# (working in main repo - no worktree)",") ! ! # JSON format output ! cat <<EOF !{ ! "dry_run": true, ! "command": "close-issue", ! "config": { ! "use_worktree": $USE_WORKTREE, ! "worktree_base": "$CONFIG_WORKTREE_BASE", ! "skip_retro": $SKIP_RETRO ! }, ! "issue": { ! "number": $ISSUE_NUMBER, ! "title": "$ISSUE_TITLE", ! "repository": "atxtechbro/dotfiles" ! }, ! "planned_actions": [ ! {"step": 1, "description": "Fetch issue details", "status": "completed (read-only)"}, ! {"step": 2, "description": "Create worktree", "path": "$WORKTREE_PATH", "branch": "$BRANCH_NAME", "enabled": $USE_WORKTREE}, ! {"step": 3, "description": "Implement solution", "note": "Interactive with user"}, ! {"step": 4, "description": "Create commit", "message": "Closes #$ISSUE_NUMBER"}, ! {"step": 5, "description": "Push branch to origin"}, ! {"step": 6, "description": "Create pull request"}, ! {"step": 7, "description": "Run retro", "enabled": $RETRO_ENABLED} ! ], ! "would_execute": [ ! $WORKTREE_CMD ! "git commit -m 'feat: <implementation>\n\nCloses #$ISSUE_NUMBER'", ! "git push -u origin $BRANCH_NAME", ! "gh pr create --title '<PR title>' --body '...'" ! ], ! "execution_status": "skipped (dry-run mode)" !} !EOF ! else ! # Human-readable format ! echo "" ! echo "Issue #$ISSUE_NUMBER: $ISSUE_TITLE" ! echo "──────────────────────────────────────" ! echo "Repository: atxtechbro/dotfiles" ! echo "Base Branch: main" ! echo "" ! echo "Effective Configuration:" ! echo " Use worktree: $USE_WORKTREE" ! echo " Worktree base: $CONFIG_WORKTREE_BASE" ! echo " Skip retro: $SKIP_RETRO" ! echo "" ! echo "Planned Actions:" ! echo " 1. ✓ Fetch issue details (completed - read-only)" ! if [ "$USE_WORKTREE" = "true" ]; then ! echo " 2. ✓ Create worktree at: $WORKTREE_PATH" ! echo " 3. Create branch: $BRANCH_NAME" ! else ! echo " 2. ⊘ Create worktree (disabled - working in main repo)" ! fi ! echo " 4. Implement solution (interactive with you)" ! echo " 5. Commit changes with message: 'Closes #$ISSUE_NUMBER'" ! echo " 6. Push branch to origin" ! echo " 7. Create PR with title from implementation" ! if [ "$SKIP_RETRO" = "true" ]; then ! echo " 8. ⊘ Run retro (skipped per config)" ! else ! echo " 8. ✓ Run retro" ! fi ! echo "" ! echo "Would Execute Commands:" ! echo " $ gh issue view $ISSUE_NUMBER --json title,body,labels" ! if [ "$USE_WORKTREE" = "true" ]; then ! echo " $ git worktree add $WORKTREE_PATH -b $BRANCH_NAME" ! echo " $ cd $WORKTREE_PATH" ! else ! echo " $ # (working in main repo - no worktree created)" ! echo " $ git checkout -b $BRANCH_NAME" ! fi ! echo " $ # [Interactive implementation happens here]" ! echo " $ git add ." ! echo " $ git commit -m 'feat: <description>\n\nCloses #$ISSUE_NUMBER'" ! echo " $ git push -u origin $BRANCH_NAME" ! echo " $ gh pr create --title '<title>' --body '<body>'" ! echo "" ! echo "──────────────────────────────────────" ! echo "(No git operations executed - dry-run mode)" ! echo "" ! echo "To execute for real, run without --dry-run flag" ! fi ! exit 0 !fi
Create isolated worktree or work in main repo based on configuration:
!# Use external script to avoid bash escaping issues in command execution !SCRIPT_PATH="${DOTFILES_ROOT:-.}/scripts/setup-issue-worktree.sh" !if [ ! -f "$SCRIPT_PATH" ]; then ! echo "Error: setup-issue-worktree.sh not found at $SCRIPT_PATH" ! exit 1 !fi ! !# Run the setup script
if ! SETUP_OUTPUT=$(bash "$SCRIPT_PATH" "$ISSUE_NUMBER" "$ISSUE_TITLE" "$CONFIG_WORKTREE_BASE" "$USE_WORKTREE"); then echo "Error: Failed to execute setup-issue-worktree.sh" exit 1 fi echo "$SETUP_OUTPUT" ! !# Extract variables from script output
WORKTREE_PATH=$(echo "$SETUP_OUTPUT" | grep "^WORKTREE_PATH=" | cut -d= -f2) BRANCH_NAME=$(echo "$SETUP_OUTPUT" | grep "^BRANCH_NAME=" | cut -d= -f2)
if [ -z "$BRANCH_NAME" ]; then echo "Error: Failed to extract BRANCH_NAME from setup script output" exit 1 fi
if [ "$USE_WORKTREE" = "true" ] && [ -z "$WORKTREE_PATH" ]; then echo "Error: Failed to extract WORKTREE_PATH from setup script output" exit 1 fi ! !# Change to worktree if created !if [ "$USE_WORKTREE" = "true" ] && [ -n "$WORKTREE_PATH" ]; then ! cd "$WORKTREE_PATH" ! echo "Working in worktree: $WORKTREE_PATH" !fi
Build the solution using tracer bullets - get something working first, then iterate.
IMPORTANT: This procedure outputs a GitHub Pull Request. The PR must be created, not just planned.
KEY WORKFLOW:
See .github/PULL_REQUEST_TEMPLATE.md for complete guidance on title patterns and body sections.
!if [ "$SKIP_RETRO" = "true" ]; then ! echo "⊘ Retro skipped (disabled in configuration)" ! echo "To enable retro, set agents.close-issue.workflow.skip_retro: false in .agent-config.yml" ! exit 0 !fi
Let's retro this context and wring out the gleanings.
Consider capturing any ghost procedures that emerged during this work - see Procedure Creation.
What would you like to focus on?