Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Clone this Aeon agent into a new GitHub repo - fork, configure skills, validate, and register in the fleet
.claude/skills/aeonfun-spawn-instance/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-13 | ✗→✓ | ▲ Improved | 140% | 0% |
| case-19 | ✗→✓ | ▲ Improved | 131% | 0% |
| case-01 | ✗→✓ | ▲ Improved | 258% | 0% |
| case-20 | ✗→✓ | ▲ Improved | 161% | 0% |
| case-09 | ✗→✓ | ▲ Improved | 220% | 0% |
<!-- autoresearch: variation C — robust: skill-existence validation, exit taxonomy, idempotent recovery, dynamic SETUP.md, pre/post-flight verification -->
> ${var} — Name and purpose of the new instance. Format name: purpose, e.g. crypto-tracker: monitor DeFi protocols and token movements. If empty, notify the owner and stop with exit SPAWN_INVALID_VAR.
Today is ${today}. Create a new Aeon instance by forking this repo, configuring it for a specific purpose, validating the configuration, and registering it in the fleet.
Read memory/MEMORY.md at the start for context.
This skill creates the repo and configuration but does NOT propagate secrets. The new instance is inert until the owner manually sets secrets (ANTHROPIC_API_KEY or CLAUDE_CODE_OAUTH_TOKEN, plus notification secrets). Each instance has its own API keys for billing isolation and blast-radius containment.
Every run ends with one of these status codes, written to the log and (where relevant) included in the notification:
| Code | Meaning | |------|---------| | SPAWN_OK | Fork created, configured, pushed, Actions enabled, registered. | | SPAWN_FORK_EXISTS_RECOVERED | Fork already existed but wasn't registered → configured + registered now. | | SPAWN_FORK_EXISTS_REGISTERED | Fork exists AND is already registered and not archived — refused, no change. | | SPAWN_INVALID_VAR | var was empty or couldn't be parsed into name: purpose. | | SPAWN_NO_SKILLS | After validation, the skill plan was empty — refused. | | SPAWN_FORK_FAILED | gh repo fork and the fallback forks API both failed. | | SPAWN_PUSH_FAILED | Fork created but push failed — fork left in place, recovery instructions emitted. | | SPAWN_ACTIONS_FAILED | Configuration pushed but enabling Actions failed — recovery instructions emitted. | | SPAWN_API_ERROR | Any other GitHub API failure not covered above. |
${var} is empty, log SPAWN_INVALID_VAR: empty var to memory/logs/${today}.md, notify: spawn-instance: empty var — re-run with "name: purpose", and stop.: — left is NAME_RAW, right is PURPOSE (trim whitespace). If either is empty, exit SPAWN_INVALID_VAR.NAME: lowercase NAME_RAW, replace non-alphanumeric runs with -, strip leading/trailing -, truncate to 40 chars. If empty after sanitization, exit SPAWN_INVALID_VAR.REPO_NAME="aeon-${NAME}".bashgh auth status || { echo "SPAWN_API_ERROR: gh not authenticated"; exit 1; } OWNER=$(gh api user --jq '.login') || { echo "SPAWN_API_ERROR: cannot read user"; exit 1; } PARENT_REPO=$(gh repo view --json nameWithOwner -q .nameWithOwner) # If current repo is itself a fork, use the upstream as the parent: PARENT_UPSTREAM=$(gh api "repos/${PARENT_REPO}" --jq '.parent.full_name // .full_name')
Check rate limit:
bashREMAINING=$(gh api rate_limit --jq '.resources.core.remaining') [ "${REMAINING:-0}" -lt 50 ] && { echo "SPAWN_API_ERROR: rate limit too low (${REMAINING})"; exit 1; }
Read memory/instances.json. If it doesn't exist, create it with {"instances": []}.
json{ "instances": [ { "name": "crypto-tracker", "repo": "OWNER/aeon-crypto-tracker", "purpose": "monitor DeFi protocols and token movements", "created": "2026-04-20", "status": "pending_secrets", "skills_enabled": ["token-movers", "defi-overview", "heartbeat"], "parent": "OWNER/aeon" } ] }
name == NAME exists and status != "archived", exit SPAWN_FORK_EXISTS_REGISTERED, notify, and stop.Do not use a hardcoded skill list. The catalog in earlier revisions contained broken names (wallet-digest, trending-coins, tweet-digest, hn-digest — none of which exist in skills/). Always enumerate live.
bash# Build live catalog of available skills and their tags/description. for d in skills/*/; do name=$(basename "$d") [ -f "$d/SKILL.md" ] || continue # Extract frontmatter fields (description + tags) awk '/^---/{f++;next} f==1{print}' "$d/SKILL.md" done
For each skill, parse the frontmatter description: and tags: lines. Classify the purpose into themes by keyword match (case-insensitive) and pick skills whose description or tags overlap:
| Purpose keywords | Prefer skills matching tags / keywords | |---|---| | crypto, defi, token, chain, wallet | tags [crypto] or description contains token/defi/chain | | research, paper, academic, science | tags [research] or name contains paper/research/deep-research | | social, twitter, x , tweet, farcaster | tags [social] or name contains tweet/twitter/farcaster/reply | | news, digest, feed, brief | name contains digest/brief/morning/rss/hacker-news | | dev, code, github, repo, pr | tags [dev] or name contains github/code/repo/pr-/changelog | | security, vuln, audit | tags [security] or name contains security/vuln/audit | | prediction, polymarket, kalshi, market | name contains polymarket/kalshi/market/narrative |
Rules:
heartbeat if the file skills/heartbeat/SKILL.md exists (health monitoring).[reflect, heartbeat] (filtered against existence).skills/${skill}/SKILL.md doesn't exist; log each drop as SPAWN_DROPPED_SKILL: ${skill}.SPAWN_NO_SKILLS, notify, and stop.Save the final list as SKILLS_ENABLED.
bashif gh api "repos/${OWNER}/${REPO_NAME}" --jq '.full_name' >/dev/null 2>&1; then FORK_STATE="exists" else # Try the modern flag first if ! gh repo fork "${PARENT_UPSTREAM}" --fork-name "${REPO_NAME}" --clone=false 2>/dev/null; then # Fallback: POST to /repos/{owner}/{repo}/forks with a custom name gh api "repos/${PARENT_UPSTREAM}/forks" -X POST -f name="${REPO_NAME}" --jq '.full_name' \ || { echo "SPAWN_FORK_FAILED"; exit 1; } fi FORK_STATE="created" fi
Wait for fork availability (GitHub forks are async):
bashfor i in $(seq 1 10); do gh api "repos/${OWNER}/${REPO_NAME}" --jq '.full_name' >/dev/null 2>&1 && break sleep 3 done gh api "repos/${OWNER}/${REPO_NAME}" --jq '.full_name' >/dev/null 2>&1 \ || { echo "SPAWN_FORK_FAILED: fork not reachable after wait"; exit 1; }
bashTMPDIR=$(mktemp -d) trap 'rm -rf "$TMPDIR"' EXIT gh repo clone "${OWNER}/${REPO_NAME}" "$TMPDIR/repo" -- --quiet \ || { echo "SPAWN_API_ERROR: clone failed"; exit 1; } cd "$TMPDIR/repo" git config user.name "aeonframework" git config user.email "aeonframework@proton.me"
aeon.ymlaeon.yml as a base (preserves comments, ordering, and the full skill list).enabled: true only on skills in SKILLS_ENABLED. All others must have enabled: false.enabled: true must correspond to an existing skills/${skill}/SKILL.md in the fork's tree. If any enabled skill is missing, abort before pushing (this is a sanity check — step 4 already validated, but the child's repo tree must match).SETUP.md dynamicallyParse each enabled skill's skills/${skill}/SKILL.md for references to uppercase env var names (pattern: [A-Z][A-Z0-9_]{2,}_(KEY|TOKEN|URL|ID|SECRET)). Collect the union across enabled skills, excluding built-ins (GITHUB_TOKEN, GH_TOKEN).
Emit a SETUP.md with three sections:
markdown# Aeon Instance Setup This is a managed Aeon instance spawned from `${PARENT_UPSTREAM}`. ## Purpose ${PURPOSE} ## Enabled Skills ${comma-separated list with one-line descriptions from each SKILL.md} ## Activate This Instance Go to **Settings > Secrets and variables > Actions** and add these secrets: ### Required (pick one for Claude auth) | Secret | Description | |--------|------------| | `ANTHROPIC_API_KEY` | Anthropic API key — console.anthropic.com | | `CLAUDE_CODE_OAUTH_TOKEN` | Alternative: Claude Code OAuth token | ### Recommended (notifications) | Secret | Description | |--------|------------| | `TELEGRAM_BOT_TOKEN` | Telegram bot token | | `TELEGRAM_CHAT_ID` | Telegram chat ID | | `DISCORD_WEBHOOK_URL` | Discord webhook URL | | `SLACK_WEBHOOK_URL` | Slack webhook URL | ### Required by your enabled skills ${one row per detected env var, with the list of skills that reference it} ## Fleet Parent Managed by `${PARENT_UPSTREAM}`. The parent can dispatch skills and monitor health via `fleet-control`. ## Security Note This instance has NO access to the parent's secrets. Each instance maintains its own API keys and credentials.
If no env vars are detected beyond the required/recommended set, omit the "Required by your enabled skills" section header (don't leave an empty table).
CLAUDE.md idempotentlyFind the heading line ## About This Repo (or the first ## section if not present). Under it, insert exactly one line:
- Managed instance of ${PARENT_UPSTREAM}. Purpose: ${PURPOSE}.If a line starting with - Managed instance of already exists, replace it rather than appending a duplicate.
The child starts with a clean slate:
bash# Reset MEMORY.md to a minimal template cat > memory/MEMORY.md <<EOF # Long-term Memory *Last consolidated: never* ## About This Repo - Aeon instance of ${PARENT_UPSTREAM}. Purpose: ${PURPOSE}. ## Next Priorities - Configure secrets per SETUP.md - Wait for first scheduled skill to run EOF # Clear logs, state, topics — safe paths only, never follow symlinks find memory/logs -mindepth 1 -type f -delete 2>/dev/null || true find memory/topics -mindepth 1 -type f -delete 2>/dev/null || true find memory/issues -mindepth 1 -type f ! -name 'INDEX.md' -delete 2>/dev/null || true [ -f memory/issues/INDEX.md ] && echo "# Issues — none open" > memory/issues/INDEX.md echo '{}' > memory/cron-state.json # Do NOT clear memory/instances.json on the child — the child has none. rm -f memory/instances.json 2>/dev/null || true
bashgit add -A git commit -m "chore: configure instance — ${PURPOSE} Skills enabled: ${SKILLS_ENABLED_CSV} Parent: ${PARENT_UPSTREAM} Spawned: ${today}" git push origin main 2>/dev/null \ || git push origin HEAD:main 2>/dev/null \ || { echo "SPAWN_PUSH_FAILED"; cd - >/dev/null; exit 1; } cd - >/dev/null
If push fails, do not delete the fork — leave it for manual inspection and notify with:
spawn-instance: fork created but push failed for ${OWNER}/${REPO_NAME}.
Recover with: gh repo clone ${OWNER}/${REPO_NAME} && push manually, then re-register.Forks have Actions disabled by default. Use typed booleans (-F, not -f, or enabled=true is sent as the string "true"):
bashgh api "repos/${OWNER}/${REPO_NAME}/actions/permissions" -X PUT \ -F enabled=true -f allowed_actions=all \ || { echo "SPAWN_ACTIONS_FAILED"; exit 1; }
bash# Fork reachable gh api "repos/${OWNER}/${REPO_NAME}" --jq '.full_name' >/dev/null \ || { echo "SPAWN_API_ERROR: post-flight fork check failed"; exit 1; } # Our commit is HEAD HEAD_SHA=$(gh api "repos/${OWNER}/${REPO_NAME}/commits/main" --jq '.sha' 2>/dev/null) [ -n "$HEAD_SHA" ] || { echo "SPAWN_API_ERROR: post-flight HEAD check failed"; exit 1; } # Actions enabled ACTIONS_ON=$(gh api "repos/${OWNER}/${REPO_NAME}/actions/permissions" --jq '.enabled' 2>/dev/null) [ "$ACTIONS_ON" = "true" ] || { echo "SPAWN_ACTIONS_FAILED: post-flight"; exit 1; }
Update memory/instances.json in the parent repo. Append:
json{ "name": "${NAME}", "repo": "${OWNER}/${REPO_NAME}", "purpose": "${PURPOSE}", "created": "${today}", "status": "pending_secrets", "skills_enabled": ${SKILLS_ENABLED_JSON}, "parent": "${PARENT_UPSTREAM}" }
If FORK_STATE == "exists" at step 5, the final exit status is SPAWN_FORK_EXISTS_RECOVERED, otherwise SPAWN_OK.
Append to memory/logs/${today}.md:
### spawn-instance
- Status: ${EXIT_CODE}
- Instance: ${OWNER}/${REPO_NAME}
- Purpose: ${PURPOSE}
- Skills: ${SKILLS_ENABLED_CSV}
- Dropped skills: ${DROPPED_CSV or "(none)"}
- Actions enabled: yes./notifyOn success (SPAWN_OK or SPAWN_FORK_EXISTS_RECOVERED):
*New Aeon Instance*
Repo: ${OWNER}/${REPO_NAME}
Purpose: ${PURPOSE}
Skills (${N}): ${SKILLS_ENABLED_CSV}
Status: ${EXIT_CODE} — PENDING SECRETS
Activate:
1. https://github.com/${OWNER}/${REPO_NAME}/settings/secrets/actions
2. Add ANTHROPIC_API_KEY (or CLAUDE_CODE_OAUTH_TOKEN)
3. Add TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID (or Discord/Slack)
See SETUP.md in the repo for the full secret list tailored to your enabled skills.On refusal (SPAWN_FORK_EXISTS_REGISTERED, SPAWN_INVALID_VAR, SPAWN_NO_SKILLS):
spawn-instance: ${EXIT_CODE} — ${one-line reason}. No changes made.On failure (SPAWN_FORK_FAILED, SPAWN_PUSH_FAILED, SPAWN_ACTIONS_FAILED, SPAWN_API_ERROR):
spawn-instance FAILED: ${EXIT_CODE}
Target: ${OWNER}/${REPO_NAME}
Recovery: ${one-line recovery instruction}This skill runs entirely through gh CLI, which handles auth internally, so no bare $SECRET ever lands on the command line for the Bash permission layer to refuse. No WebFetch fallback needed. If gh is missing or unauthenticated, the pre-flight step fails with SPAWN_API_ERROR.
ANTHROPIC_API_KEY, TELEGRAM_*, DISCORD_*, SLACK_*, or any API key into the child repo. The whole security model depends on this.skills/*/SKILL.md at runtime to avoid referencing renamed or removed skills.${var} must never produce duplicate registry entries, duplicate CLAUDE.md lines, or a second fork.memory/instances.json via the normal Aeon workflow. The parent commit happens through the same path as any other skill output.Write complete, working code. No TODOs or placeholders.
Other measured skills in the registry, with their headline benchmark lift.