Free SKILL.md scraped from GitHub. Clone the repo or copy the file directly into your Claude Code skills directory.
npx versuz@latest install seb155-atlas-plugin-dist-atlas-dev-addon-skills-forgejo-prgit clone https://github.com/seb155/atlas-plugin.gitcp atlas-plugin/SKILL.MD ~/.claude/skills/seb155-atlas-plugin-dist-atlas-dev-addon-skills-forgejo-pr/SKILL.md---
name: forgejo-pr
description: "Forgejo PR lifecycle manager. This skill should be used when the user asks to 'create a PR', 'merge PR', 'promote dev to main', 'rebase PR', '/forgejo-pr', or needs Forgejo-specific PR orchestration."
mode: [engineering, ops]
triggers:
- "/atlas pr"
- "/atlas promote"
- "create PR"
- "merge PR"
- "promote dev to main"
- "merge to main"
effort: low
---
# Forgejo PR Lifecycle
Automate the full PR workflow: create branch → push → create PR → rebase if behind → merge → cleanup.
## Commands
```bash
/atlas pr promote dev main # Create + merge in one step
/atlas pr promote dev main --title "feat: my feature"
/atlas pr create "title" --head dev --base main
/atlas pr merge 150 # Merge existing PR by number
/atlas pr status # Show open PRs
```
## Process
### Step 1: Read Configuration
```bash
# Source Forgejo credentials
source ~/.env 2>/dev/null
FORGEJO_API="${FORGEJO_INTERNAL_URL:-http://192.168.10.75:3000/api/v1}"
TOKEN="${FORGEJO_TOKEN}"
# Detect repo org/name from git remote
REMOTE_URL=$(git remote get-url origin)
# Extract org/repo from SSH or HTTP URL
ORG=$(echo "$REMOTE_URL" | grep -oP '(?<=/)[^/]+(?=/[^/]+\.git)' | tail -1)
REPO=$(echo "$REMOTE_URL" | grep -oP '[^/]+(?=\.git$)')
```
### Step 2: Promote Flow (create + merge)
When user runs `/atlas pr promote {head} {base}`:
1. **Create temporary promote branch** from head:
```bash
git checkout {head}
git pull origin {head}
PROMOTE_BRANCH="promote/{head}-to-{base}-$(date +%Y-%m-%d)"
git checkout -b $PROMOTE_BRANCH
```
2. **Rebase on base** to ensure up-to-date:
```bash
git fetch origin {base}
git rebase origin/{base}
# If conflict → HITL gate: ask user to resolve
git push --force-with-lease origin $PROMOTE_BRANCH
```
3. **Create PR via API**:
```bash
PR_NUM=$(curl -s -X POST "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls" \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"title\":\"${TITLE}\",\"head\":\"${PROMOTE_BRANCH}\",\"base\":\"${BASE}\"}" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('number',''))")
```
4. **Merge PR**:
```bash
# Try merge, fallback to rebase
RESULT=$(curl -s -X POST "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls/${PR_NUM}/merge" \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"Do":"merge"}')
# If "behind base branch" → rebase on base + force push + retry
if echo "$RESULT" | grep -q "behind"; then
git fetch origin {base} && git rebase origin/{base}
git push --force-with-lease origin $PROMOTE_BRANCH
# Retry merge with rebase strategy
curl -s -X POST "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls/${PR_NUM}/merge" \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"Do":"rebase"}'
fi
```
5. **Cleanup**:
```bash
git checkout {head}
git branch -D $PROMOTE_BRANCH
git push origin --delete $PROMOTE_BRANCH
```
### Step 3: Merge Existing PR
When user runs `/atlas pr merge {number}`:
```bash
# Check PR status
PR=$(curl -s "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls/${NUMBER}" \
-H "Authorization: token ${TOKEN}")
STATE=$(echo "$PR" | python3 -c "import sys,json; print(json.load(sys.stdin).get('state',''))")
if [ "$STATE" != "open" ]; then
echo "PR #${NUMBER} is ${STATE}, not open"
exit 1
fi
# Merge
curl -s -X POST "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls/${NUMBER}/merge" \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"Do":"merge"}'
```
### Step 4: Status Check
When user runs `/atlas pr status`:
```bash
curl -s "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls?state=open&limit=10" \
-H "Authorization: token ${TOKEN}" | python3 -c "
import sys,json
prs=json.load(sys.stdin)
print(f'{len(prs)} open PRs:')
for p in prs:
print(f' #{p[\"number\"]:4d} {p[\"title\"][:60]} ({p[\"head\"][\"ref\"]}→{p[\"base\"][\"ref\"]})')
"
```
## Error Handling
| Error | Action |
|-------|--------|
| "PR already exists" | Find existing PR, merge it instead |
| "behind base branch" | Auto-rebase + force push + retry merge |
| Merge conflict | HITL gate: show conflicts, ask user to resolve |
| Token missing | Error: "Set FORGEJO_TOKEN in ~/.env" |
| API unreachable | Error: "Forgejo at {URL} not responding" |
## HITL Gates
- **Merge conflicts**: Always ask user before resolving
- **Force push**: Show what will be overwritten
- **Merge to main**: Confirm PR title and commit count
## Bulk operations (T4.4 — atlas-coord interop)
For programmatic bulk-creation of issues (e.g. when populating a fresh
plan's Section N tasks via `atlas-coord plan-to-issues`), use the Python
companion CLI:
```bash
# JSON spec file: array of {title, body, labels?}
cat > /tmp/specs.json <<'EOF'
[
{"title": "Bug 1", "body": "Repro steps...", "labels": ["bug"]},
{"title": "Bug 2", "body": "Other repro...", "labels": ["bug", "p1"]}
]
EOF
# Dry-run first (no API calls)
python3 lib/forgejo_bulk_create_issues.py \
--repo=axoiq/synapse --input=/tmp/specs.json --dry-run
# Real run (rate-limited to 10 req/sec by default)
source ~/.env # exports ATLAS_COORD_TOKEN
python3 lib/forgejo_bulk_create_issues.py \
--repo=axoiq/synapse --input=/tmp/specs.json
```
Options:
| Flag | Default | Description |
|------|--------:|-------------|
| `--repo OWNER/NAME` | `axoiq/synapse` | Forgejo repository |
| `--input PATH` | stdin | JSON spec file (use `-` to be explicit; omit to read stdin) |
| `--rate-limit-rps N` | `10` | Max requests per second (atlas-coord token quota) |
| `--token TOKEN` | `$ATLAS_COORD_TOKEN` | Forgejo API token |
| `--base-url URL` | `https://forgejo.axoiq.com` | Forgejo base URL |
| `--dry-run` | `false` | Print what WOULD be created |
| `--json` | `false` | JSON summary output (machine-readable) |
Spec validation (rejected client-side):
- `title` required, non-empty, ≤ 256 chars
- `body` required (use `""` for empty)
- `labels` optional, list of strings
Failure handling:
- HTTP 401 (auth) → propagates immediately, no further calls
- HTTP 5xx / network → retried by shared `lib.forgejo_api.ForgejoClient` (x2);
if still failing, the row is recorded as `failed` and the loop continues
- Per-row failures result in CLI exit code 2 + JSON `failed: N` in summary
Auth & rate limit reuse the shared `lib/forgejo_api.py` client (T4.2 / T4.3).
This is the same code path `atlas-coord plan-to-issues` uses internally — so
"bulk" and "plan-to-issues" share idempotency guarantees.
## Sub-plan PR Template (sp3 — atlas-batch auto-attach)
> Added: 2026-05-02 (sp3-atlas-batch-sota-2026). Used by all 7 sub-plan PRs in Wave B.
> Template file: `.forgejo/ISSUE_TEMPLATE/sub-plan-pr.yml` (cross-cutting, sp3-owned).
When `/atlas-batch` completes a sub-plan and creates a PR, pre-populate the body
from the `sub-plan-pr.yml` template. This ensures every sub-plan PR is traceable to
the master plan, carries a gate score, and lists verification commands run.
### Pre-fill template fields
Build the PR body before the API call:
```bash
source ~/.env
FORGEJO_API="${FORGEJO_INTERNAL_URL:-http://192.168.10.75:3000/api/v1}"
FORGEJO_URL="https://forgejo.axoiq.com"
# Values injected by /atlas-batch after agent completion
SP_ID="sp3"
SP_TITLE="atlas-batch SOTA 2026"
CURRENT_PHASE="Phase 2: Command + skill enhancements"
GATE_SCORE="12/15"
WAVE="B (parallel)"
MASTER_PLAN_PATH=".blueprint/plans/regarde-pour-rajouter-au-tender-lark.md"
VERIFY_COMMANDS="make test — PASS\nyq '.' .forgejo/ISSUE_TEMPLATE/sub-plan-pr.yml — PASS\njq -c . < evals/skills/atlas-batch/golden.jsonl — PASS"
PR_BODY=$(cat <<BODY
<!-- sub-plan-pr template v1.0 (sp3-atlas-batch-sota-2026) -->
**Master Plan**: [${MASTER_PLAN_PATH}](${FORGEJO_URL}/${ORG}/${REPO}/blob/main/${MASTER_PLAN_PATH})
**Sub-Plan ID**: ${SP_ID}
**Sub-Plan Title**: ${SP_TITLE}
**Current Phase**: ${CURRENT_PHASE}
**Gate Score**: ${GATE_SCORE}
**Wave**: ${WAVE}
### Verification Commands Run
\`\`\`
${VERIFY_COMMANDS}
\`\`\`
### Files Changed
$(git diff --name-only HEAD origin/dev 2>/dev/null || git diff --name-only HEAD~1 HEAD)
---
> ⚠️ DO NOT MERGE without coordinator review (LAW-CI-VERIFY-001).
> PR created by /atlas-batch. Human HITL required before merge.
BODY
)
```
### Create PR with pre-filled body
```bash
curl -s -X POST "${FORGEJO_API}/repos/${ORG}/${REPO}/pulls" \
-H "Authorization: token ${FORGEJO_TOKEN}" \
-H "Content-Type: application/json" \
-d "$(python3 -c "
import sys, json
body = open('/dev/stdin').read() if False else '''${PR_BODY}'''
print(json.dumps({
'title': 'feat(${SP_ID}): ${SP_TITLE}',
'head': 'feature/${SP_ID}-${BRANCH_SLUG}',
'base': 'dev',
'body': body
}))
")"
```
### Forgejo template YAML location
The full field spec lives at `.forgejo/ISSUE_TEMPLATE/sub-plan-pr.yml`.
That file defines 8 form fields (master_plan_link, sub_plan_id, sub_plan_title,
current_phase, gate_score, verification_commands_run, wave, hitl_confirmed).
The body constructed above mirrors those fields in plain Markdown for API compatibility
(Forgejo API `pulls` endpoint accepts free-form body, not YAML form data).
## Related
- `finishing-branch` — Uses this skill for the PR step
- `ci-management` — Check CI before merging
- `.claude/references/forgejo-api.md` — API documentation
- `skills/atlas-coord/plan-to-issues.md` — sibling sub-skill that also creates issues, but with idempotency keyed on the structured `<!-- atlas-coord:v1 -->` block (instead of a flat label)
- `commands/atlas-batch.md` — Calls this skill's Sub-plan PR Template section for each agent
- `.forgejo/ISSUE_TEMPLATE/sub-plan-pr.yml` — Cross-cutting template (sp3-owned)