Initial commit
This commit is contained in:
589
commands/github.md
Normal file
589
commands/github.md
Normal file
@@ -0,0 +1,589 @@
|
||||
---
|
||||
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
|
||||
Reference in New Issue
Block a user