Synchronization: Plane.so & GitHub
AgilePlus synchronizes specifications, work packages, and status with external trackers, keeping your workflow system and issue tracker in sync.
Supported Integrations
Plane.so
Full bi-directional sync with Plane project management:
| What Syncs | Direction | Notes |
|---|---|---|
| Issues | ← → | Specs create issues, updates sync both ways |
| States | ← → | Done/blocked/done syncs states in Plane |
| Labels | ← → | AgilePlus labels sync to Plane |
| Milestones | ← → | WP groups sync as milestones |
| Assignees | ← → | WP owner syncs to Plane assignee |
| Comments | ← | Plane comments pull into AgilePlus |
GitHub Issues
Bi-directional sync with GitHub Issues:
| What Syncs | Direction | Notes |
|---|---|---|
| Issues | ← → | Create issues from specs |
| Labels | ← → | Status labels sync both ways |
| Milestones | ← → | Tie issues to release milestones |
| PR Links | → | Link issues to merged PRs |
| Assignees | ← → | Assignment syncs both ways |
GitHub Projects (v2)
Push-only sync to GitHub Projects board:
| What Syncs | Direction | Notes |
|---|---|---|
| Cards | → | WPs become cards |
| Status | → | WP lane → Status field |
| Custom Fields | → | Custom field mapping |
Setup & Configuration
1. Get API Credentials
For Plane.so:
- Go to Plane Settings
- Click API Tokens
- Create new token, copy it
export PLANE_API_KEY="your_token_here"For GitHub:
- Go to GitHub Settings → Developer settings → Personal access tokens
- Create new token with scopes:
repo,read:org - Copy token
export GITHUB_TOKEN="ghp_xxxxxxxxxxxxxxxxxxxx"2. Configure AgilePlus
Edit .kittify/config.toml:
[sync.plane]
enabled = true
workspace = "my-org"
project = "my-project"
api_key = "${PLANE_API_KEY}"
[sync.github]
enabled = true
repo = "org/my-repo"
token = "${GITHUB_TOKEN}"
[sync.github.pr]
auto_create = true
draft = false
request_review = true
[sync]
interval = 5 # Sync every 5 minutes
conflict_strategy = "local-wins"3. Test Connection
agileplus sync --testOutput:
Testing Plane.so connection...
✓ Connected to workspace: my-org
✓ Project: my-project
✓ API key valid
Testing GitHub connection...
✓ Connected to repo: org/my-repo
✓ Token valid
✓ Can create issues and PRs
All connections working!Sync Workflow
Automatic Sync
By default, AgilePlus syncs every 5 minutes (configurable):
AgilePlus State Plane.so GitHub Issues
───────────────────── ───────────────── ──────────────
Planned ──────→ Bug #123 #456
Doing ──────→ In Progress In Progress
Done ──────→ Done Closed
←────── (Label changes) (Re-assigned)Manual Sync
# Full bi-directional sync
agileplus sync
# Only push local changes
agileplus sync --push-only
# Only pull remote changes
agileplus sync --pull-only
# Verbose output
agileplus sync --verbose
# Test without making changes
agileplus sync --dry-runOne-Way Sync
Create issues in tracker without auto-update:
[sync]
direction = "push-only"Only pull from tracker without creating issues:
[sync]
direction = "pull-only"Mapping: Specs to Issues
When you create a specification, AgilePlus can create a tracker issue:
agileplus specify "Add two-factor authentication"
# Automatically creates issue in Plane/GitHub if sync enabled
# → [SPEC] Add two-factor authentication #123The sync creates:
Issue Title: [SPEC] Add two-factor authentication
Description: Feature specification
Label: spec
Status: New
Milestone: (current sprint, if configured)Mapping: Work Packages to Issues
When you create work packages, they sync to the tracker:
agileplus tasks 001
# Creates issues for each WP
# → [WP] WP01: Database schema #124
# → [WP] WP02: Models layer #125
# → [WP] WP03: API endpoints #126Each WP issue shows:
Title: [WP] WP01: Database schema
Description: Work package WP01 for feature 001
Label: work-package, wp-01
Status: Planned
Assignee: (owner when assigned)
Milestone: Feature 001Status Synchronization
AgilePlus → Tracker
When you move a WP through lanes:
agileplus move WP01 --to doing # Updates tracker: In Progress
agileplus move WP01 --to for_review # Updates tracker: In Review
agileplus move WP01 --to done # Updates tracker: DoneThe tracker issue status updates automatically.
Tracker → AgilePlus
When you update status in the tracker:
Plane.so Status → AgilePlus Lane
───────────────────────────────────
Backlog → planned
Todo → planned
In Progress → doing
In Review → for_review
Done → doneThe next sync pulls these changes.
Label Management
Auto-Generated Labels
AgilePlus creates standard labels:
spec # For specifications
work-package # For work packages
wp-01, wp-02 # Per-work-package labels
high-priority # From queue priority
bug, feature # From issue typeCustom Label Mapping
[sync.label_mapping]
my-urgent = "critical"
in-review = "for_review"
approved = "done"Conflict Resolution
When both AgilePlus and tracker make conflicting changes:
Strategy: local-wins (default)
[sync]
conflict_strategy = "local-wins"If AgilePlus says "Done" and tracker says "In Progress", AgilePlus wins.
Strategy: remote-wins
[sync]
conflict_strategy = "remote-wins"If tracker says "In Progress" and AgilePlus says "Done", tracker wins.
Strategy: merge
[sync]
conflict_strategy = "merge"Conflicting labels are merged (both kept). Only status uses winner strategy.
Advanced: Custom Field Mapping
Map AgilePlus data to custom fields in tracker:
[sync.github.custom_fields]
complexity = "Estimated Hours"
risk = "Risk Level"
dependencies = "Blocked By"Then use in GitHub Projects:
# View custom fields
agileplus show WP01 --include-custom-fieldsTroubleshooting
Sync Not Working
# Check sync status
agileplus sync --status
# Enable debug logging
agileplus sync -vv
# Test credentials
agileplus config show --include-secretsCheck firewall:
Make sure you can reach API endpoints:
# Plane.so
curl -H "Authorization: Bearer $PLANE_API_KEY" \
https://api.plane.so/api/workspaces/
# GitHub
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
https://api.github.com/user/reposIssues Not Syncing
# Verify labels are created
agileplus sync --labels-only
# Check mapping configuration
cat .kittify/config.toml | grep -A 10 "sync"
# Force full sync
agileplus sync --force --verboseDuplicate Issues Created
This happens if sync runs twice on same feature:
# Check existing issues in tracker
agileplus sync --dry-run
# If duplicates exist, manually delete in tracker
# Then re-run sync
agileplus sync --forceToken Invalid
# Re-enter credentials
export PLANE_API_KEY="new_token"
export GITHUB_TOKEN="new_token"
# Test again
agileplus sync --testExamples
Example 1: Full Plane.so Integration
[sync.plane]
enabled = true
workspace = "acme-corp"
project = "backend-api"
api_key = "${PLANE_API_KEY}"
[sync.plane.mapping]
spec_label = "spec"
wp_label = "work-package"
priority_map = {
critical = "urgent",
high = "high",
medium = "medium",
low = "low"
}
[sync]
interval = 5
conflict_strategy = "local-wins"Run sync:
agileplus sync --verbose
# Output:
# ✓ Synced 3 specs as issues
# ✓ Synced 12 work packages
# ✓ Updated 5 issue states
# ✓ Pulled 2 label changes from Plane.soExample 2: GitHub Issues Only (No Auto PR)
[sync.github]
enabled = true
repo = "acme-corp/api"
token = "${GITHUB_TOKEN}"
[sync.github.pr]
auto_create = false # Don't auto-create PRs
[sync.github.mapping]
labels = {
spec = "spec",
wp = "work-package"
}Example 3: Push-Only (Don't Pull Tracker Changes)
[sync]
direction = "push-only"
[sync.plane]
enabled = true
workspace = "my-org"
project = "my-project"
api_key = "${PLANE_API_KEY}"This creates issues in tracker but doesn't pull status updates back.
Best Practices
1. Sync Before Merging
agileplus sync --pull-only
# Make sure no conflicting changes from tracker
agileplus merge 0012. Regular Sync Intervals
Don't set sync interval too low (API rate limits):
[sync]
interval = 5 # 5 minutes is reasonable3. Link Everything
When creating specs, link to tracker:
agileplus specify "title" --tracker-issue https://github.com/org/repo/issues/1234. Archive Old Issues
Clean up closed issues to reduce clutter:
agileplus sync --archive-closed5. Review Mapping Regularly
Check that labels and statuses still match:
agileplus config show --include syncWebhook-Driven Sync
For real-time bidirectional sync (instead of polling), configure webhooks. When Plane.so or GitHub fires a webhook, AgilePlus processes it immediately:
GitHub Webhook Setup
- Go to your repository → Settings → Webhooks → Add webhook
- Payload URL:
https://your-server:8080/webhooks/github - Content type:
application/json - Secret: generate with
openssl rand -hex 32 - Events: Issues, Pull requests, Issue comments
Configure in AgilePlus:
[sync.github.webhook]
enabled = true
secret = "${GITHUB_WEBHOOK_SECRET}"
port = 8080
path = "/webhooks/github"When a webhook arrives:
GitHub event: issue.labeled (label: "in-progress")
→ AgilePlus lookup: which WP maps to issue #42?
→ SyncMapping: WP02 of feature "user-auth"
→ Transition WP02 from Planned → Doing
→ Audit entry recorded: actor=sync:github
→ NATS event published: wp.state.changed
→ Dashboard SSE update sentPlane.so Webhook Setup
- Go to Plane workspace → Settings → Webhooks
- Add webhook URL:
https://your-server:8080/webhooks/plane - Select events: Issue updated, Issue state changed
[sync.plane.webhook]
enabled = true
workspace_secret = "${PLANE_WEBHOOK_SECRET}"
port = 8080
path = "/webhooks/plane"State Mapping Reference
The complete mapping between AgilePlus states and external tracker states:
AgilePlus Feature States → Plane.so
| AgilePlus State | Plane.so Issue State | Label Added |
|---|---|---|
| Created | Backlog | — |
| Specified | Backlog | spec |
| Researched | Todo | spec, researched |
| Planned | Todo | planned |
| Implementing | In Progress | in-progress |
| Validated | Done | validated |
| Shipped | Done | shipped |
| Cancelled | Cancelled | cancelled |
AgilePlus WP States → GitHub
| AgilePlus WP State | GitHub Issue Label | PR State |
|---|---|---|
| Planned | work-package, planned | — |
| Doing | work-package, in-progress | Draft PR |
| ForReview | work-package, in-review | PR ready for review |
| Done | work-package | PR merged, issue closed |
| Blocked | work-package, blocked | PR blocked label |
Customizing State Mappings
[sync.plane.state_map]
# AgilePlus state = Plane.so state name
"Created" = "Backlog"
"Specified" = "Todo"
"Planned" = "Todo"
"Implementing" = "In Progress"
"Validated" = "In Review" # Custom: use "In Review" instead of "Done"
"Shipped" = "Done"
[sync.github.label_map]
# AgilePlus state = GitHub label
"Doing" = "wip" # Custom: use "wip" instead of "in-progress"
"ForReview" = "needs-review"
"Blocked" = "blocked"Conflict Resolution Deep Dive
When both AgilePlus and an external tracker change state since the last sync, a conflict is detected:
Conflict detected for WP02 (feature: user-auth):
AgilePlus state: Doing (updated 5 min ago by agent:claude-code)
Plane.so state: Done (updated 2 min ago by human:alice)
Last synced: 15 min agoResolution strategies:
local-wins
[sync]
conflict_strategy = "local-wins"AgilePlus state wins. Plane.so is updated to match AgilePlus:
Resolution: AgilePlus "Doing" wins → update Plane.so to "In Progress"
Audit: conflict recorded, resolution actor = sync:local-winsremote-wins
[sync]
conflict_strategy = "remote-wins"External tracker wins. AgilePlus state is updated to match tracker. This triggers a governance check — if transitioning WP02 from Doing to Done without completing acceptance criteria, the transition is blocked:
Resolution: Plane.so "Done" wins → attempt WP02 Done transition
Governance check: acceptance criteria not met
BLOCKED: Cannot apply remote-wins transition (governance violation)
Action required: Human must resolve manuallymanual
[sync]
conflict_strategy = "manual"Conflicts are logged and require manual resolution:
# List unresolved conflicts
agileplus sync status --conflicts
# Output:
# Conflict 1: WP02 (user-auth) — AgilePlus=Doing, Plane.so=Done
# Conflict 2: WP05 (email-notif) — AgilePlus=ForReview, GitHub=Closed
# Resolve by choosing a winner
agileplus sync resolve WP02 --use local # Keep AgilePlus state
agileplus sync resolve WP05 --use remote # Use tracker stateNext Steps
- Configuration — Detailed config options
- Getting Started — Full setup walkthrough
- Core Workflow — Understand the pipeline
- Environment Variables — Plane.so and GitHub configuration
- MCP Tools — Programmatic sync control
- Feature Lifecycle — How sync fits in the feature flow