Resolve Conflicts
Semi-automatic merge and rebase conflict resolution.
Compatibility
This skill mutates git state and may rewrite branch history. Expose it to Codex
only as an explicitly invoked skill.
Asking the User
Every question in this skill is written as
options. Use that tool where
the host offers it, or the host's nearest structured-choice equivalent. Where the host has
neither, ask the same question in normal chat as a numbered list of 2–5 options —
recommended first, one short line of description each — and wait for the user to reply
with a number.
Entry Point Detection
Determine mode from arguments:
| Input | Mode |
|---|
| PR number, PR URL, issue number, issue URL | PR mode |
| No arguments + active conflicts in working tree | Local mode |
| Invoked by another skill during rebase/merge | Conditional mode |
| No arguments + no active conflicts | Error: nothing to resolve |
PR Mode
Step 1: Fetch PR Info
Run
scripts/fetch-pr-info.sh $ARGUMENTS
from the skill directory.
Parse the key=value output. Handle exit codes:
| Exit | Meaning | Action |
|---|
| 0 | PR found with conflicts | Continue to Step 2 |
| 1 | Input parsing failed | Show error, stop |
| 2 | gh not authenticated | Show error, stop |
| 3 | PR/issue not found | Show error, stop |
| 4 | Issue has no linked PR | Show context, stop |
| 5 | PR has no conflicts | Show context, stop |
Always display context header (even when stopping):
PR: <title>
Branches: <base> ← <head>
Link: <url>
Step 2: Prepare Working Directory
- Check if working tree is dirty ()
- If dirty OR user included "worktree" in the invocation → create an isolated
worktree for using whatever mechanism the host provides
(a built-in worktree tool, or ). If the host cannot
continue in the new worktree automatically, show the resulting
command and stop so the user can resume there.
- If clean → work in current repo
Step 3: Fetch and Rebase
bash
git fetch origin <base>
git fetch origin <head>
before=$(git rev-parse "origin/<head>")
git checkout <head>
git reset --hard origin/<head>
git rebase origin/<base>
Keep
for Step 5. It is the remote tip this rebase started from, and the only
value a force-push may safely lease against.
If rebase produces conflicts → proceed to Resolution Pipeline.
If rebase completes cleanly → show "No conflicts after rebase" and stop.
Step 4: Resolution Pipeline
Run the Resolution Pipeline (see below). This may loop multiple times during rebase — each
can produce new conflicts.
Step 5: Completion
After all conflicts resolved and rebase complete:
- Run verification (see Verification section)
- Show final report
- Ask whether to force-push to remote, per Asking the User:
- (Recommended) — update the PR branch after the resolved rebase
- — keep the rebased branch local only
- Yes →
git push --force-with-lease="<head>:$before" origin <head>
- No → skip
A bare
git push --force-with-lease
leases against the remote-tracking ref, which any
refreshes. Resolution loops for as long as the conflicts take, so anything
fetching in between would turn the lease into a no-op and let the push discard a commit
someone else added to the PR branch.
, captured in Step 3, is the value the
rebase was actually built on. If the push is rejected, the remote moved: re-run from
Step 3 rather than escalating to
.
If unresolved conflicts remain:
- Show final report with remaining files
- Stop and wait for user input
Local Mode
- Detect active merge/rebase state:
- or → rebase in progress
- → merge in progress
- Neither → error "No active merge or rebase"
- Run Resolution Pipeline
- After resolution: or
- Loop if new conflicts appear
- Show final report when done
Conditional Mode
Same as Local Mode but:
- No AskUserQuestion prompts
- No push questions
- Return control to calling skill silently after resolution
Resolution Pipeline
Phase 1: Classify
cd "$(git rev-parse --show-toplevel)"
, then run scripts/classify-conflicts.sh
.
Its paths are relative to the repository root, so every resolution command below
has to run from there too. Exit 3 means a conflicted path contains a newline, which
a line-oriented report cannot represent — surface the message and stop; do not
resolve a partial list.
- Parse the output — extract counts and file lists per status code
- If there are UU files, classify them by difficulty:
- Read
references/conflict-analyzer-prompt.md
and dispatch a read-only
subagent with it as the prompt body plus the UU file list. It classifies
each file trivial / simple / complex and resolves nothing.
- If the host has no subagent facility, classify inline using the same prompt.
- Parse the classifier output to get the UU trivial/simple/complex subgroups
- Combine bash output (DU/UD/DD/AA/AU/UA) with classifier output
- Print the initial report (see
references/report-format.md
)
Phase 2: Auto-Resolve
Resolve groups that need no LLM analysis:
| Group | Command |
|---|
| DU | for each file |
| UD | for each file |
| DD | for each file |
| AA | git checkout --theirs "<file>" && git add "<file>"
— but if the classifier treated it as UU (both have meaningful content), treat as UU |
| AU | git checkout --theirs "<file>" && git add "<file>"
|
| UA | git checkout --theirs "<file>" && git add "<file>"
|
| UU trivial | git checkout --theirs "<file>" && git add "<file>"
|
prints each path verbatim — unquoted and unescaped, so a name
with a space or a non-ASCII character arrives intact. Two consequences: always quote it
when passing it back to git, because the report does not escape it for you; and run from
the repository root, because the paths are relative to it.
Batch all trivial UU files into one resolver subagent for parallel execution.
Phase 3: Context-Aware Resolve
Attempt to resolve all remaining UU files (simple and complex).
Effort thresholds:
- ≤3 complex files → spend significant effort on each, read surrounding code for context
- 4–10 complex files → attempt each, move on if stuck after reasonable effort
- 10+ complex files → attempt but don't over-invest; resolve what's feasible
Parallelization:
- Dispatch one resolver subagent per file for parallel resolution
- Cap at ~5 concurrent resolver subagents
- Resolver subagents: read the conflicted file → understand both sides → write
the resolved version →
- Resolver subagents must NOT run lint, build, typecheck, or any verification
commands
For each resolver subagent, do this:
- Read the file to find all conflict markers
- For each conflict block, understand what "ours" changed and what "theirs" changed
- Decide how to combine both changes (or pick one side if they're truly incompatible)
- Write the resolved file (no conflict markers remaining)
If a resolver subagent cannot resolve a file: Leave the conflict markers in
place. Do not
it. The file will appear in the final report as
unresolved.
Phase 4: Continue Loop
After all resolvable conflicts are handled:
- Check for remaining conflict markers:
git status --short | grep -E '^(UU|DU|UD|AU|UA|AA|DD) '
- If none remain:
- For rebase:
- For merge:
- If the continue produces new conflicts → go back to Phase 1
- If the continue succeeds → proceed to Verification
- If conflicts remain:
- Show final report with unresolved files
- In PR/Local mode: stop and wait for user
- In Conditional mode: return with status indicating unresolved conflicts
Verification
Run after all conflicts are resolved and rebase/merge is complete.
- — confirm clean working tree
- Check for project-specific lint/typecheck commands:
- Read or scripts for available commands
- Run fast checks only: , , , etc.
- Do NOT run slow builds or integration tests at this stage
- If issues found (broken imports, type errors from deleted files):
- Attempt to fix them
- Re-run the check to confirm
- Run build only at the very end if the project has a fast build (typical for JS projects)
- If issues cannot be fixed → report them alongside any unresolved conflicts