AI AGENT SKILLS

Fork Manager

一个面向 Other 场景的 Agent 技能。原始说明:Manage forks with open PRs - sync upstream, rebase branches, track PR status, and maintain production branches with pending contributions. Supports automatic...

SKILL.md

SKILL.md


name: fork-manager
description: Manage forks with open PRs - sync upstream, rebase branches, track PR status, and maintain production branches with pending contributions. Supports automatic conflict resolution via --auto-resolve flag (spawns AI subagents to resolve rebase conflicts). Use when syncing forks, rebasing PR branches, building production branches that combine all open PRs, reviewing closed/rejected PRs, or managing local patches kept outside upstream. Requires Git and GitHub CLI (gh).
metadata: {"openclaw": {"requires": {"bins": ["git", "gh"]}}}


Fork Manager Skill

Manage forks where you contribute PRs but also use improvements before they're merged upstream. Includes support for local patches — fixes kept in the production branch even when the upstream PR was closed/rejected.

When to use

  • Sync a fork with upstream
  • Check status of open PRs
  • Rebase PR branches onto latest upstream
  • Build a production branch combining all open PRs + local patches
  • Review recently closed/rejected PRs and decide whether to keep locally
  • Manage local patches (fixes not submitted or rejected upstream)

When NOT to use

  • General GitHub queries (issues, PRs, CI status on any repo) → use github skill instead
  • Triaging/ranking/prioritizing issues → use issue-prioritizer skill instead
  • Reviewing code changes before publishing a PR → use pr-review skill instead
  • Creating new PRs from scratch (not fork sync) → use gh pr create directly

Execution Model — Orchestrator + Worker

A skill NUNCA deve ser executada inline pelo agente principal. Sempre usar o padrão orchestrator/worker:

Fluxo

  1. Orchestrator (agente principal) — prepara o contexto e spawna um subagente:
   sessions_spawn(
     task: "<prompt completo com contexto do repo, config, último history>",
     model: "<model adequado>",
     mode: "run"
   )
  1. Worker (subagente) — executa o full-sync/status/rebase/etc. Lê a SKILL.md, segue o fluxo, escreve history.
  2. Monitoramento — o orchestrator checa progresso a cada 4 minutos via sessions_list / sessions_history:
  • Se o worker estiver ativo e progredindo → aguarda
  • Se o worker estiver parado/travado (sem output novo por 2 checks consecutivos) → subagents kill + spawna novo worker
  • Se o worker completou → lê o resultado e reporta ao usuário
  1. Fallback — se o worker falhar (crash, timeout, erro):
  • Orchestrator verifica o estado do repo (git status, último checkpoint)
  • Spawna novo worker com contexto atualizado incluindo o ponto onde parou
  • Máximo de 2 retries antes de reportar falha ao usuário

Contexto para o Worker

O orchestrator deve incluir no prompt do worker:

  • Path da SKILL.md (para o worker ler e seguir)
  • Config do repo (inline ou path)
  • Última entrada do history (resumo ou path)
  • Modo de execução (full-sync, status, rebase-all, etc.)
  • Se é cron mode ou manual
  • Quaisquer instruções específicas do usuário

Por que subagente?

  • Resiliência: se o worker falha, o orchestrator pode recuperar
  • Context window: a skill é pesada (145+ PRs = muito output). O worker gasta seu context sem poluir o agente principal
  • Paralelismo futuro: permite spawnar workers para repos diferentes simultaneamente

Cron Mode

