Files
2025-11-30 09:07:10 +08:00

15 KiB

description, allowed-tools
description allowed-tools
github-sync Bash, Read, Edit, Write, Glob, Grep

github-sync

Two-way sync between AgileFlow stories and GitHub Issues with automatic status updates.

Prompt

ROLE: GitHub Integration Specialist

OBJECTIVE Synchronize AgileFlow stories with GitHub Issues bidirectionally, keeping status, labels, assignees, and milestones in sync.

INPUTS (optional)

  • MODE=import|export|sync (default: sync)
  • EPIC=<EP_ID> (filter by specific epic)
  • STORY=<US_ID> (sync single story)
  • DRY_RUN=true|false (default: false - preview changes)
  • DIRECTION=agileflow-to-github|github-to-agileflow|bidirectional (default: bidirectional)

PREREQUISITES

  1. GitHub MCP Server configured in .mcp.json:

    {
      "mcpServers": {
        "github": {
          "command": "npx",
          "args": ["-y", "@modelcontextprotocol/server-github"],
          "env": {
            "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token_here"
          }
        }
      }
    }
    

    Token permissions needed:

    • repo (full control) - for issues, PRs, labels
    • read:org - for organization access (if needed)

    Get token from: https://github.com/settings/tokens

  2. Repository configured in sync map or provided via GITHUB_REPO parameter

  3. Sync Mapping (create if missing): docs/08-project/github-sync-map.json:

    {
      "last_sync": "2025-10-17T14:30:00Z",
      "mappings": {
        "US-0030": {"issue_number": 42, "last_synced": "2025-10-17T10:00:00Z"},
        "US-0031": {"issue_number": 43, "last_synced": "2025-10-17T11:00:00Z"}
      },
      "config": {
        "status_mapping": {
          "ready": "Status: Ready",
          "in-progress": "Status: In Progress",
          "in-review": "Status: In Review",
          "done": "Status: Done"
        },
        "epic_to_milestone": {
          "EP-0010": "Milestone 1: Authentication",
          "EP-0011": "Milestone 2: Payments"
        }
      }
    }
    

SYNC WORKFLOW

1. Read Current State

# AgileFlow state
Read docs/09-agents/status.json
Read docs/06-stories/**/*.md

# GitHub state (via MCP tools)
# Use mcp__github__* tools to list issues, get repo info, etc.
# MCP provides: search_repositories, create_or_update_file, push_files, create_issue,
#              create_pull_request, fork_repository, create_repository, get_file_contents, etc.

2. Detect Changes

From AgileFlow to GitHub:

  • New stories not in mapping → Create GitHub Issues
  • Status changes → Update GitHub labels
  • Story updates (title, description, AC) → Update Issue body
  • Assignment changes → Update Issue assignees

From GitHub to AgileFlow:

  • New Issues with agileflow: label → Create stories
  • Issue closed → Update status to "done"
  • Label changes → Update status.json
  • Assignee changes → Update story frontmatter

3. Apply Changes (with conflict resolution)

If both sides changed:

  1. Check timestamps (last_synced vs. GitHub updated_at vs. bus/log.jsonl timestamp)
  2. Prefer most recent change
  3. Log conflict to docs/09-agents/bus/log.jsonl:
    {"ts":"2025-10-17T14:30:00Z","type":"sync-conflict","story":"US-0030","github_issue":42,"resolution":"kept_github","reason":"GitHub updated more recently"}
    

AGILEFLOW → GITHUB EXPORT

Create GitHub Issue from Story

For each story in docs/06-stories/**/*.md:

# Read story frontmatter
story_id=US-0030
title="User registration endpoint"
owner=AG-API
epic=EP-0010
status=done

# Create issue if not exists
if ! in_mapping($story_id); then
  issue_number=$(gh issue create \
    --repo $GITHUB_REPO \
    --title "[$story_id] $title" \
    --body "$(cat <<EOF
## Story: $story_id

**Epic**: $epic
**Owner**: $owner
**Estimate**: 1d

### Acceptance Criteria

- [ ] Given a new user submits registration form
- [ ] When all fields are valid
- [ ] Then account is created and confirmation email sent

### Links
- [Story file](docs/06-stories/EP-0010/US-0030.md)
- [Test cases](docs/07-testing/test-cases/US-0030.md)

---
*Synced from AgileFlow*
EOF
)" \
    --label "agileflow:story" \
    --label "epic:EP-0010" \
    --label "owner:AG-API" \
    --label "Status: Done" \
    --milestone "Milestone 1: Authentication" \
    | grep -oP '#\K\d+')

  # Record mapping
  update_mapping $story_id $issue_number
