590 lines
15 KiB
Markdown
590 lines
15 KiB
Markdown
---
|
|
description: github-sync
|
|
allowed-tools: 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`:
|
|
```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`:
|
|
```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
|
|
|
|
```bash
|
|
# 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:
|
|
```json
|
|
{"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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# 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`:
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```markdown
|
|
# 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
/AgileFlow:github-sync DRY_RUN=true
|
|
```
|
|
|
|
Output:
|
|
```markdown
|
|
# 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
|
|
|
|
```bash
|
|
# 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
|
|
```bash
|
|
/AgileFlow:github-sync
|
|
```
|
|
|
|
### Export all stories to GitHub
|
|
```bash
|
|
/AgileFlow:github-sync MODE=export
|
|
```
|
|
|
|
### Import GitHub issues to AgileFlow
|
|
```bash
|
|
/AgileFlow:github-sync MODE=import
|
|
```
|
|
|
|
### Sync single story
|
|
```bash
|
|
/AgileFlow:github-sync STORY=US-0030
|
|
```
|
|
|
|
### Sync specific epic
|
|
```bash
|
|
/AgileFlow:github-sync EPIC=EP-0010
|
|
```
|
|
|
|
### Preview changes (dry run)
|
|
```bash
|
|
/AgileFlow:github-sync DRY_RUN=true
|
|
```
|
|
|
|
### One-way sync (AgileFlow → GitHub only)
|
|
```bash
|
|
/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`)
|
|
|
|
---
|
|
|
|
## RELATED COMMANDS
|
|
|
|
- `/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
|