Local Webhook & Event-Driven Development Automation
This skill defines the architectural pattern and operational procedures for receiving GitHub webhook events locally in real-time, executing automated filesystem and branch cleanups upon PR merge, and ensuring cold-boot reconciliation after offline periods.
1. Overview & Architectural Design
Traditional local environment maintenance relies on manual cleanup commands or periodic polling. Polling incurs API latency and exhausts GitHub API rate limits.
This skill establishes a Push-Based Event Architecture:
flowchart LR
GH["GitHub Repository"] -- "Webhook Event: pull_request.closed" --> Rel["gh webhook forward (Tunnel Relay)"]
Rel -- "HTTP POST http://127.0.0.1:9876/events" --> Daemon["Local Python/Go Webhook Daemon"]
Daemon -- "Verify: No uncommitted edits" --> Safety{"Safety Guard"}
Safety -- "Clean" --> Clean["Remove Worktree & Delete Local Branch"]
Safety -- "Dirty" --> Alert["Preserve Files & Issue Notification"]2. Core Invariants & Safety Guarantees
git status --porcelain returns uncommitted files, even if the PR was merged remotely.3. Operational Runbook
A. Starting the Webhook Relay
The GitHub CLI provides built-in webhook forwarding without requiring public DNS or tunneling tools (like ngrok):
gh webhook forward \
--repo=<owner>/<repo> \
--events=pull_request \
--url=http://127.0.0.1:9876/eventsB. Daemon Event Handling Logic
When receiving a pull_request event:
# 1. Parse JSON payload
data = json.loads(payload)
action = data.get("action")
merged = data.get("pull_request", {}).get("merged", False)
branch = data.get("pull_request", {}).get("head", {}).get("ref")
# 2. Check for closed PR (merged or closed without merge)
if action == "closed":
# 3. Locate worktree for branch
# 4. Check git status (abort if dirty files exist)
# 5. Execute git worktree remove and git branch -DC. Systemd User Service Configuration
To run the event daemon automatically on user login:
# ~/.config/systemd/user/git-webhook-daemon.service
[Unit]
Description=Local Git Webhook Event Daemon
After=network.target
[Service]
Type=simple
ExecStart=/usr/bin/python3 %h/.local/bin/git-webhook-daemon.py
Restart=always
RestartSec=5
[Install]
WantedBy=default.targetEnable and start the service:
systemctl --user daemon-reload
systemctl --user enable --now git-webhook-daemon.service4. Cold-Boot Offline Catch-Up Algorithm
Whenever the daemon initializes:
# 1. Inspect all active worktrees
git worktree list --porcelain
# 2. Query GitHub for merged PR branches
gh pr list --state merged --json headRefName --jq '.[].headRefName'
# 3. Reconcile differences: If worktree branch is in merged list and clean, prune it
git worktree remove <worktree-path>
git branch -D <branch-name>