fi

Update GitHub Issue Status

# When status changes in status.json
story_id=US-0030
new_status=in-review

# Remove old status label, add new
gh issue edit $issue_number \
  --remove-label "Status: In Progress" \
  --add-label "Status: In Review"

# If done, close issue
if [ "$new_status" = "done" ]; then
  gh issue close $issue_number --comment "✅ Story completed in AgileFlow"
fi

# Log to bus
echo "{\"ts\":\"$(date -Iseconds)\",\"type\":\"github-sync\",\"story\":\"$story_id\",\"issue\":$issue_number,\"action\":\"status_updated\",\"status\":\"$new_status\"}" >> docs/09-agents/bus/log.jsonl

GITHUB → AGILEFLOW IMPORT

Create Story from GitHub Issue

For each Issue with label agileflow:story:

gh issue view $issue_number --json number,title,body,labels,assignees,milestone,state

# Extract story metadata from labels
epic=$(echo $labels | grep -oP 'epic:\K[A-Z]+-\d+')
owner=$(echo $labels | grep -oP 'owner:\K[A-Z]+-[A-Z]+')
status_label=$(echo $labels | grep -oP 'Status: \K.*')

# Map status
case "$status_label" in
  "Ready") status=ready ;;
  "In Progress") status=in-progress ;;
  "In Review") status=in-review ;;
  "Done") status=done ;;
esac

# Generate story ID if not in title
if [[ $title =~ \[US-([0-9]+)\] ]]; then
  story_id=US-${BASH_REMATCH[1]}
else
  # Find next available ID
  story_id=$(find_next_story_id)
  # Update GitHub issue title
  gh issue edit $issue_number --title "[$story_id] $title"
fi

# Create story file if doesn't exist
if [ ! -f "docs/06-stories/$epic/$story_id.md" ]; then
  cat > "docs/06-stories/$epic/$story_id.md" <<EOF
---
id: $story_id
title: $title
epic: $epic
owner: $owner
status: $status
estimate: 1d
github_issue: $issue_number
---

# $title

## Description
$body

## Acceptance Criteria

- [ ] (Imported from GitHub - please refine)

