Update From Upstream Main
remotion-dev/remotion
Update the current Remotion branch with the latest canonical main branch.
Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
$ npx skills add aeonfun/aeon --skill aeon-update -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install aeonfun/aeon aeon-update --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .claude/skills && cp -r skills-src/skills/aeon-update .claude/skills/aeon-update && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "aeon-update" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/aeon-update into .claude/skills/aeon-update/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aeon-update", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/aeonfun/aeon/tree/main/skills/aeon-updateType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add aeonfun/aeon --skill aeon-update -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install aeonfun/aeon aeon-update --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .agents/skills && cp -r skills-src/skills/aeon-update .agents/skills/aeon-update && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "aeon-update" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/aeon-update into .agents/skills/aeon-update/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aeon-update", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aeonfun/aeon --skill aeon-update -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install aeonfun/aeon aeon-update --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/skills/aeon-update .cursor/skills/aeon-update && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "aeon-update" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/aeon-update into .cursor/skills/aeon-update/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aeon-update", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/aeonfun/aeon.git --path skills/aeon-update--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add aeonfun/aeon --skill aeon-update -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install aeonfun/aeon aeon-update --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/skills/aeon-update .gemini/skills/aeon-update && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "aeon-update" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/aeon-update into .gemini/skills/aeon-update/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aeon-update", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install aeonfun/aeon aeon-updateInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add aeonfun/aeon --skill aeon-update -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .github/skills && cp -r skills-src/skills/aeon-update .github/skills/aeon-update && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "aeon-update" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/aeon-update into .github/skills/aeon-update/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aeon-update", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add aeonfun/aeon --skill aeon-update -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install aeonfun/aeon aeon-update --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/aeonfun/aeon.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/skills/aeon-update .opencode/skills/aeon-update && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "aeon-update" agent skill from https://github.com/aeonfun/aeon/tree/main/skills/aeon-update into .opencode/skills/aeon-update/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "aeon-update", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
aeon-updatePull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
Aeon Update is an agent skill from aeonfun/aeon. Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
Its SKILL.md is about 7.4k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.
The repository describes itself as: The most autonomous AI agent framework: runs unattended on GitHub Actions, self-healing skills, drives Claude Code, Grok, Codex & more. No approval loops. Configure once, forget… The licence is MIT.
Read from SKILL.md and the folder at commit f252074. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitghjqnodeFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git and gh, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
GITHUB_TOKENFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Aeon Update loads about 7.4k tokens when it runs. Until then it costs about 40 tokens; SKILL.md has 2,459 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check noted patterns worth knowing about, such as sudo or a known installer.
output/** .mcp.json .env* aeon.dbAutomated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from aeonfun/aeon at commit f252074, republished under its MIT licence (© aeonfun). 2,459 words, ~7,437 tokens.
.claude/skills/aeon-update/SKILL.md (or your agent's skills folder).${var} — mode selector; space-separated tokens, order-independent, all optional:
- mode (
sync|report, defaultsync) —syncopens a PR with the framework changes;reportcomputes the delta and notifies, mutating nothing (dry run).repo=owner/name— override the upstream source repo (else auto-resolved from this instance'sparent, falling back toaeonfun/aeon).reset=<sha|fork-point>— force the stored baseline to<sha>(or the merge-base with upstream) before running. Recovery / backfill lever.Empty ⇒ sync from the auto-resolved upstream. Examples: `` ·
report·repo=aeonfun/aeon·reset=fork-point.
Today is ${today}. This is the fleet's downstream updater - the counterpart to fork-fleet. fork-fleet looks outward from the parent to find work in the forks worth pulling up; this skill runs inside an instance and pulls the parent's shipped framework changes down - new skills, script/harness fixes, workflow and doc updates - and lands them as a reviewable PR. It is how an instance stays current with aeonfun/aeon without a hand-run rsync-overlay rebase.
main. Every framework change ships as one reviewable PR. The operator merges.aeon.yml, STRATEGY.md, soul/, memory/, output/, .mcp.json and the git-derived catalogs are instance-owned. Upstream changes to them are surfaced for manual review in the PR body, never written into the tree.git merge-file 3-way, S6); it is only listed as a conflict for a human when the same lines changed on both sides. This is what lets a hand-narrowed workflow keep receiving unrelated upstream fixes without a manual merge every run.mkdir -p memory/topics
[ -f memory/topics/aeon-update-state.json ] || echo '{"baseline_sha":null,"upstream":null,"last_run":null,"last_pr":null,"pending_conflicts":[]}' > memory/topics/aeon-update-state.jsonRead memory/MEMORY.md for context and scan the last ~3 days of memory/logs/ - drop anything already reported so a repeat run isn't re-sent. Read the state file:
BASELINE = .baseline_sha (the upstream commit this instance was last synced to).PENDING = .pending_conflicts (files surfaced as conflicts in a prior run, not yet resolved).${var}MODE = report if the token report (or dry) is present, else sync.REPO_OVERRIDE = value of a repo=owner/name token, if any.RESET = value of a reset= token, if any (fork-point or a 7-40 char SHA).SELF=$(gh repo view --json nameWithOwner -q .nameWithOwner)
UPSTREAM="${REPO_OVERRIDE:-$(gh api "repos/${SELF}" --jq '.parent.full_name // empty')}"
[ -z "$UPSTREAM" ] && UPSTREAM="aeonfun/aeon"If UPSTREAM == SELF, this instance is canon - there is nothing upstream to pull. Write status AEON_UPDATE_IS_UPSTREAM to memory/logs/${today}.md, send no notification, and stop.
UP_DEFAULT=$(gh api "repos/${UPSTREAM}" --jq '.default_branch')
HEAD_SHA=$(gh api "repos/${UPSTREAM}/commits/${UP_DEFAULT}" --jq '.sha')reset=fork-point → BASELINE=$(gh api "repos/${UPSTREAM}/compare/${HEAD_SHA}...$(git rev-parse HEAD)" --jq '.merge_base_commit.sha'). reset=<sha> → BASELINE=<sha>. Persist immediately to state, then continue.BASELINE null and no reset): a fresh instance already carries all of canon from fork time, so there is no delta to apply - just anchor the watermark. Set baseline_sha = HEAD_SHA, write state, log AEON_UPDATE_BASELINE_SET, send a one-line notify (baseline initialized at <head7>; future runs sync from here), and stop. (To backfill everything since the fork point instead, re-run with reset=fork-point.)BASELINE == HEAD_SHA: in sync. Re-verify PENDING (S8) in case a prior conflict is now resolved, update state, log AEON_UPDATE_IN_SYNC, notify nothing, stop.gh api "repos/${UPSTREAM}/compare/${BASELINE}...${HEAD_SHA}" --jq '{
ahead: .ahead_by, behind: .behind_by, status,
commits: [.commits[]? | {sha: .sha[0:7], msg: (.commit.message | split("\n")[0]), date: .commit.author.date}],
files: [.files[]? | {filename, status, previous_filename, additions, deletions}]
}' > /tmp/aeon-update-compare.jsonError handling:
status: "diverged" with no merge base (baseline not an ancestor of HEAD - history rewrite or unrelated repo): stop with AEON_UPDATE_BASELINE_UNREACHABLE; notify the operator to re-run with reset=fork-point or reset=<sha>..files looks truncated, note files_truncated=true in the report - the operator can run again after merging to pick up the remainder.Classify every entry in .files by path. A file is OPERATOR-owned (surfaced, never auto-written) if its path matches any of:
aeon.yml STRATEGY.md soul/** memory/**
output/** .mcp.json .env* aeon.db
skills.lock eyebrowlock.json .claude/** (except .claude/skills/aeon/**)
catalog/*.json (except catalog/skill-packs.json)
apps/dashboard/outputs/**Everything else is OWNED (a candidate for auto-apply): skills/**, scripts/**, bin/**, harness-adapter/**, .github/**, apps/** (except apps/dashboard/outputs/**), CLAUDE.md, AGENTS.md, docs/**, .github/README.md, LICENSE, CHANGELOG.md, .gitignore, eyebrow.policy.json, catalog/skill-packs.json, and tracked root helpers (aeon, ...).
catalog/*.json and eyebrowlock.json are OPERATOR-owned here only so they are never blindly copied - they are regenerated from the synced sources in S7, which is the correct way to reconcile them.
catalog/skill-packs.json is the exception: it is OWNED and synced, not regenerated. skills.json, packs.json and skill-icons.json each have a bin/generate-* script; the community pack registry has none. It is hand-maintained as a pair with the Listed packs table in docs/community-skill-packs.md, and scripts/validate-skill-packs.mjs (ci-skill-packs) hard-fails when the two disagree. Filing it under "regenerate" meant it was never synced at all, so an instance's copy froze while the synced doc table moved on, and CI went red once the validator started enforcing parity (aeon-agent#232). Treat these paths as one pack-registry unit:
catalog/skill-packs.json docs/community-skill-packs.md scripts/validate-skill-packs.mjs
scripts/tests/test_validate_skill_packs.sh .github/workflows/ci-skill-packs.yml.files touches any member, add every member to the S6 set (as modified when .files does not list it) so the unit is classified together. Always add catalog/skill-packs.json itself, even when this range did not touch it, so a copy frozen by the old rule gets caught up.skill-packs-unit, so the registry and the table never land out of step.catalog/skill-packs.json would go to a 3-way MERGE but lists no pack that upstream HEAD lacks (every jq -r '.packs[].repo' of the local copy is also in HEAD's), it is a frozen copy, not an operator edit - CLEAN-UPDATE it to the HEAD blob.Set up a workspace and, for each OWNED file f, fetch upstream's HEAD and BASELINE blobs:
WORK=$(mktemp -d)
fetch() { gh api "repos/${UPSTREAM}/contents/$1?ref=$2" --jq '.content' 2>/dev/null | base64 -d; } # $1=path $2=ref
h() { sha256sum 2>/dev/null | cut -d' ' -f1; }Decide f's disposition from its status and a content 3-way (local-current vs upstream@BASELINE vs upstream@HEAD):
status | Test | Disposition |
|---|---|---|
added | path absent locally | CLEAN-ADD (write HEAD blob) |
added | path present locally (collision, e.g. a fork-only skill) | CONFLICT |
modified | sha256(local) == sha256(HEAD blob) | already synced → SKIP |
modified | sha256(local) == sha256(BASELINE blob) (operator never touched it) | CLEAN-UPDATE (write HEAD blob) |
modified | otherwise (operator customized it) | 3-WAY MERGE → CLEAN-MERGE or CONFLICT (see below) |
removed | sha256(local) == sha256(BASELINE blob) | CLEAN-DELETE (git rm) |
removed | local differs or absent | CONFLICT (or already gone → SKIP if absent) |
renamed | treat as removed previous_filename + added filename under the rules above | per-part |
Never CLEAN-DELETE a skills/<name>/ directory whose <name> is not present in upstream's tree - fork-only skills are operator work and are structurally untouched (upstream's compare can only reference paths that exist upstream).
Also never CLEAN-DELETE a skills/<name>/ directory if <name> is currently enabled: true in the operator's aeon.yml (grep -E "^ ${name}: *\{[^}]*enabled: true" aeon.yml) - upstream retiring a skill the operator has actively scheduled is exactly the case validate-config.js's skill-refs check exists to catch, but only after the PR is merged; nothing in the PR review itself would otherwise flag it. Downgrade this case to CONFLICT (reason: enabled-skill-removed-upstream) instead of deleting - the directory stays, and S9 surfaces it as its own loud PR-body section rather than folding it into "Applied cleanly" or the generic conflict list.
3-way content merge (OWNED files only). A modified file that reached the otherwise row means the operator diverged from BASELINE and upstream changed the file too. Do not give up on it - most of the time the two sets of edits are in different parts of the file (e.g. the operator narrowed the workflow's env: secrets block while upstream bumped a timeout and a retry loop elsewhere), and a real 3-way merge combines both losslessly. Attempt it before declaring a conflict:
fetch "$f" "$BASELINE" > "$WORK/base" # upstream@BASELINE (the common ancestor)
fetch "$f" "$HEAD_SHA" > "$WORK/head" # upstream@HEAD (what to bring in)
cp "$f" "$WORK/local" # operator's current copy (ours)
if git merge-file -p --diff3 "$WORK/local" "$WORK/base" "$WORK/head" > "$WORK/merged.$$" 2>/dev/null; then
disposition=CLEAN-MERGE # exit 0 = disjoint hunks; the merged file carries BOTH edits - write it in S7
else
disposition=CONFLICT # exit >0 = the same lines changed on both sides; surface for a human as before
figit merge-file exits 0 only when the merge is clean (operator and upstream touched disjoint regions); the merged output preserves the operator's customization AND applies upstream's change. A non-zero exit means a genuine overlap - keep it a CONFLICT and list it with the upstream diff (S9). Only OWNED files are ever 3-way-merged; OPERATOR-owned paths are always surfaced, never written. A merged file gets the same S7 YAML/JSON parse-check as any written file - if the merge produced something that no longer parses, abort that file back to CONFLICT rather than committing it.
If MODE == report, skip to S9. Otherwise:
BR="aeon-update/sync-$(echo "$HEAD_SHA" | cut -c1-7)"
git checkout -b "$BR"Write every CLEAN-ADD / CLEAN-UPDATE (mkdir -p "$(dirname f)" then write the HEAD blob to f), write every CLEAN-MERGE (the merged file $WORK/merged.$$ from S6, over the existing f), and git rm every CLEAN-DELETE. CONFLICT and OPERATOR files are not touched - they go in the PR body only. (CLEAN-MERGE counts as a clean apply for the "nothing CLEAN applied" test below.)
If any skills/** path was applied, regenerate the derived catalogs from the synced sources (never copy them from upstream):
bin/generate-skills-json && bin/generate-packs-json && bin/generate-skill-icons
node scripts/gen-agents-md.js || truePack-registry parity check. If any pack-registry unit member (S5) was applied, run the validator against the final tree:
node scripts/validate-skill-packs.mjsA non-zero exit means the branch would turn ci-skill-packs red. Revert the unit's applied members (git checkout ${UP_DEFAULT} -- <member>, or git rm --cached for an add), re-classify them CONFLICT with reason skill-packs-parity, and include the validator output in S9 so the operator can fix the registry and the table in one edit.
Refresh the eyebrow integrity lock for any NEWLY-ADDED skill. ci-skill-integrity fails a PR when a present skill's skills/<slug>/SKILL.md has no "discoveredFrom": "skills/<slug>/SKILL.md" entry in eyebrowlock.json. That entry is produced only by the eyebrow binary, which is not preinstalled in this run - so a CLEAN-ADD of a new skill would otherwise land the PR CI-red. Fetch the binary at the exact version ci-skill-integrity.yml pins, parsed from that workflow's alexverify/eyebrow/action@<sha> # vX.Y.Z line so it can never drift from CI - a hardcoded version writes a lock that CI's (newer) eyebrow verify then rejects as drift, which is the recurring cause of red sync PRs. Verify the downloaded tarball against the release's own checksums.txt before running it (the same checksum-verified install the action does), then rescan:
EYEBROW_OK=0
EB=$(command -v eyebrow || true)
if [ -z "$EB" ]; then
# Use the SAME eyebrow binary ci-skill-integrity.yml runs, parsed from the
# eyebrow step's `version:` input (the action downloads exactly that binary).
# Not the `# vX.Y.Z` comment on the `uses:` line: Dependabot bumps only the ref
# + comment, so the comment can drift ahead of the binary CI really runs, and a
# lock written with that newer binary then trips CI as drift (the recurring
# sync red). Fall back to the comment, then v0.5.6, if unparsable.
EBV=$(grep -A6 'alexverify/eyebrow/action@' .github/workflows/ci-skill-integrity.yml | grep -oE '^ *version: *v[0-9]+\.[0-9]+\.[0-9]+' | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1)
[ -n "$EBV" ] || EBV=$(grep -oE 'alexverify/eyebrow/action@[0-9a-f]+ *# *v[0-9]+\.[0-9]+\.[0-9]+' .github/workflows/ci-skill-integrity.yml | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1)
EBV=${EBV:-v0.5.6}
# Linux runner (ubuntu-latest) assumed; unknown arch => skip to the fail-safe.
case "$(uname -m)" in
x86_64) A=amd64 ;;
aarch64|arm64) A=arm64 ;;
*) A= ;;
esac
TB="eyebrow_${EBV#v}_linux_${A}.tar.gz"
# Download the tarball + the release's checksums.txt and verify the tarball
# against it BEFORE extract or exec (mirrors the action's checksum-verified
# install). A tampered or moved asset fails the check, so we do NOT run it and
# fall through to the fail-safe. --ignore-missing checks only the asset present.
if [ -n "$A" ] && gh release download "$EBV" -R alexverify/eyebrow -p "$TB" -p checksums.txt -D "$WORK/eb" 2>/dev/null; then
if (cd "$WORK/eb" && shasum -a 256 --check --ignore-missing --strict checksums.txt >/dev/null 2>&1); then
tar xzf "$WORK/eb/$TB" -C "$WORK/eb" 2>/dev/null \
&& EB=$(find "$WORK/eb" -type f -name eyebrow | head -1) && chmod +x "$EB" 2>/dev/null || true
else
echo "::warning::eyebrow $TB failed checksums.txt verification - not executing"; EB=
fi
fi
fi
# Run with a SCRUBBED env (allowlist PATH+HOME only). eyebrow scan is a local
# file-hasher - it needs no secrets and no network - so denying it the run's
# secret env (GH_GLOBAL + provider/notify keys) means even a bad binary that
# slipped the checksum cannot read or exfiltrate them. If the scan fails, EYEBROW_OK
# stays 0 and the fail-safe below covers it.
[ -n "$EB" ] && env -i PATH="$PATH" HOME="$HOME" "$EB" scan --path . --lockfile eyebrowlock.json 2>/dev/null && EYEBROW_OK=1Fail-safe - guarantees a green PR without the binary. If EYEBROW_OK is still 0 (binary unavailable or scan failed), never ship a skill against a stale eyebrowlock.json entry. For each skill this run would invalidate, first try to carry upstream's entry (below); if that is not possible, revert it from the branch (git rm -r --cached skills/<slug> for a new skill, or git checkout ${UP_DEFAULT} -- skills/<slug>/SKILL.md to drop an edit to an existing one) and re-classify it as CONFLICT with reason needs-eyebrowlock-scan. S9 surfaces each with the exact operator command (eyebrow scan --path . --lockfile eyebrowlock.json then commit). Two cases invalidate the lock:
skills/** SKILL.md - a brand-new skill has no lock entry, so ci-skill-integrity's coverage precheck fails outright.SKILL.md that already has one or more findings in eyebrowlock.json. This is NOT safe to keep unscanned. eyebrow.policy.json sets failOnCapabilityExpansion: true, and an edit that changes the line count above a pinned finding relocates that finding to a new line - which eyebrow verify --ci reports as an expanded capability and fails, even though allowContentDrift: true (byte-level drift is allowed; a finding surfacing at a new location is not). This is the recurring cause of red sync PRs on prose-only edits. Detect it: the slug has a "discoveredFrom": "skills/<slug>/SKILL.md" entry in eyebrowlock.json and that entry's findings array is non-empty. Zero findings alone does not make an edit safe - see the next case.capabilities changed between BASELINE and HEAD. A skill with zero findings can still gain reach: upstream 46d1836 taught hunter-22 to call clawhunter.fun, an unscanned sync shipped the new SKILL.md against the old lock entry, and eyebrow verify --ci failed with policy: capability expansion - hunter-22 gained network: clawhunter.fun (miroshark-aeon#183). Upstream's own eyebrowlock.json already records the change, so detect it with no binary by diffing the slug's entry at the two commits:lockcaps() { fetch eyebrowlock.json "$1" | jq -cS --arg d "skills/$2/SKILL.md" '[.artifacts[] | select(.discoveredFrom == $d) | .capabilities]'; } # $1=ref $2=slug
[ "$(lockcaps "$BASELINE" "$slug")" != "$(lockcaps "$HEAD_SHA" "$slug")" ] && echo "$slug: capabilities changed upstream"Carry upstream's entry before holding. For any of these cases, if the skill's final tree on the branch is byte-identical to upstream HEAD's (same blob SHAs, so no operator customization), upstream's HEAD lock entry for that slug is exactly what upstream's own CI verified against those bytes. Splice it in instead of reverting:
up=$(gh api "repos/${UPSTREAM}/git/trees/${HEAD_SHA}?recursive=1" | jq -r --arg p "skills/$slug/" '.tree[] | select(.type == "blob" and (.path | startswith($p))) | "\(.sha) \(.path)"' | sort)
loc=$(git add -A "skills/$slug" && git ls-files -s "skills/$slug" | awk '{print $2" "$4}' | sort)
if [ -n "$up" ] && [ "$up" = "$loc" ]; then
fetch eyebrowlock.json "$HEAD_SHA" > "$WORK/uplock.json"
jq --slurpfile up "$WORK/uplock.json" --arg d "skills/$slug/SKILL.md" '
($up[0].artifacts | map(select(.discoveredFrom == $d))) as $e
| if ($e | length) != 1 then error("no upstream lock entry")
elif any(.artifacts[]; .discoveredFrom == $d) then .artifacts |= map(if .discoveredFrom == $d then $e[0] else . end)
else .artifacts += $e end' eyebrowlock.json > "$WORK/lock.new" && mv "$WORK/lock.new" eyebrowlock.json
fiIf the trees differ (the operator customized the skill, e.g. a CLEAN-MERGE) or the splice errors, upstream's entry does not describe these bytes - hold the skill as above.
After any such revert, re-run the S7 catalog regeneration (bin/generate-skills-json && bin/generate-packs-json) so skills.json/packs.json reflect the final tree, not the reverted-out file. This trades deferring a rescan-needing change (rare) for never landing a red PR; the change still arrives, just as a one-line manual step in the PR.
Then validate config:
node scripts/validate-config.js aeon.yml || echo "validate-config flagged (may be pre-existing drift; note, do not abort on it)"If nothing CLEAN applied (every upstream change was CONFLICT or OPERATOR): open no PR. Fold all changes into the report, log AEON_UPDATE_MANUAL_ONLY, and go to S10 (notify the operator that the sync needs a manual merge). If a file we wrote breaks YAML/JSON parsing, abort: git checkout . && git checkout ${UP_DEFAULT} && git branch -D "$BR", exit AEON_UPDATE_VALIDATION_FAILED, notify with the failing file.
Recompute PENDING: for every CONFLICT file this run plus every prior PENDING entry, keep it only if sha256(local) != sha256(HEAD blob) (still genuinely divergent). Drop the rest (resolved). A file that was CLEAN-MERGE-applied this run is resolved - never carry it as pending (its sha256(local) != sha256(HEAD blob) because it still holds the operator's edits, but the upstream change is now merged in, so the naive test would wrongly keep it forever; a 3-way-merged file is only a CONFLICT again if a future upstream change overlaps the operator's lines). Likewise drop any prior PENDING entry whose file was CLEAN-MERGE- or CLEAN-UPDATE-applied this run. Exception: enabled-skill-removed-upstream entries have no HEAD blob to diff (the path is deleted upstream) - resolve them instead when the skill is no longer enabled: true in the operator's current aeon.yml (they disabled it, so the CLEAN-DELETE rule can now apply next run) or upstream re-adds a path of that name (re-classify as CONFLICT/modified or CLEAN-UPDATE under the normal rules).
Write memory/topics/aeon-update-state.json and commit it with the sync so merging advances the watermark:
{
"baseline_sha": "${HEAD_SHA}",
"upstream": "${UPSTREAM}",
"last_run": "${today}",
"last_pr": null,
"applied": { "added": N, "updated": N, "deleted": N },
"pending_conflicts": [
{ "path": "scripts/foo.sh", "reason": "operator-customized", "upstream_commits": ["abc1234"] }
]
}Advancing baseline_sha to HEAD means clean files never re-notify, while pending_conflicts carries unresolved merges forward independently - so they resurface each run until the operator actually reconciles them, and are also written to the PR body. (In report mode, do not write state - a dry run mutates nothing.)
git add -A
git commit -F /tmp/aeon-update-commit.txt # never inline the message with -m (backticks/`$()` in commit text get shell-substituted)
git push -u origin "$BR"
gh pr create --repo "$SELF" --base "$UP_DEFAULT" \
--title "aeon-update: sync ${N_COMMITS} upstream commits (${BASE7}..${HEAD7})" \
--body-file /tmp/aeon-update-pr-body.md/tmp/aeon-update-commit.txt:
aeon-update: sync upstream ${BASE7}..${HEAD7}
${N_COMMITS} upstream commits from ${UPSTREAM}. ${N_APPLIED} files applied cleanly, ${N_CONFLICT} need manual review. Baseline advanced to ${HEAD7}.PR body (/tmp/aeon-update-pr-body.md) - only include sections that have content:
## Upstream sync: `${UPSTREAM}` `${BASE7}..${HEAD7}`
**${N_COMMITS} commits** ({earliest date} → {latest date}) · **${N_APPLIED} applied** · **${N_CONFLICT} manual** · baseline → `${HEAD7}`.
### Applied cleanly
- **New skills:** `foo`, `bar` _(regenerated catalogs + agents.md + skill-icons)_
- **Modified skills:** `baz`
- **Scripts / harness:** `scripts/notify.sh`, ...
- **Workflows:** `.github/workflows/...`
- **Auto-merged (3-way):** `.github/workflows/aeon.yml`, ... _(your local customization kept; upstream's disjoint changes applied - review the merged hunks)_
- **Docs / other:** `docs/...`, `CLAUDE.md`, ...
### ⚠️ Currently-enabled skills removed upstream
For each CONFLICT with reason `enabled-skill-removed-upstream` (S6):
- `verdikta-hunter` — enabled in your `aeon.yml`, deleted upstream in {commit(s)}. **Not deleted here** so nothing breaks. Pick one: keep it as a fork-only skill going forward (nothing else to do), or disable it in `aeon.yml` to match upstream's current default set.
### Needs manual review (conflicts - your local copy diverges from upstream)
For each *other* conflict reason (`enabled-skill-removed-upstream` is covered above, don't duplicate it here): what upstream changed and why it wasn't auto-applied.
- `scripts/foo.sh` — you customized this locally; upstream changed it in {commits}. Upstream diff:
```diff
{short upstream base..head diff for the file}aeon.yml — upstream added skills / changed defaults: {summary}. Merge the new entries you want (keep your enable/schedule/model choices).soul/…, STRATEGY.md — {summary, if changed}| SHA | Summary |
|---|---|
| abc1234 | ... |
Capture the PR URL; write it back into `last_pr` in the branch's state file (amend the state commit) so the merged watermark records its own PR.
### S10. Log + notify
Append to `memory/logs/${today}.md` under `### aeon-update`: status, `UPSTREAM`, `${BASE7}..${HEAD7}`, applied/conflict counts, PR URL (or `report`/`manual-only`), and `pending_conflicts` count.
**Notify only on signal** - match `soul/` voice if present. Send when there is a PR, a report with changes, or a manual-only situation; stay silent for `IN_SYNC` / `BASELINE_SET`-with-nothing. Keep it ≤4000 chars:
aeon-update — ${today} {verdict: "synced N commits → PR" | "N changes need manual merge" | "report: N commits behind"}
Upstream ${UPSTREAM} is ${AHEAD} commits ahead. Applied ${N_APPLIED} cleanly, ${N_CONFLICT} need review.
{Top applied highlight: e.g. "new skill: token-radar; harness fix in run-harness"}
{If conflicts: "Manual: aeon.yml (new skills), scripts/foo.sh (local edit)"}
PR: {url} (or "dry run — nothing changed")
Pass `--mute-key "aeon-update:${HEAD7}"` so a muted sync doesn't re-ping for the same upstream HEAD.
## Exit taxonomy
| Code | When | Notify |
|------|------|--------|
| `AEON_UPDATE_OK` | PR opened with ≥1 clean file (conflicts, if any, listed in it) | Yes - PR link |
| `AEON_UPDATE_MANUAL_ONLY` | Upstream changed only operator-owned / conflicting files - no clean apply, no PR | Yes - manual call-out |
| `AEON_UPDATE_REPORT` | `report` mode - delta computed, nothing mutated | Yes - dry-run summary |
| `AEON_UPDATE_IN_SYNC` | Baseline already == upstream HEAD | No (log only) |
| `AEON_UPDATE_BASELINE_SET` | First run - watermark anchored, nothing applied | One-line notify |
| `AEON_UPDATE_IS_UPSTREAM` | This instance is the upstream repo itself | No (log only) |
| `AEON_UPDATE_BASELINE_UNREACHABLE` | Baseline is not an ancestor of HEAD (history rewrite) | Yes - asks for `reset=` |
| `AEON_UPDATE_VALIDATION_FAILED` | An applied file broke YAML/JSON parsing → branch reverted | Yes - failing file |
## Constraints
- **Never** push to `main` or auto-write an OPERATOR-owned path (`aeon.yml`, `soul/`, `memory/`, `STRATEGY.md`, `.mcp.json`, `output/`).
- **Never** delete a fork-only skill, and **never** copy `catalog/*.json` / `eyebrowlock.json` from upstream - regenerate them. Two exceptions: `catalog/skill-packs.json` has no generator and is synced as part of the pack-registry unit (S5), and a single skill's lock entry may be carried from upstream when that skill's tree matches upstream HEAD exactly (S7 fail-safe).
- **Never** advance `baseline_sha` without carrying unresolved conflicts forward in `pending_conflicts`.
- Cross-repo compare caps at 300 files - note `files_truncated=true` and let a follow-up run pick up the rest.
- A clean, in-sync run is **correct**, not a failure - it notifies nothing.
## Network note
Every network call is `gh api`, which authenticates via `GITHUB_TOKEN` automatically - no `curl`, no `./secretcurl`, no `$SECRET` on the command line for the Bash permission layer to refuse, and no secret beyond the default `GITHUB_TOKEN`. There are no irreversible side-effects: the skill's only mutation is a PR against this instance's own repo, which the operator reviews and merges. Retry policy: on `403` with `X-RateLimit-Remaining: 0`, sleep 60s and retry once; on a persistent contents-API failure for one file, mark it `UNREADABLE` in the report and continue with a partial sync rather than aborting the whole run.© aeonfun, MIT. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in skills/aeon-update of aeonfun/aeon.
Open the folder on GitHubat commit f252074
Aeon Update next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Aeon Update this skillaeonfun/aeon | 767 | — | ~7.4k | Automated safety check: Notes | MIT | |
| Update From Upstream Mainremotion-dev/remotion | 62k | — | ~1k | Automated safety check: Pass | Custom licence | |
| Update Workerd Upstreamelliothux/open-compute | 1.6k | — | ~922 | Automated safety check: Pass | Apache-2.0 | |
| UI Updatesickn33/agentic-awesome-skills | 47k | 1 repos | ~1.4k | Automated safety check: Pass | MIT | |
| Update Specificationgithub/awesome-copilot | 40k | 2 repos | ~1.4k | Automated safety check: Pass | MIT | |
| UpdateQ00/ouroboros | 6.2k | — | ~1.1k | Automated safety check: Pass | MIT |
remotion-dev/remotion
Update the current Remotion branch with the latest canonical main branch.
elliothux/open-compute
Update open-compute's workerd fork onto current Cloudflare upstream, minimize fork-owned code, regroup fork commits by capability, and coordinate the submodule, pin, tests, and docs.
sickn33/agentic-awesome-skills
Update StyleSeed engine in your project — analyzes what's outdated and updates safely
github/awesome-copilot
Update an existing specification file for the solution, optimized for Generative AI consumption based on new requirements or updates to any existing code.
Q00/ouroboros
Check for updates and upgrade Ouroboros to the latest version
davila7/claude-code-templates
Guides time series machine learning with the aeon toolkit: classification, regression, clustering, forecasting, anomaly detection, segmentation and similarity search.
aeonfun/aeon
Browses open tasks on the TaskMarket agent-worker market and, with explicit operator approval, creates tasks, tracks submissions and submits finished work.
aeonfun/aeon
Sets up and manages an Aeon agent instance that runs skills on a schedule through GitHub Actions: starting, rescheduling, debugging, editing skills and mining chat history.
aeonfun/aeon
Reads a Base Account's address, portfolio and transaction history through the Base MCP server, and stays strictly read-only in unattended Aeon runs, reporting only changes.
aeonfun/aeon
Audits every page of a site each day from its sitemap, scores on-page and technical SEO, checks duplicates across pages and reports what changed since the last run.
aeonfun/aeon
5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates
aeonfun/aeon
Static linter for an Aeon instance's configuration that catches silent failures such as unquoted schedules, duplicate keys, unconfigured skills and broken MCP references.
Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config. Aeon Update is an agent skill from aeonfun/aeon. Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.
Run `npx skills add aeonfun/aeon --skill aeon-update -a claude-code`. Or copy the skill folder (skills/aeon-update in aeonfun/aeon) into .claude/skills/aeon-update in your project. Claude Code loads it when a task matches its description.
Run `npx skills add aeonfun/aeon --skill aeon-update -a codex`. Or copy the skill folder (skills/aeon-update in aeonfun/aeon) into .agents/skills/aeon-update in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add aeonfun/aeon --skill aeon-update -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/aeon-update, .gemini/skills/aeon-update, .github/skills/aeon-update and .opencode/skills/aeon-update in your project.
Going by SKILL.md and its folder, Aeon Update needs the command-line tools its instructions call (git, gh, jq and node) and credentials named GITHUB_TOKEN.
SKILL.md contains no URLs. Its commands use git and gh, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Aeon Update is published under the MIT licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 7.4k tokens (SKILL.md is roughly 30k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Aeon Update: Update From Upstream Main (remotion-dev/remotion, 62k stars), Update Workerd Upstream (elliothux/open-compute, 1.6k stars), UI Update (sickn33/agentic-awesome-skills, 47k stars) and Update Specification (github/awesome-copilot, 40k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
aeonfun (a GitHub organization) maintains it in aeonfun/aeon, which has 767 GitHub stars. The repository holds 82 skills in this directory. The repository was last updated on October 6, 2026.
Source: aeonfun/aeon on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.