EGM sync upstream
Bring changes from marcus-darden/egm into this port without losing the port's renames, Cursor adaptations or port-only files. Read the repo-root AGENTS.md first: it is the rule book this skill applies. This skill lives in .cursor/skills/ so it is project-local; install.sh never installs it.
Invoke as /egm-sync-upstream [--dry-run] [upstream-ref]. upstream-ref defaults to upstream main. With --dry-run, do Steps 1 to 4 and print the plan; change nothing.
Hard rules
- Never push to
main. All work happens onsync/upstream-<shortsha>; Juan merges the PR. - Never overwrite a port-only file (list in
AGENTS.md). If upstream changed something those files mirror (for example the upstream README's install section), describe it in the report instead. - Don't guess. New upstream skills or agents, deleted upstream files, renamed upstream files and merge conflicts are flagged for Juan with the exact paths, not resolved by inventing a name or picking a side.
- Keep attribution.
LICENSEkeeps both copyright lines;UPSTREAM_COMMITmoves only when the branch actually contains the upstream changes.
Helpers
<skill-dir> is the folder holding this file.
python3 <skill-dir>/scripts/port_names.py path <upstream-path>...maps upstream paths to port paths and says when a path is port-only, not ported, or new.python3 <skill-dir>/scripts/port_names.py merge <clone> <old> <new> <upstream-path> [--write]three-way merges one upstream file change into its port file (base and theirs are rewritten into port names first, ours is the port file), so Cursor adaptations in untouched lines survive. Exit 0 clean, 1 conflict markers left in, 3 nothing to do (port-only, not ported, or new).python3 <skill-dir>/scripts/port_names.py rewrite <file>/check <file>...rewrite names in text, or list leftover old slash names.bash <skill-dir>/scripts/check_port.shruns every port check (Step 7).
Workflow
EGM sync progress:
- [ ] Step 1: working tree clean, on an up-to-date main
- [ ] Step 2: upstream cloned to a temp dir; old = UPSTREAM_COMMIT, new = upstream HEAD
- [ ] Step 3: CHANGELOG entries since old read; changed files listed with status
- [ ] Step 4: every changed file mapped and classified (merge / flag / skip)
- [ ] Step 5: branch sync/upstream-<shortsha> created; merges applied; adaptations re-checked
- [ ] Step 6: UPSTREAM_COMMIT updated
- [ ] Step 7: check_port.sh passes
- [ ] Step 8: SYNC-REPORT.md written
- [ ] Step 9: branch pushed; PR opened with gh; Juan told
Step 1: Clean start
git status --porcelain must be empty and the current branch main, up to date with origin/main (git fetch origin && git status -sb). If not, stop and tell Juan what is dirty. Do not stash his work.
Step 2: Fetch upstream
tmp="$(mktemp -d)"
git clone --quiet https://github.com/marcus-darden/egm.git "$tmp/upstream"
old="$(cat UPSTREAM_COMMIT)"; new="$(git -C "$tmp/upstream" rev-parse ${REF:-HEAD})"
If old == new, report "upstream has no new commits since " and stop (no branch, no PR). If old is not an ancestor of new (git -C "$tmp/upstream" merge-base --is-ancestor $old $new), upstream rewrote history: stop and flag it.
Step 3: Read what changed
git -C "$tmp/upstream" log --oneline $old..$newgit -C "$tmp/upstream" diff --name-status -M $old $new- The new
CHANGELOG.mdentries (tags after the one containingold). The CHANGELOG explains why; quote the relevant rows in the report.
Step 4: Map and classify each file
Run port_names.py path on every changed upstream path, then put each in one bucket:
| Upstream status and mapping | Bucket |
|---|---|
| M, maps to an existing port file | merge |
A, maps into a known skill folder (for example a new asset in skills/goldfish/) | merge (creates the file) |
| A, new skill, agent or walkthrough ("NEW" note) | flag: Juan picks the egm- name and whether to port it |
| D (deleted upstream) | flag: do not delete the port file |
| R (renamed upstream) | flag: show old and new paths and the port path |
| port-only or not-ported note | skip (describe the change if it matters, such as README install text) |
LICENSE | merge, then confirm both copyright lines remain |
With --dry-run, print this table and the commit list, then stop.
Step 5: Apply on a branch
git switch -c "sync/upstream-${new:0:7}"
python3 <skill-dir>/scripts/port_names.py merge "$tmp/upstream" $old $new <upstream-path> --write
Run the merge once per merge file. On exit 1, leave the conflict markers out of the commit: restore the file (git checkout -- <port-path>), and record the conflict hunk in the report for Juan. After merging, re-read each touched file for the Cursor adaptations listed in AGENTS.md (Task-tool spawn text, readonly: true in agents, AGENTS.md alongside CLAUDE.md, reviewers ignoring AGENTS.md and .cursor/rules/, /review not /code-review, the Cursor row in runtimes.md). If upstream added a new place that needs one (for example a new CLAUDE.md lookup, a new tools: line in an agent, a new Claude-only spawn instruction), apply the same adaptation and list it under "adapted". Commit in logical groups with messages that cite upstream commits.
Step 6: Move the pointer
Write new to UPSTREAM_COMMIT only if every merge file merged cleanly or its conflict is recorded. Commit it last.
Step 7: Check
bash <skill-dir>/scripts/check_port.sh. It checks name equals folder, eval JSON, check_gate.py (BLOCKED then GO), leftover old slash names, and a test install to a temp dir (which must not contain this skill). Fix failures caused by the merge; anything else goes in the report.
Step 8: SYNC-REPORT.md
Write SYNC-REPORT.md at the repo root on the branch, and use the same text as the PR body:
# Upstream sync: <old7>..<new7>
Upstream commits: <list>
CHANGELOG: <quoted rows>
## Applied
- <port path> <- <upstream path>: <one line>
## Adapted for Cursor
- <port path>: <which adaptation and why>
## Skipped
- <upstream path>: <port-only | not ported>, <what changed, if it matters>
## Flagged for Juan
- <new skill/agent, deletion, rename, conflict>: <paths, the hunk, the question>
## Checks
<check_port.sh summary>
## Open questions
- <...>
Step 9: Push and open the PR
git push -u origin "sync/upstream-${new:0:7}"
gh pr create --base main --head "sync/upstream-${new:0:7}" --title "Sync upstream marcus-darden/egm ${old:0:7}..${new:0:7}" --body-file SYNC-REPORT.md
Tell Juan the PR URL, the counts (applied, adapted, skipped, flagged), and the flagged items. Do not merge the PR. Remove the temp clone.