## GitHub
- Issue: #$issue_number
- [View on GitHub](https://github.com/$GITHUB_REPO/issues/$issue_number)
EOF

  # Update status.json
  update_status_json $story_id $status $owner

  # Log to bus
  echo "{\"ts\":\"$(date -Iseconds)\",\"type\":\"github-import\",\"story\":\"$story_id\",\"issue\":$issue_number,\"action\":\"created\"}" >> docs/09-agents/bus/log.jsonl
fi

Sync Issue Closure

# When GitHub issue is closed
if [ "$issue_state" = "CLOSED" ]; then
  # Update AgileFlow status
  story_id=$(get_story_from_mapping $issue_number)

  if [ -n "$story_id" ]; then
    # Update status.json
    update_status_json $story_id "done" "$owner"

    # Log to bus
    echo "{\"ts\":\"$(date -Iseconds)\",\"type\":\"github-sync\",\"story\":\"$story_id\",\"issue\":$issue_number,\"action\":\"closed\",\"status\":\"done\"}" >> docs/09-agents/bus/log.jsonl
  fi
fi

SYNC REPORT

After sync completes, generate report:

# GitHub Sync Report

**Generated**: 2025-10-17 14:30
**Mode**: Bidirectional sync
**Repository**: owner/repo

---

## 📊 Summary

**AgileFlow → GitHub**: 5 changes
- ✅ 3 issues created
- ✅ 2 statuses updated

**GitHub → AgileFlow**: 2 changes
- ✅ 1 story created
- ✅ 1 status updated (closed)

**Conflicts**: 1 resolved
- US-0030: GitHub updated more recently (kept GitHub state)

---

## 📤 Exported to GitHub

| Story | Issue | Action | Status |
|-------|-------|--------|--------|
| US-0042 | #45 | Created | ✅ Success |
| US-0043 | #46 | Created | ✅ Success |
| US-0044 | #47 | Created | ✅ Success |
| US-0038 | #40 | Updated status | ✅ Success |
| US-0035 | #38 | Updated status | ✅ Success |

## 📥 Imported from GitHub

| Issue | Story | Action | Status |
|-------|-------|--------|--------|
| #48 | US-0050 | Created story | ✅ Success |
| #42 | US-0030 | Closed → done | ✅ Success |

## ⚠️ Conflicts Resolved

| Story | Issue | Conflict | Resolution |
|-------|-------|----------|------------|
| US-0030 | #42 | Both updated | Kept GitHub (more recent) |

## 🔗 Mapping Updates

Updated `docs/08-project/github-sync-map.json` with 5 new mappings.

**Next sync**: Run `/AgileFlow:github-sync` again to keep in sync, or set up GitHub Actions webhook.

---

## 🤖 Automation Recommendation

Set up automatic sync with GitHub Actions:

`.github/workflows/agileflow-sync.yml`:
```yaml
name: AgileFlow Sync
on:
  issues:
    types: [opened, edited, closed, labeled]
  schedule:
    - cron: '0 */6 * * *'  # Every 6 hours

jobs:
  sync:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: /AgileFlow:github-sync MODE=sync

LABEL MANAGEMENT

### Auto-create AgileFlow Labels

```bash
# Create standard labels if they don't exist
labels=(
  "agileflow:story|Story tracked in AgileFlow|0366d6"
  "epic:EP-0010|Epic: User Authentication|d4c5f9"
  "owner:AG-UI|Owner: UI Agent|fbca04"
  "owner:AG-API|Owner: API Agent|0e8a16"
  "Status: Ready|Ready to start|ededed"
  "Status: In Progress|Work in progress|fbca04"
  "Status: In Review|Under review|0075ca"
  "Status: Done|Completed|0e8a16"
)

for label_def in "${labels[@]}"; do
  IFS='|' read -r name description color <<< "$label_def"
  gh label create "$name" --description "$description" --color "$color" --force
done

WEBHOOK INTEGRATION (Advanced)

For real-time sync, set up GitHub webhook:

# webhook-handler.sh (runs on issue events)
payload=$(cat)
action=$(echo $payload | jq -r '.action')
issue_number=$(echo $payload | jq -r '.issue.number')

case $action in
  opened|edited)
    # Import/update story
    /AgileFlow:github-sync MODE=import ISSUE=$issue_number
    ;;
  closed)
    # Mark story done
    story_id=$(get_story_from_mapping $issue_number)
    /AgileFlow:status STORY=$story_id STATUS=done
    ;;
  labeled)
    # Sync status if status label changed
    new_label=$(echo $payload | jq -r '.label.name')
    if [[ $new_label =~ ^Status: ]]; then
      /AgileFlow:github-sync MODE=import ISSUE=$issue_number
    fi
    ;;
esac

CONFLICT RESOLUTION STRATEGY

  1. Timestamp comparison:

    • AgileFlow: Parse latest timestamp from bus/log.jsonl for story
    • GitHub: Use updated_at from issue
    • Winner: Most recent timestamp
  2. Manual resolution (if timestamps within 5 minutes):

    • Show diff to user
    • Ask which to keep
    • Log decision to bus
  3. Auto-resolution rules:

    • Status changes: Prefer GitHub (closer to source of truth for developers)
    • Content changes: Prefer AgileFlow (more detailed AC and structure)
    • Assignment: Prefer GitHub (reflects actual work assignment)

DRY RUN MODE

Preview changes before applying:

/AgileFlow:github-sync DRY_RUN=true

Output:

# GitHub Sync (DRY RUN)

**Would apply 7 changes** (use DRY_RUN=false to apply)

## AgileFlow → GitHub (5 changes)

✏️ **Create** Issue for US-0042 "Login form UI"
  - Labels: agileflow:story, epic:EP-0010, owner:AG-UI, Status: Ready
  - Milestone: Milestone 1: Authentication

✏️ **Create** Issue for US-0043 "Profile page"
  - Labels: agileflow:story, epic:EP-0011, owner:AG-UI, Status: Ready

✏️ **Update** Issue #40 status label
  - Remove: Status: Ready
  - Add: Status: In Progress

## GitHub → AgileFlow (2 changes)

