From fork-sync
Sync Minion fork with upstream and update all development branches
How this skill is triggered — by the user, by Claude, or both
Slash command
/fork-sync:fork-syncThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Comprehensive skill for managing the Minion fork workflow, syncing with upstream, and updating all development branches.
Comprehensive skill for managing the Minion fork workflow, syncing with upstream, and updating all development branches.
IMPORTANT: The mirror branch must ALWAYS be a clean mirror of upstream/main. It should NEVER contain custom commits.
When mirror has diverged from upstream (contains custom commits):
git reset --hard upstream/maingit push --force-with-lease origin mirror# Check if mirror has diverged
git fetch upstream
git log --oneline upstream/main..mirror
# If output shows commits (mirror has diverged):
# 1. Verify commits are on DEV/main
git log --oneline DEV | head -20
git log --oneline main | head -20
# 2. Reset mirror to upstream
git checkout mirror
git reset --hard upstream/main
git push --force-with-lease origin mirror
This fork maintains a simplified branch hierarchy:
upstream/main (source of truth)
↓
mirror (clean mirror - NO custom commits)
↓
DEV (integration branch: mirror + all custom work)
↓
main (production - deployed to servers, Docker tag: prd)
Goal: Ensure mirror is a clean mirror of upstream
# Check if mirror has diverged
git checkout mirror
git fetch upstream
git log --oneline upstream/main..mirror
# If commits shown, mirror has diverged - fix it:
# 1. Verify custom commits exist on DEV/main
git log --oneline DEV | grep "CustomCommit"
git log --oneline main | grep "CustomCommit"
# 2. Reset mirror to upstream
git reset --hard upstream/main
git push --force-with-lease origin mirror
When to run: Any time mirror has custom commits (detected by divergence check)
Goal: Update local mirror to match upstream/main
# Fetch latest from upstream
git fetch upstream
# Switch to mirror (should already be clean)
git checkout mirror
# Fast-forward merge upstream changes (should succeed now)
git merge --ff-only upstream/main
# Push to fork
git push origin mirror
Expected: Fast-forward merge, no conflicts (since mirror is clean mirror)
Goal: Analyze upstream changes before merging to understand impact and plan conflict resolution
# Compare mirror (upstream) with main to see what changed
git diff main..mirror --stat | head -50
git log --oneline main..mirror --no-merges | head -30
# Generate per-file change analysis
git diff main..mirror --name-status | sort
Evaluation Process:
Categorize changes by type:
For each significant change, evaluate:
Identify high-risk files:
Plan merge strategy:
Document evaluation: Create a mental (or written) map of:
Expected time: 5-10 minutes for review
Output: Clear understanding of what's changing and plan for Phase 2 merge
When to use: Gap exceeds ~50 commits, or a bulk merge produced too many conflicts to resolve in one session.
Goal: Build a module-by-module merge shopping list — a conflict resolution playbook that guides Phase 2.
Core principle: Full merge (git merge mirror) remains the integration strategy. The evaluation produces a plan for how to resolve each conflict, not which commits to cherry-pick.
State file: ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/state/evaluation.json
Reference: See evaluation-reference.md for detailed heuristics, schema, and module presentation format.
On invocation, check for existing state:
state/evaluation.json exists and snapshot.mirrorHead matches current mirror HEAD → resume from cursor.currentModuleIndexSnapshot current branch state and build the commit inventory.
# Record snapshot
MERGE_BASE=$(git merge-base mirror DEV)
MIRROR_HEAD=$(git rev-parse mirror)
DEV_HEAD=$(git rev-parse DEV)
UPSTREAM_COUNT=$(git rev-list --count $MERGE_BASE..mirror)
FORK_COUNT=$(git rev-list --count $MERGE_BASE..DEV)
# Build full commit list grouped by module (src/ subdirectory)
git log --oneline --name-only $MERGE_BASE..mirror
Identify which files have actual fork functionality (not just rebrand renames). This determines conflict risk per module.
# Files changed in fork
git diff --name-only $MERGE_BASE..DEV
# For each: classify as rebrand-only, config-only, or functional
git diff $MERGE_BASE..DEV -- <file>
Record functional changes in forkFeatureIndex with fork commit SHAs, feature description, and recommended resolution.
Apply heuristic tiers (see evaluation-reference.md Section B) to tag each commit:
Expected: ~87% of commits auto-categorized, ~13% need manual review.
Present each module to the user in priority order (see evaluation-reference.md Section C). For each module show:
forkFeatureIndex)User decides per module: Accept all | Review individually | Defer | Skip module
Save state after each module decision. This step is resumable — the user can stop and resume across sessions.
After all modules are evaluated, compile the merge shopping list:
@nikolasp98/minion depExport to UPSTREAM_MERGE_EVALUATION.md at project root. This document becomes the playbook for Phase 2.
Expected time: 15–30 minutes for first run (initialization + high-priority modules), 5–10 minutes per resumed session
Trigger: Run immediately after Phase 1.6 Step 5 (shopping list finalized). Always run before Phase 2.
Goal: Convert the evaluation shopping list into a deterministic bash script that resolves all mechanical merge conflicts in seconds.
Output: ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh
Generation instructions:
Read evaluation.json's mergeShoppingList. Produce a script with this structure — emit one resolve call per file in each category:
#!/bin/bash
# Auto-generated by fork-sync Phase 1.7
# Generated: <ISO-8601>
# Mirror head: <git rev-parse mirror>
# Counts: <keepOurs N> keepOurs, <manualMerge N> manualMerge,
# <acceptWithRebrand N> acceptWithRebrand, <extensionPackageJson N> extpkg,
# <acceptDeletion N> deletions, <autoAccept N> autoAccept
# DO NOT EDIT — regenerate via Phase 1.7
set -e
# NOTE: Requires GNU sed (Linux default). On macOS: brew install gnu-sed && alias sed=gsed
REBRAND='s/from '"'"'openclaw'"'"'/from '"'"'@nikolasp98\/minion'"'"'/g; s/from "openclaw"/from "@nikolasp98\/minion"/g; s/require('"'"'openclaw'"'"')/require('"'"'@nikolasp98\/minion'"'"')/g; s/require("openclaw")/require("@nikolasp98\/minion")/g'
EXT_PKG='s/"openclaw": "workspace:\*"/"@nikolasp98\/minion": "workspace:*"/g'
resolve() {
local f="$1" strategy="$2"
# Idempotent: skip if not currently conflicted
git ls-files -u -- "$f" | grep -q . || return 0
case "$strategy" in
ours) git checkout --ours "$f" 2>/dev/null && git add "$f" && echo "✓ ours: $f" ;;
theirs) git checkout --theirs "$f" 2>/dev/null && git add "$f" && echo "✓ theirs: $f" ;;
rebrand) git checkout --theirs "$f" 2>/dev/null && sed -i "$REBRAND" "$f" && git add "$f" && echo "✓ rebrand: $f" ;;
extpkg) git checkout --theirs "$f" 2>/dev/null && sed -i "$EXT_PKG" "$f" && git add "$f" && echo "✓ extpkg: $f" ;;
rm) git rm -f "$f" 2>/dev/null && echo "✓ removed: $f" || true ;;
*) echo "ERROR: unknown strategy '$strategy' for '$f'" >&2; return 1 ;;
esac
}
# === keepOurs (<N> files) ===
resolve "Dockerfile" ours
# ... one line per file in keepOurs
# === acceptWithRebrand (<N> files) ===
resolve "src/agents/minion-tools.ts" rebrand
# ... one line per file in acceptWithRebrand
# === extensionPackageJson (<N> files) ===
resolve "extensions/bluebubbles/package.json" extpkg
# ... one line per file in extensionPackageJson
# === acceptDeletion (<N> files) ===
resolve ".agents/AGENT_SUBMISSION_CONTROL_POLICY.md" rm
# ... one line per file in acceptDeletion
# === autoAccept (<N> files) ===
resolve "docs/channels/telegram.md" theirs
# ... one line per file in autoAccept
# === MANUAL MERGE REQUIRED (<N> files) ===
# NOT resolved by this script — Claude handles these in Phase 2 Step 5.
# <file>: <notes from mergeShoppingList>
echo ""
echo "✓ Auto-resolution complete."
echo "MANUAL: Resolve these files, then: git add . && git merge --continue"
# list manualMerge files here as echo lines
Category → strategy mapping:
| Shopping list category | Script strategy |
|---|---|
keepOurs | ours |
acceptWithRebrand | rebrand |
extensionPackageJson | extpkg |
acceptDeletion | rm |
autoAccept | theirs |
manualMerge | comment only |
Save and commit:
chmod +x ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh
git add ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh
git commit -m "feat(fork-sync): Phase 1.7 — generate conflict resolution script"
If the script already exists: overwrite it — the shopping list may have been updated by a delta update.
Goal: Merge updated mirror into DEV using an isolated git worktree — immune to parallel git sessions in the main workspace.
Prerequisite: Phase 1.7 resolution script exists at ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh. Run Phase 1.7 first if it doesn't.
# Step 1: Verify resolution script is present
ls ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh
# Step 2: Create isolated worktree pointing at DEV
git worktree add ../openclaw-merge-worktree DEV
# Step 3: Merge mirror into worktree (--no-commit to allow pre-commit resolution)
git -C ../openclaw-merge-worktree merge mirror --no-commit --no-ff
# Step 4: Run resolution script from within the worktree directory
# (script uses relative git commands, must run from worktree root)
cd ../openclaw-merge-worktree
bash ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh
# Step 5: Manually resolve manualMerge files listed by the script
# Inspect each, resolve hunks per the script's comment notes
# Common files: package.json, .github/workflows/ci.yml, .github/workflows/docker-release.yml
# After resolving each file:
git -C ../openclaw-merge-worktree add package.json .github/workflows/ci.yml .github/workflows/docker-release.yml
# Step 6: Commit the merge
git -C ../openclaw-merge-worktree commit -m "Merge upstream changes from mirror"
# Step 7: Push DEV
git -C ../openclaw-merge-worktree push origin DEV
# Step 8: Cleanup
git worktree remove ../openclaw-merge-worktree
Why worktrees prevent interruption: The main workspace can be on any branch. Commits, checkouts, and stash pops in the main workspace have zero effect on the worktree. The active merge state lives in ../openclaw-merge-worktree/.git/MERGE_HEAD — a completely separate directory.
Recovery if interrupted (session ends, error, parallel git activity):
# Full reset in ~30 seconds
git worktree remove --force ../openclaw-merge-worktree
git worktree add ../openclaw-merge-worktree DEV
git -C ../openclaw-merge-worktree merge mirror --no-commit --no-ff
cd ../openclaw-merge-worktree
bash ${CLAUDE_PLUGIN_ROOT}/skills/fork-sync/scripts/resolve-conflicts.sh
# ... then Step 5 (manualMerge), Step 6 (commit), Step 7 (push), Step 8 (cleanup)
Expected: Resolution script resolves ~925 of 928 conflicts in under 60 seconds. Claude manually handles the 3 manualMerge files only.
Note: Feature branches and main (production) are not synced automatically. Update them manually when needed.
Goal: Add backward-compat aliases for any new upstream OpenClaw* exports that the fork references by Minion* names (or vice versa).
Why this is needed: Upstream uses OpenClaw* naming (e.g., createOpenClawTools, OpenClawConfig). The fork uses Minion* naming. When upstream adds new exports, they use OpenClaw* names. Fork test files and code may reference the Minion* equivalent, which doesn't exist until we add an alias.
When to run: After every Phase 2 merge commit, before pushing DEV.
# Step 1: Run TypeScript type check to find broken imports
npx tsc --noEmit 2>&1 | grep "^src/" | head -40
# Step 2: Categorize errors
# - "has no exported member 'createMinionX'" → add alias in source file
# - "Cannot find name 'OpenClawX'" → change to MinionX in test, or add import
# - "Did you mean 'DEFAULT_MINION_X'?" → add alias in constants file
# - "Property 'X' does not exist on type" → add to type definition (fork feature)
# Step 3: For each missing Minion* alias, add to the source file:
# export const createMinionX = createOpenClawX;
# export type MinionX = OpenClawX;
# Step 4: For each missing OpenClaw* alias (upstream files importing fork names):
# Create re-export shim files: src/module/openclaw-name.ts → re-exports minion-name.ts
# Step 5: For test files using bare OpenClawConfig without import:
# Change to MinionConfig (which IS imported)
# Step 6: Verify
npx tsc --noEmit 2>&1 | grep "^src/" | wc -l # Should be 0
npx oxlint --type-aware # Should be 0 errors
Common alias patterns:
| Source file | Alias to add |
|---|---|
src/agents/minion-tools.ts | export const createMinionTools = createOpenClawTools |
src/agents/pi-tools.ts | export const createMinionCodingTools = createOpenClawCodingTools |
src/browser/constants.ts | export const DEFAULT_OPENCLAW_X = DEFAULT_MINION_X |
src/browser/chrome.ts | export const resolveMinionUserDataDir = resolveOpenClawUserDataDir |
src/config/types.minion.ts | export type OpenClawConfig = MinionConfig |
src/plugins/types.ts | export type OpenClawPluginX = MinionPluginX |
Re-export shim files (for modules renamed during rebrand):
| Shim file | Re-exports |
|---|---|
src/infra/openclaw-root.ts | src/infra/minion-root.ts |
src/infra/tmp-openclaw-dir.ts | src/infra/tmp-minion-dir.ts |
src/agents/openclaw-tools.ts | src/agents/minion-tools.ts |
src/config/types.openclaw.ts | src/config/types.minion.ts |
Commit separately from the merge commit:
git commit -m "fix: post-merge compatibility — add OpenClaw/Minion backward-compat aliases"
Before starting the sync workflow:
git status)git stash if needed) — avoids partial-staging complexity during syncgit remote -v | grep upstream)git fetch upstream)After completing sync:
# Verify mirror matches upstream
git log --oneline mirror..upstream/main # Should be empty
# Verify branch relationships
git log --oneline --graph --all --decorate -20
# Verify DEV contains mirror's commits
git merge-base --is-ancestor mirror DEV && echo "✓ DEV contains mirror" || echo "✗ DEV missing mirror commits"
mirror matches upstream/main (no divergence)mirror pushed to origin/mirrorDEV merged mirror successfullyDEV pushed to remoteNote: Feature branches and main (production) are not synced automatically. Update them manually when needed.
Problem: git merge --ff-only upstream/main fails with "Not possible to fast-forward"
Root Cause: Mirror contains custom commits (violates clean mirror principle)
Solution:
git log --oneline DEV | headgit reset --hard upstream/maingit push --force-with-lease origin mirrorIf conflicts occur when merging mirror into DEV:
Identify conflicts: git status shows conflicted files
Common conflict areas:
Evaluate upstream vs fork for each conflict:
Do NOT default to keeping fork changes. For each conflicted hunk, ask:
Rule of thumb: Upstream is the source of truth for architecture. Fork changes should layer on top of upstream, not override it. When in doubt, adopt upstream and re-apply fork additions.
Complete merge:
git add <resolved-files>
git commit -m "Merge upstream changes, resolve conflicts"
git push origin DEV
If issues occur during merge:
# Abort current merge
git merge --abort
# Reset to previous state
git reflog # Find previous commit
git reset --hard HEAD@{1}
# Or restore from remote
git reset --hard origin/<branch-name>
When upstream has new commits and you want to pull them in:
Note: Feature branches and main (production) are not auto-synced. Update manually when needed.
When mirror accidentally contains custom commits:
When upstream has a critical fix you need immediately:
git cherry-pick <commit-sha>git status showing cleangit log mirror..upstream/main before mergingCause: Mirror has custom commits (diverged from upstream)
Fix: Run Phase 0 (Clean Mirror) first
Cause: Uncommitted changes in working directory
Fix:
git status # Review changes
git stash # Temporarily save changes
# Run sync workflow
git stash pop # Restore changes after sync
Cause: Remote branch has commits not in local branch
Fix:
git pull --rebase origin <branch-name>
# Resolve conflicts if any
git push origin <branch-name>
┌──────────────────────────────────────────────────────────────────┐
│ Fork Sync Quick Reference │
├──────────────────────────────────────────────────────────────────┤
│ 0. Clean mirror (if needed) → git checkout mirror │
│ git reset --hard upstream/main │
│ git push --force-with-lease │
├──────────────────────────────────────────────────────────────────┤
│ 1. Sync mirror → git checkout mirror │
│ git merge --ff-only upstream/main │
│ git push origin mirror │
├──────────────────────────────────────────────────────────────────┤
│ 1.5. Pre-merge evaluation → git diff main..mirror --stat │
│ Review changes per-file │
│ Plan conflict resolution strategy │
├──────────────────────────────────────────────────────────────────┤
│ 1.6. Systematic evaluation → "evaluate upstream" (resumable) │
│ (large gaps only) Module-by-module categorization │
│ Builds merge shopping list │
│ Delta-updates when mirror advances│
│ See evaluation-reference.md │
├──────────────────────────────────────────────────────────────────┤
│ 1.7. Generate script → Read evaluation.json │
│ (after 1.6 completes) Write resolve-conflicts.sh │
│ chmod +x && commit │
├──────────────────────────────────────────────────────────────────┤
│ 2. Update DEV (worktree) → git worktree add ../merge-wt DEV │
│ git -C ../merge-wt merge mirror │
│ bash scripts/resolve-conflicts.sh│
│ Resolve manualMerge files (N) │
│ git -C ../merge-wt commit + push │
│ git worktree remove ../merge-wt │
├──────────────────────────────────────────────────────────────────┤
│ 2.5. Alias fixup → npx tsc --noEmit | grep ^src/ │
│ (after merge commit) Add Minion↔OpenClaw aliases │
│ Create re-export shim files │
│ Fix test imports │
│ Commit separately from merge │
├──────────────────────────────────────────────────────────────────┤
│ Verify: → git log mirror..upstream/main │
│ (should be empty) │
├──────────────────────────────────────────────────────────────────┤
│ Note: Feature branches and main (production) are manual │
└──────────────────────────────────────────────────────────────────┘
Invoke this skill when:
Skill Version: 5.1.0 Last Updated: 2026-02-18 Maintained By: Nikolas P. (NikolasP98)
npx claudepluginhub nikolasp98/minion_plugins --plugin fork-syncManages Git forked repositories: sets up origin/upstream remotes, detects divergence with rev-list/log, syncs branches, and advises sync strategies/contribution prep.
Syncs beads forks with upstream steveyegge/beads repo, updates bd binary, and cleans merged PR branches in crew clone directories. Triggers on sync upstream or branch cleanup phrases.
Syncs Gastown forks with upstream, updates gt binary, cleans merged PR branches, and performs fork hygiene in crew clones using git and GitHub CLI.