When invoked by a cron job (automated recurring sync), follow these guidelines for efficient execution:

  1. Skip interactive prompts — auto-resolve decisions that don't require human input:
  • Rebases: attempt automatically, report failures
  • Closed PRs: report but defer decision (don't drop or keep without human input)
  • Audit findings: report but don't act
  1. Compact output — use the summary format, not full verbose report:
   🍴 Fork Sync Complete — <repo>
   Main: synced N commits (old_sha → new_sha)
   PRs: X open, Y changed state
   - Rebased: A/B clean (C conflicts)
   Production: rebuilt clean | N conflicts
   Notable upstream: [1-3 bullet highlights]
  1. Checkpoint on failure — if a rebase fails or production build has conflicts, write state to repos/<name>/checkpoint.json so the next run (or manual invocation) can resume
  2. Time budget — target <10 minutes total. If rebasing 20+ PRs, batch push at the end instead of per-branch

Configuration

Configs are organized per repository in repos/<repo-name>/config.json relative to the skill directory:

fork-manager/
├── SKILL.md
├── scripts/
│   └── update-config.mjs
└── repos/
    ├── project-a/
    │   └── config.json
    └── project-b/
        └── config.json

Formato do config.json:

{
  "repo": "owner/repo",
  "fork": "your-user/repo",
  "localPath": "/path/to/local/clone",
  "mainBranch": "main",
  "productionBranch": "main-with-all-prs",
  "upstreamRemote": "upstream",
  "forkRemote": "origin",
  "autoResolveConflicts": false,
  "openPRs": [123, 456],
  "prBranches": {
    "123": "fix/issue-123",
    "456": "feat/feature-456"
  },
  "localPatches": {
    "local/my-custom-fix": {
      "description": "Breve descrição do que o patch faz",
      "originalPR": 789,
      "closedReason": "rejected|superseded|duplicate|wontfix",
      "keepReason": "Motivo pelo qual mantemos localmente",
      "addedAt": "2026-02-07T00:00:00Z",
      "reviewDate": "2026-03-07T00:00:00Z"
    }
  },
  "lastSync": "2026-01-28T12:00:00Z",
  "notes": {
    "mergedUpstream": {},
    "closedWithoutMerge": {},
    "droppedPatches": {}
  }
}

PR tracking source of truth

openPRs and prBranches are a local cache, not the source of truth.
The source of truth for open PRs is GitHub.

Before any command that depends on the PR set (status, rebase-all,
build-production, full-sync, audit-open), run:

node <skillDir>/scripts/update-config.mjs --config <skillDir>/repos/<repo-name>/config.json

Use --check to verify without writing and --dry-run to print the updated
JSON. Do not build production from a stale openPRs list. A config that is
missing an open PR by the configured fork owner is incomplete and must be
updated before rebasing or rebuilding production.

Resolução automática de conflitos (autoResolveConflicts)

A resolução automática pode ser ativada de duas formas (qualquer uma basta):

  1. Flag de invocação (ad-hoc, por execução):
   /fork-manager --auto-resolve
   /fork-manager full-sync --auto-resolve
  1. Config persistente (sempre ativo pra aquele repo):
   { "autoResolveConflicts": true }

| Fonte | Comportamento |
|-------|---------------|
| Nenhuma (default) | Conflitos são reportados mas não resolvidos. Relatório inclui "⚠️ Conflitos requerem aval do desenvolvedor." |
| --auto-resolve OU config.autoResolveConflicts: true | Spawna subagentes Opus para resolver conflitos. Resultados classificados como trivial/semântico/irresolvível. |

Precedência: --auto-resolve na invocação ativa a resolução mesmo se o config diz false. Não existe --no-auto-resolve — se o config diz true e o usuário não quer resolver, basta não rodar o passo manualmente.

Para usuários do ClawHub: basta passar --auto-resolve no comando. Nenhuma configuração de repo necessária.

Campos de localPatches

Cada entry em localPatches é uma branch local mantida na production branch mas sem PR aberto no upstream.

| Campo | Descrição |
|-------|-----------|
| description | O que o patch faz |
| originalPR | Número do PR original que foi fechado (opcional se criado direto como patch) |
| closedReason | Por que o PR foi fechado: rejected (mantenedor recusou), superseded (outro PR resolve parcialmente mas não totalmente), duplicate (fechamos nós mesmos), wontfix (upstream não vai resolver) |
| keepReason | Por que precisamos manter localmente |
| addedAt | Data em que foi convertido para local patch |
| reviewDate | Data para reavaliar se ainda é necessário (upstream pode ter resolvido) |

Histórico de Execuções

Cada repositório gerenciado tem um arquivo history.md que registra todas as execuções da skill como um livro de registro append-only:

fork-manager/
└── repos/
    ├── project-a/
    │   ├── config.json
    │   └── history.md
    └── project-b/
        ├── config.json
        └── history.md

Regra: Ler último output antes de começar

Antes de qualquer operação, ler o history.md do repositório alvo e extrair a última entrada (último bloco ---). Isso dá contexto sobre:

  • O que foi feito na última execução
  • Quais PRs tinham problemas
  • Quais decisões foram tomadas
  • Se ficou alguma ação pendente
# Ler última entrada do history (tudo após o último "---")
tail -n +$(grep -n '^---$' "$SKILL_DIR/repos/<repo-name>/history.md" | tail -1 | cut -d: -f1) "$SKILL_DIR/repos/<repo-name>/history.md"

Se o arquivo não existir, criar com o header e prosseguir normalmente.

Regra: Registrar output ao finalizar

Ao final de toda execução, fazer append ao history.md com o resultado completo. Formato:

---
## YYYY-MM-DD HH:MM UTC | <comando>
**Operator:** <claude-code | openclaw-agent | manual>

### Summary
- Main: <status do sync>
- PRs: <X open, Y merged, Z closed, W reopened>
- Local Patches: <N total, M com review vencida>
- Production: <rebuilt OK | not rebuilt | build failed>

### Actions Taken
- <lista de ações executadas, ex: "Synced main (was 12 commits behind)">
- <"Rebased 21/21 branches clean">
- <"PR #999 closed → kept as local patch local/my-fix">
- <"PR #777 reopened → restored to openPRs (was in droppedPatches)">

### Pending
- <ações que ficaram pendentes, ex: "PR #456 has conflicts — needs manual resolution">
- <"3 local patches with expired reviewDate — run review-patches">

### Full Report
<o relatório completo que seria mostrado ao usuário, colado aqui na íntegra>

Importante: O bloco Full Report contém o relatório completo sem abreviação. Isso garante que o próximo agente que ler o history tenha toda a informação, não apenas o resumo.

Fluxo de Análise

1. Carregar config e histórico

Resolve the skill directory (where SKILL.md lives):

# SKILL_DIR is the directory containing this SKILL.md
# Resolve it relative to the agent's workspace or skill install path
SKILL_DIR="<path-to-fork-manager-skill>"

# Load config for the target repo
cat "$SKILL_DIR/repos/<repo-name>/config.json"

# Ler último output do history para contexto
HISTORY="$SKILL_DIR/repos/<repo-name>/history.md"
if [ -f "$HISTORY" ]; then
  # Extrair última entrada (após último ---)
  LAST_SEP=$(grep -n '^---$' "$HISTORY" | tail -1 | cut -d: -f1)
  if [ -n "$LAST_SEP" ]; then
    tail -n +"$LAST_SEP" "$HISTORY"
  fi
fi

2. Navegar para o repositório

cd <localPath>

3. Fetch de ambos remotes

git fetch <upstreamRemote>
git fetch <originRemote>

4. Analisar estado do main

# Commits que upstream tem e origin/main não tem
git log --oneline <originRemote>/<mainBranch>..<upstreamRemote>/<mainBranch>

# Contar commits atrás
git rev-list --count <originRemote>/<mainBranch>..<upstreamRemote>/<mainBranch>

5. Verificar PRs abertos via GitHub CLI

# Listar PRs abertos do usuário
gh pr list --state open --author @me --json number,title,headRefName,state

# Verificar status de um PR específico
gh pr view <number> --json state,mergedAt,closedAt,title

6. Classificar cada PR

Para cada PR no config, verificar:

| Estado | Condição | Ação |
| ------------ | --------------------------------- | --------------------------------------- |
| open | PR aberto no GitHub | Manter, verificar se precisa rebase |
| merged | PR foi mergeado | Remover do config, deletar branch local |
| closed | PR fechado sem merge | Acionar review-closed (ver abaixo) |
| conflict | Branch tem conflitos com upstream | Precisa rebase manual |
| outdated | Branch está atrás do upstream | Precisa rebase |

Comando para verificar se branch precisa rebase:

git log --oneline <upstreamRemote>/<mainBranch>..<originRemote>/<branch> | wc -l  # commits à frente
git log --oneline <originRemote>/<branch>..<upstreamRemote>/<mainBranch> | wc -l  # commits atrás

7. Revisar PRs recém-fechados (review-closed)

Quando um PR é detectado como fechado sem merge, NÃO remover automaticamente. Iniciar um fluxo de revisão interativo:

7.1. Coletar contexto do fechamento

# Buscar comentários e motivo do fechamento
gh pr view <number> --repo <repo> --json title,closedAt,state,comments,labels

# Verificar se upstream resolveu o problema de outra forma
# (procurar PRs mergeados recentes que toquem os mesmos arquivos)
gh pr list --state merged --repo <repo> --json number,title,mergedAt --limit 30

7.2. Classificar o motivo do fechamento

| Categoria | Descrição | Ação padrão |
|-----------|-----------|-------------|
| resolved_upstream | Upstream corrigiu o problema por outro caminho | drop — não precisamos mais |
| supersededbyours | Fechamos nós mesmos em favor de outro PR nosso | drop — o substituto já está em openPRs |
| rejected_approach | Mantenedor não gostou da abordagem, mas o bug/feature existe | review — considerar resubmeter com abordagem diferente |
| rejected_need | Mantenedor não concorda que é um problema | review — avaliar se precisamos localmente |
| wontfix | Upstream marcou como wontfix | review — provável candidato a local patch |

7.3. Apresentar ao usuário para decisão

Para cada PR fechado, apresentar:

### PR #<number> — <title>
- **Fechado em:** <data>
- **Motivo:** <categoria>
- **Comentários do mantenedor:** <resumo>
- **O fix ainda é relevante pra nós?** Análise: <o que o patch faz e se upstream resolve>

**Opções:**
1. 🗑️ **Drop** — remover completamente (branch local + remote)
2. 📌 **Keep as local patch** — mover para `localPatches`, manter na production branch
3. 🔄 **Resubmit** — retrabalhar e abrir novo PR com abordagem diferente
4. ⏸️ **Defer** — manter no limbo por agora, revisitar depois

7.4. Executar a decisão

Drop:

git branch -D <branch> 2>/dev/null
git push <originRemote> --delete <branch> 2>/dev/null
# Mover para notes.droppedPatches no config

Keep as local patch:

# Branch continua existindo, mas sai de openPRs/prBranches
# Entra em localPatches com metadata completa
# Renomear branch de fix/xxx para local/xxx (opcional, para clareza)

Resubmit:

# Manter branch, criar novo PR com descrição atualizada
gh pr create --title "<novo titulo>" --body "<nova descrição com contexto>"
# Atualizar config com novo número de PR

Defer:

# Mover para uma seção notes.deferred no config
# Será apresentado novamente no próximo full-sync

8. Auditar PRs abertos (audit-open)

Análise proativa dos PRs ainda abertos para detectar redundâncias e obsolescência. Deve rodar no full-sync depois do update-config.

8.1. Resolved upstream

Verificar se o upstream já resolveu o problema que nosso PR corrige, sem mergear nosso PR:

# Para cada PR aberto, buscar os arquivos que ele toca
gh pr view <number> --repo <repo> --json files --jq '[.files[].path]'

# Verificar se upstream alterou esses mesmos arquivos recentemente
# (commits no upstream/main que não estão no nosso PR branch)
git log --oneline upstream/main --since="<lastSync>" -- <files>

# Se houve mudanças upstream nos mesmos arquivos, verificar se o diff
# do nosso PR ainda faz diferença (pode ter sido absorvido)
git diff upstream/main..origin/<branch> -- <files>

Se o diff do PR estiver vazio (upstream absorveu as mudanças): marcar como resolved_upstream.
Se o diff for parcial (upstream resolveu parte): marcar como partially_resolved para revisão.

8.2. Duplicate externo

Verificar se outra pessoa abriu um PR que resolve o mesmo problema:

# Buscar PRs abertos no upstream que tocam os mesmos arquivos
gh pr list --state open --repo <repo> --json number,title,headRefName,files --limit 50

# Buscar PRs mergeados recentes que tocam os mesmos arquivos
gh pr list --state merged --repo <repo> --json number,title,mergedAt,files --limit 30 \
  | jq '[.[] | select(.mergedAt >= "<lastSync>")]'

Para cada PR encontrado que toca os mesmos arquivos, comparar:

  • Mesmo issue referenciado?
  • Mesma área de código?
  • Mesmo tipo de fix?

Se houver match forte: marcar como duplicate_external ou superseded_external.

8.3. Self-duplicate

Detectar sobreposição entre nossos próprios PRs abertos:

# Coletar files de todos os nossos PRs abertos
for pr in <openPRs>; do
  gh pr view $pr --repo <repo> --json number,files --jq '{number, files: [.files[].path]}'
done

# Cruzar: se dois PRs tocam os mesmos arquivos, são candidatos a duplicata

Para cada par com overlap de arquivos:

  • Verificar se o diff é similar ou complementar
  • Se similar: recomendar fechar o mais antigo/menos limpo
  • Se complementar: ok, apenas nota informativa

8.4. Apresentar resultados

### Audit de PRs Abertos

#### Possivelmente resolvidos upstream
| # | Titulo | Arquivos em comum | Status |
|---|--------|-------------------|--------|
| 123 | fix(foo): bar | foo.ts (changed upstream 3 days ago) | ⚠️ Verificar |

#### Possíveis duplicatas externas
| Nosso PR | PR externo | Overlap | Recomendação |
|----------|-----------|---------|--------------|
| #123 | #456 (@user) | foo.ts, bar.ts | ⚠️ Mesmo issue, verificar |

#### Self-duplicates (nossos PRs que se sobrepõem)
| PR A | PR B | Arquivos em comum | Recomendação |
|------|------|-------------------|--------------|
| #6471 | #8386 | skills/refresh.ts | 🗑️ Fechar #6471 (duplicata) |

**Opções por PR flagged:**
1. 🗑️ **Close** — fechar o PR no upstream e drop
2. ✅ **Keep** — falso positivo, manter aberto
3. 🔄 **Merge into** — combinar com outro PR
4. ⏸️ **Defer** — revisitar depois

Comandos do Agente

status - Verificar estado atual

  1. Carregar config
  2. Fetch remotes
  3. Contar commits atrás do upstream
  4. Listar PRs e seus estados
  5. Reportar ao usuário

sync - Sincronizar main com upstream

cd <localPath>
git fetch <upstreamRemote>
git checkout <mainBranch>
git merge <upstreamRemote>/<mainBranch>
git push <originRemote> <mainBranch>

rebase <branch> - Rebase de uma branch específica

git checkout <branch>
git fetch <upstreamRemote>
git rebase <upstreamRemote>/<mainBranch>
# Se conflito: resolver e git rebase --continue
git push <originRemote> <branch> --force-with-lease

rebase-all - Rebase de todas as branches de PR

Para cada branch em prBranches:

  1. Checkout da branch
  2. Rebase no upstream/main
  3. Push com --force-with-lease
  4. Reportar sucesso/falha

resolve-conflicts - Resolução automática de conflitos via subagentes

Requer --auto-resolve na invocação OU autoResolveConflicts: true no config do repo. Se nenhum dos dois, este comando não é executado e conflitos são apenas reportados com a nota "⚠️ Conflitos requerem aval do desenvolvedor."

Após rebase-all detectar conflitos, o orchestrator (agente principal) spawna subagentes individuais para tentar resolver cada conflito automaticamente.

Fluxo

  1. O worker do rebase-all retorna a lista de branches com conflito
  2. O orchestrator agrupa os conflitos e spawna até 5 subagentes simultâneos (model: Opus)
  3. Conforme subagentes terminam, novos são lançados até esgotar a fila
  4. Cada subagente tem timeout de 10 minutos
  5. Resultados são coletados e integrados no relatório final

Prompt do subagente resolver

Cada subagente recebe:

Resolve o conflito de rebase da branch <branch> (PR #<number>) no repo <localPath>.

## Contexto
- Upstream: <upstreamRemote>/<mainBranch>
- Branch do PR: <originRemote>/<branch>
- Arquivos em conflito: <lista de arquivos do erro de rebase>

## Passos
1. cd <localPath>
2. git checkout -B <branch> <originRemote>/<branch> --no-track
3. git rebase <upstreamRemote>/<mainBranch>
   → O rebase vai parar com conflito
4. Para cada arquivo em conflito:
   a. Ler o arquivo com os marcadores de conflito (<<<<<<<, =======, >>>>>>>)
   b. Entender o que o upstream mudou (OURS) vs o que o PR mudou (THEIRS)
   c. Resolver preservando a intenção de ambos
   d. git add <arquivo>
5. git rebase --continue
6. Se houver mais conflitos em commits subsequentes, repetir 4-5
7. git push <originRemote> <branch> --force-with-lease

## Regras de resolução
- **Arquivo deletado no upstream + modificado pelo PR:** aceitar a deleção do upstream (nosso PR targeted código que não existe mais). git rm <arquivo> && git rebase --continue
- **Import/formatting conflicts:** mesclar ambos, preservar imports de ambos os lados
- **Lógica alterada em ambos os lados:** preservar a mudança do upstream E encaixar o fix do PR. Se o fix do PR não faz mais sentido com o novo código upstream, reportar como UNRESOLVABLE.
- **NUNCA alterar a lógica do PR** — apenas adaptar ao novo contexto do upstream

## Output
Responda com EXATAMENTE um destes formatos:

RESOLVED|<branch>|trivial|<resumo de 1 linha>
RESOLVED|<branch>|semantic|<resumo de 1 linha>
UNRESOLVABLE|<branch>|<motivo de 1 linha>

Classificação do resultado

| Resultado | Significado | Ação |
|-----------|-------------|------|
| RESOLVED\|trivial | Conflito mecânico resolvido (imports, formatting, deleted files) | ✅ Push feito, sem necessidade de revisão |
| RESOLVED\|semantic | Conflito envolvendo lógica de negócio resolvido | ⚠️ Push feito, marcar no report para revisão humana |
| UNRESOLVABLE | Subagente não conseguiu resolver sem risco | ❌ Não faz push, escala no report |

Após resolução

Para branches resolvidas com sucesso:

  1. Verificar que o push foi feito (git log --oneline <originRemote>/<branch> -1)
  2. Tentar rebase novamente para confirmar que está clean
  3. Atualizar notes.conflictBranches no config: remover entries resolvidas

Para branches não resolvidas:

  1. Manter em notes.conflictBranches com ciclo incrementado
  2. Incluir no relatório com o motivo do subagente

Integração com build-production

Após resolve-conflicts, o build-production roda normalmente. Branches que foram resolvidas no rebase agora devem mergear clean na production. Se ainda houver conflito de production merge (diferente do conflito de rebase), o build-production trata como sempre: abort e reportar.

update-config - Atualizar config com PRs atuais

node <skillDir>/scripts/update-config.mjs --config <skillDir>/repos/<repo-name>/config.json

The script queries GitHub with gh pr list --state open --author <fork owner>
(or --author @me when no fork is configured), treats GitHub as canonical for
open PRs, and rewrites openPRs + prBranches as a cache of the current open
set.

It also:

  • adds new open PRs automatically
  • updates branch names when a PR head branch changes
  • restores reopened PRs from notes.closedWithoutMerge / notes.droppedPatches
  • promotes reopened local patches back into openPRs
  • removes no-longer-open PRs from openPRs
  • records merged PRs in notes.mergedUpstream
  • records closed-without-merge PRs in notes.closedWithoutMerge with reviewStatus: "pending"

Useful modes:

node <skillDir>/scripts/update-config.mjs --config <config.json> --check
node <skillDir>/scripts/update-config.mjs --config <config.json> --dry-run

Detecção de PRs reabertos

Ao comparar a lista do GitHub (gh pr list --state open) com o config local, detectar três cenários:

| Cenário | Condição | Ação |
|---------|----------|------|
| PR novo | No GitHub mas não em openPRs, localPatches, nem notes | Adicionar a openPRs + prBranches normalmente |
| PR reaberto (dropped) | No GitHub como open, encontrado em notes.closedWithoutMerge ou notes.droppedPatches | Restaurar: mover de volta para openPRs + prBranches, remover da seção notes. Fetch da branch: git fetch <originRemote> <branch>. Logar no relatório como "🔄 Reopened" |
| PR reaberto (local patch) | No GitHub como open, encontrado em localPatches (via campo originalPR) | Promover: mover de localPatches para openPRs + prBranches. Logar no relatório como "🔄 Reopened (was local patch)" |

Implementação:

# Para cada PR open no GitHub que NÃO está em openPRs:
# 1. Checar se o número está em notes.closedWithoutMerge ou notes.droppedPatches
#    → Se sim: PR foi reaberto. Restaurar automaticamente.
# 2. Checar se algum entry em localPatches tem originalPR == número
#    → Se sim: PR foi reaberto. Promover de volta a openPRs.
# 3. Se não encontrado em lugar nenhum: PR genuinamente novo.

# Restaurar branch se foi deletada:
git fetch <originRemote> <branch> 2>/dev/null || git fetch <originRemote> pull/<number>/head:<branch>

Nota: A restauração é automática (sem interação) porque o mantenedor reabrir um PR é sinal claro de que ele deve voltar ao tracking. O relatório sempre lista os PRs restaurados para visibilidade.

build-production - Criar branch de produção com todos os PRs + local patches

Before rebuilding, run update-config and report any difference between the
GitHub open-PR set and the cached config. Continue only after the config includes
all currently open PRs by the authenticated contributor.

cd <localPath>
git fetch <upstreamRemote>
git fetch <originRemote>

# ⚠️ SEMPRE preservar arquivos não-commitados antes de trocar de branch
if [ -n "$(git status --porcelain)" ]; then
  git stash push --include-untracked -m "fork-manager: pre-build-production $(date -u +%Y%m%dT%H%M%S)"
  STASHED=1
fi

# Deletar branch antiga se existir
git branch -D <productionBranch> 2>/dev/null || true

# Criar nova branch a partir do upstream
git checkout -b <productionBranch> <upstreamRemote>/<mainBranch>

# 1. Mergear cada PR branch (contribuições upstream pendentes)
for branch in <prBranches>; do
  git merge <originRemote>/$branch -m "Merge PR #<number>: <title>"
  # Se conflito, resolver
done

# 2. Mergear cada local patch (fixes mantidos localmente)
for branch in <localPatches>; do
  git merge <originRemote>/$branch -m "Merge local patch: <description>"
  # Se conflito, resolver
done

# Push
git push <originRemote> <productionBranch> --force-with-lease

# Restaurar arquivos não-commitados
if [ "$STASHED" = "1" ]; then
  git stash pop
fi

After rebuilding the production branch, remind the user to run their project's build command if needed.

Ordem de merge: PRs abertos primeiro (ordem crescente por número), local patches depois. Isso garante que patches locais se aplicam sobre a base mais completa possível.

audit-open - Auditar PRs abertos por redundância/obsolescência

Análise proativa de todos os PRs abertos (seção 8 acima):

  1. Para cada PR aberto, coletar arquivos tocados
  2. Resolved upstream: verificar se upstream alterou os mesmos arquivos desde último sync; se diff do PR ficou vazio, flaggar
  3. Duplicate externo: buscar PRs upstream (open + recently merged) que tocam mesmos arquivos
  4. Self-duplicate: cruzar arquivos entre nossos próprios PRs abertos
  5. Apresentar findings ao usuário com opções: close / keep / merge-into / defer
  6. Executar decisões
  7. Atualizar config

review-closed - Revisar PRs recém-fechados

Detecta PRs que foram fechados/mergeados desde o último sync e guia o usuário na decisão:

  1. Buscar todos os PRs do config no GitHub
  2. Identificar os que mudaram de estado (merged ou closed)
  3. Para merged: mover para notes.mergedUpstream, deletar branches
  4. Para closed sem merge: iniciar fluxo de revisão interativo (seção 7 acima)
  5. Para cada closed, apresentar contexto e opções ao usuário
  6. Executar decisão: drop / keep as local patch / resubmit / defer
  7. Atualizar config

review-patches - Reavaliar patches locais existentes

Para cada entry em localPatches cuja reviewDate já passou:

  1. Verificar se upstream resolveu o problema desde a última revisão
  2. Verificar se o patch ainda aplica limpo (sem conflitos)
  3. Apresentar ao usuário com opções: manter / drop / resubmit / estender reviewDate
  4. Atualizar config

full-sync - Sincronização completa

  1. Stash - git stash --include-untracked se houver arquivos não-commitados
  2. sync - Atualizar main
  • Before sync: record OLD_SHA=$(git rev-parse upstream/main)
  • After sync: record NEW_SHA=$(git rev-parse upstream/main)
  1. post-sync hooks (optional, repo-specific) - Run custom post-sync actions
  • Skip if OLD_SHA == NEW_SHA (no upstream changes)
  • Hooks are defined per-repo in config.json under "postSyncHooks" (array of shell commands or descriptions)
  • Example: detect CHANGELOG changes, update downstream skills, trigger CI
  • If no hooks configured: skip this step entirely
  1. update-config - Atualizar lista de PRs a partir do GitHub (obrigatório; openPRs é cache)
  2. review-closed - Revisar PRs recém-fechados/mergeados (interativo)
  3. audit-open - Auditar PRs abertos por redundância/obsolescência (interativo)
  4. review-patches - Reavaliar local patches com reviewDate vencida (interativo)
  5. rebase-all - Rebase de todas as branches (PRs + local patches)
  6. resolve-conflicts (only if --auto-resolve flag OR autoResolveConflicts: true in config) - Resolver conflitos de rebase automaticamente via subagentes (Opus, até 5 paralelos, 10min timeout cada). Se nenhum dos dois, pular este passo.
  7. build-production - Recriar branch de produção (PRs + local patches)
  8. Pop stash - git stash pop para restaurar arquivos locais
  9. Remind user to run their project's build command if needed

Nota sobre ordem: update-config roda antes de review-closed porque é ali que PRs reabertos são detectados e restaurados automaticamente. Depois, review-closed processa PRs que foram genuinamente fechados. Por fim, audit-open roda por último, já com a lista de PRs abertos atualizada (incluindo os reabertos).

Relatório para o Usuário

Após qualquer operação, gerar relatório:

## 🍴 Fork Status: <repo>

### Upstream Sync

- **Main branch:** X commits behind upstream
- **Last sync:** <date>

### Open PRs (Y total)

| #   | Branch        | Status           | Action Needed     |
| --- | ------------- | ---------------- | ----------------- |
| 123 | fix/issue-123 | ✅ Up to date    | None              |
| 456 | feat/feature  | ⚠️ Needs rebase  | Run rebase        |
| 789 | fix/bug       | ❌ Has conflicts | Manual resolution |

#### 🔧 Resolução Automática de Conflitos

_Seção presente apenas quando resolução automática está ativa (`--auto-resolve` ou `autoResolveConflicts: true`). Caso contrário, substituir por:_
> ⚠️ **Conflitos requerem aval do desenvolvedor.** Resolução automática não ativada. Use `--auto-resolve` para tentar resolver automaticamente.

_Quando ativa: Conflitos semânticos (⚠️) foram resolvidos automaticamente mas **requerem revisão humana** — o subagente pode ter interpretado mal a intenção do código._

| #   | Branch        | Tipo      | Resultado          | Detalhe                          |
| --- | ------------- | --------- | ------------------ | -------------------------------- |
| 123 | fix/issue-123 | trivial   | ✅ Resolvido        | Removed deleted test file        |
| 456 | fix/issue-456 | semântico | ⚠️ Resolvido (revisar) | Adapted runner call to new API   |
| 789 | fix/issue-789 | —         | ❌ Não resolvido    | Lógica incompatível com upstream |

#### 🔴 Conflitos persistentes (3+ ciclos)

_Seção presente apenas quando há conflitos que persistem por 3 ou mais execuções consecutivas da skill, mesmo após tentativa de resolução automática. Esses PRs merecem atenção prioritária: considerar dropar, recriar sobre o main atual, ou resolver manualmente._

| #   | Branch        | Ciclos | Arquivo(s)        | Recomendação        |
| --- | ------------- | ------ | ----------------- | ------------------- |
| 789 | fix/bug       | 5      | agent-runner.ts   | 🗑️ Dropar ou recriar |

### Local Patches (Z total)

| Branch             | Original PR | Motivo          | Review em  |
| ------------------ | ----------- | --------------- | ---------- |
| local/my-fix       | #321        | rejected_need   | 2026-03-07 |
| local/custom-tweak | —           | wontfix         | 2026-04-01 |

### Audit de PRs Abertos

| #   | Título           | Flag                | Detalhe                          |
| --- | ---------------- | ------------------- | -------------------------------- |
| 123 | fix(foo): bar    | ⚠️ resolved_upstream | upstream changed foo.ts 3d ago   |
| 456 | fix(baz): qux    | ⚠️ duplicate_external | similar to #789 by @user         |
| 111 | fix(a): b        | ⚠️ self_duplicate    | overlaps with our #222           |

### PRs Reabertos (restaurados automaticamente)

| #   | Título           | Origem              | Ação                    |
| --- | ---------------- | ------------------- | ----------------------- |
| 777 | fix(foo): bar    | notes.droppedPatches | 🔄 Restored to openPRs |
| 888 | feat(baz): qux   | localPatches         | 🔄 Promoted to openPRs |

_Seção presente apenas quando há PRs reabertos no ciclo atual._

### PRs Recém-Fechados (aguardando decisão)

| #   | Título           | Fechado em | Motivo              | Recomendação     |
| --- | ---------------- | ---------- | ------------------- | ---------------- |
| 999 | fix(foo): bar    | 2026-02-05 | resolved_upstream   | 🗑️ Drop          |
| 888 | feat(baz): qux   | 2026-02-06 | rejected_need       | 📌 Local patch   |

### Production Branch

- **Branch:** main-with-all-prs
- **Contains:** PRs #123, #456 + Local patches: local/my-fix, local/custom-tweak
- **Status:** ✅ Up to date / ⚠️ Needs rebuild

### Recommended Actions

1. ...
2. ...

Notas Importantes

  • Sempre usar --force-with-lease em vez de --force para push
  • Sempre fazer backup antes de operações destrutivas
  • Use the project's package manager for build commands (bun/npm/yarn/pnpm)
  • Manter o config atualizado após cada operação
  • Resolução automática de conflitos (requer --auto-resolve ou autoResolveConflicts: true): Quando ativa, após rebase-all a skill spawna subagentes (Opus, até 5 paralelos, 10min timeout cada) para tentar resolver conflitos. Conflitos triviais (imports, formatting, arquivos deletados) são resolvidos e pushed sem necessidade de revisão. Conflitos semânticos (lógica de negócio) são resolvidos e pushed mas marcados no relatório como ⚠️ para revisão humana. Se o subagente não conseguir resolver, marca como ❌ e não faz push. Quando desativada, conflitos são apenas reportados.
  • Conflitos persistentes (3+ ciclos): Quando um conflito persiste por 3+ execuções consecutivas (tracked pelo sufixo "Nth cycle" em notes.conflictBranches), escalar no relatório com seção dedicada "🔴 Conflitos persistentes" e recomendar ação: dropar o PR, recriar a branch, ou resolver manualmente. O ciclo é incrementado a cada execução onde o conflito reaparece, mesmo que a resolução automática tenha sido tentada e falhado.
  • Local patches são cidadãos de primeira classe: rebase, build e relatório incluem tanto PRs abertos quanto local patches
  • Nunca remover automaticamente um PR fechado sem merge. Sempre passar pelo fluxo review-closed para decisão do usuário
  • Review dates em local patches: ao criar um local patch, definir uma data de revisão (default: 30 dias). No full-sync, patches com review vencida são apresentados ao usuário para reavaliação
  • Naming convention para local patches: prefixo local/ para distinguir de branches de PR (ex: local/my-custom-fix). A branch original pode ser renomeada ou mantida — o importante é que o config rastreie a branch correta

⚠️ Proteger arquivos não-commitados antes de operações destrutivas

Antes de qualquer operação que troca de branch ou deleta/recria branches (especialmente build-production e full-sync), sempre verificar e preservar arquivos unstaged, untracked e staged:

cd <localPath>

# 1. Checar se há arquivos em risco
git status --porcelain

# 2. Se houver arquivos modificados/untracked, fazer stash com untracked
git stash push --include-untracked -m "fork-manager: pre-sync stash $(date -u +%Y%m%dT%H%M%S)"

# 3. Executar a operação (rebase, checkout, merge, etc.)
# ...

# 4. Após concluir, restaurar o stash
git stash pop

Por quê? Ao deletar e recriar a branch de produção (git branch -D <productionBranch>), arquivos que existiam apenas no working directory (não commitados) são perdidos permanentemente. Isso inclui:

  • Arquivos gerados (dashboards, history, state)
  • Arquivos de configuração local (serve.ts, .env)
  • Dados acumulados (JSON, SQLite)

Regra: Se git status --porcelain retornar qualquer saída, fazer git stash --include-untracked antes de prosseguir. Restaurar com git stash pop ao final.

Security Notice

This skill performs operations that require broad filesystem and network access by design:

  • Git operations: fetch, checkout, merge, rebase, push across multiple remotes and branches
  • GitHub CLI: reads PR status, creates PRs, queries repo metadata

Before using this skill on a repository:

  • All git push operations use --force-with-lease (not --force) to prevent data loss
  • The skill always stashes uncommitted files before destructive branch operations

These capabilities are inherent to fork management and cannot be removed without breaking core functionality.

Usage Examples

Basic sync

User: "sync my fork of project-x"

Agent:

  1. Load config from $SKILL_DIR/repos/project-x/config.json
  2. Run status to assess current state
  3. If main is behind, run sync
  4. If PRs need rebase, run rebase-all
  5. Update productionBranch if needed
  6. Remind user to rebuild if needed
  7. Report results to user

Sync with auto-resolve

User: "/fork-manager --auto-resolve" or "/fork-manager full-sync --auto-resolve"

Agent:

  1. Same as basic sync, but after rebase-all:
  2. For each conflict, spawn a resolver subagent (Opus)
  3. Collect results (trivial ✅ / semantic ⚠️ / unresolvable ❌)
  4. Re-run build-production with resolved branches
  5. Report includes resolution table with review flags