✏️ **Create** Story US-0050 from Issue #48 "Add password reset flow"
  - File: docs/06-stories/EP-0010/US-0050.md
  - Status: ready
  - Owner: AG-API

✏️ **Update** US-0030 status to "done" (Issue #42 closed)
  - Update status.json
  - Log to bus

**Run with DRY_RUN=false to apply these changes.**

ERROR HANDLING

# Check for GitHub MCP tools
if ! command -v mcp__github__search_repositories &> /dev/null; then
  echo "❌ GitHub MCP not configured. Add to .mcp.json:"
  echo '  "github": {'
  echo '    "command": "npx",'
  echo '    "args": ["-y", "@modelcontextprotocol/server-github"],'
  echo '    "env": {"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token"}'
  echo '  }'
  echo ""
  echo "Then restart Claude Code to load MCP server."
  exit 1
fi

# Validate sync map
if [ ! -f "docs/08-project/github-sync-map.json" ]; then
  echo "⚠️ Sync map not found. Creating new mapping file..."
  mkdir -p docs/08-project
  echo '{"last_sync":null,"mappings":{},"config":{}}' > docs/08-project/github-sync-map.json
fi

USAGE EXAMPLES

Full bidirectional sync

/AgileFlow:github-sync

Export all stories to GitHub

/AgileFlow:github-sync MODE=export

Import GitHub issues to AgileFlow

/AgileFlow:github-sync MODE=import

Sync single story

/AgileFlow:github-sync STORY=US-0030

Sync specific epic

/AgileFlow:github-sync EPIC=EP-0010

Preview changes (dry run)

/AgileFlow:github-sync DRY_RUN=true

One-way sync (AgileFlow → GitHub only)

/AgileFlow:github-sync DIRECTION=agileflow-to-github

INTEGRATION WITH OTHER COMMANDS

  • After /AgileFlow:story-new: Optionally prompt to create GitHub issue
  • After /AgileFlow:status: Auto-sync status to GitHub
  • In /AgileFlow:board: Show GitHub issue links
  • In /AgileFlow:velocity: Include GitHub activity metrics

RULES

  • Never create duplicate issues (check mapping first)
  • Always log sync actions to bus/log.jsonl
  • Preserve GitHub issue numbers in story frontmatter
  • Use labels for all metadata (status, epic, owner)
  • Handle rate limits gracefully (GitHub API: 5000 req/hour)
  • Validate GitHub token before any write operations

OUTPUT

  • Sync report (markdown)
  • Updated github-sync-map.json
  • Updated status.json (if GitHub → AgileFlow changes)
  • Bus log entries for all sync actions
  • Optional: Saved report to docs/08-project/sync-reports/sync-YYYYMMDD-HHMMSS.md

GITHUB MCP INTEGRATION

This command uses GitHub MCP for all GitHub operations, providing:

Advantages

  • No sudo required - npx handles installation automatically
  • Consistent with Notion - Both use MCP with tokens in .mcp.json
  • Native Claude Code integration - Built-in MCP tool support
  • No CLI dependency - Works in any environment (Docker, codespaces, etc.)
  • Unified configuration - All integrations in one .mcp.json file

Available MCP Tools

The GitHub MCP server provides these tools (prefix: mcp__github__):

  • search_repositories - Search for repositories
  • create_or_update_file - Create/update files in repo
  • create_issue - Create GitHub issues
  • create_pull_request - Create PRs
  • get_file_contents - Read file contents
  • push_files - Batch file operations
  • fork_repository - Fork repos
  • create_repository - Create new repos

Migration from gh CLI

If you previously used GitHub CLI (gh):

  1. Remove gh CLI (no longer needed)
  2. Add GitHub MCP to .mcp.json (see Prerequisites above)
  3. Restart Claude Code to load MCP server
  4. Run /AgileFlow:github-sync - it now uses MCP automatically

Why We Switched

  • Removes sudo/installation barrier
  • Consistent security model across all integrations
  • Better portability across environments
  • Simpler team onboarding (just copy .mcp.json.example)

  • /AgileFlow:notion-export - Sync with Notion (uses MCP)
  • /AgileFlow:story-new - Create new story (can auto-create GitHub issue)
  • /AgileFlow:board - Visualize stories with GitHub links
  • /AgileFlow:velocity - Track velocity including GitHub activity