Thanks for helping make Aeon the default way people run autonomous agents. This guide collects the conventions already used across the repo so you don't have to reverse-engineer them from existing PRs.
Most contributions fall into one of four buckets — a new skill, a new LLM gateway, a community skill pack listing, or a core fix (dashboard, scripts, workflows, docs). Each has its own checklist below.
- Fork or use the template. This repo is a public template — click Use this template (or
gh repo fork aaronjmars/aeon --clone). Run your own instance as a fork; open PRs back here for changes that benefit everyone. - Branch from
main. Never push tomain. Use a descriptive branch name (feat/...,fix/...,docs/...). - One change per PR. A focused 20-line fix lands faster than a 500-line bundle. Don't mix a new skill with an unrelated refactor.
- PRs are squash-merged. Write a clear PR title — it becomes the commit subject on
main. Squashing also rewrites per-skill commit hashes, which is why the CI gate below normalizes them.
A skill is a single skills/<name>/SKILL.md prompt file plus a registration in aeon.yml. The fastest paths:
./new-from-template <template> <skill-name> --category <pack> # scaffold from skill-templates/
./add-skill <owner/repo> <skill> [skill...] # import from any GitHub repoYou can also describe one to the create-skill skill, or label a GitHub issue ai-build and let Aeon implement it and open the PR.
Every SKILL.md opens with YAML frontmatter. The full contract lives in skill-templates/TEMPLATE.md; the essentials:
---
name: my-skill
category: dev # the pack this skill joins
description: One-line description of what this skill does
var: ""
tags: [dev, crypto]
requires: [XAI_API_KEY, COINGECKO_API_KEY?] # bare = required · `?` = works better with
mcp: [base] # MCP servers, same two tiers
---category:is the single source of truth for which pack the skill belongs to. Use one ofresearchdevcryptoonchain-securitysocialproductivitymeta. (coreandfleetare curated inpacks.config.json, not set here.) Omit it and the skill lands in the Lab catch-all until triaged../new-from-template ... --category <pack>and the dashboard import dropdown set it for you.requires:is the single source of truth the dashboard reads to show which skill needs which key. Use the exact env-var name. A bare name is required; a trailing?means the skill still runs without it (degraded). Omitrequires:(or use[]) for skills that only need the built-in Claude + GitHub tokens.mcp:declares MCP servers the skill calls, with the same two-tier semantics. Slugs referenceapps/dashboard/lib/mcp-catalog.ts.- Match the names to the registry in
apps/dashboard/app/api/secrets/route.tsso the dashboard can describe the key and link where to get it.
- Be explicit and self-contained. A skill runs unattended — spell out the steps, the data sources, and what to do on failure.
- Add a "Sandbox note". GitHub Actions blocks
$ENV_VARexpansion incurlheaders and may block outbound network from bash. Use the right fallback: a WebFetch fallback for keyless public APIs, ascripts/prefetch-*.shcache for auth'd APIs, orgh apifor GitHub. SeeCLAUDE.md. - Notify through
./notify— never call a channel API directly. It fans out to every configured channel and silently skips the rest. - Don't monkey-patch Aeon internals. A skill should be a prompt, not a patch to the runner, scheduler, or dashboard.
skills.json (the skill catalog) and packs.json (the pack catalog the dashboard reads) are both generated, never hand-edited. After adding or recategorizing a skill, regenerate both:
./generate-skills-json # skill catalog (reads each SKILL.md's category:)
./generate-packs-json # pack catalog (groups skills by category + packs.config.json)
# then commit both resultsThe ci-skills-json and ci-packs-json workflows fail any PR that leaves either manifest stale. They normalize out the generated timestamp (and ci-skills-json also the per-skill sha/updated), so only real catalog drift fails them. ci-packs-json also fails if a skill ends up unassigned to any pack with no Lab catch-all declared.
A gateway is wired through five files, all following the existing pattern — copy an entry of the same tier (native: already speaks the Anthropic API; sidecar: OpenAI-compatible, bridged per run). The exact checklist is in the README: Adding a gateway.
apps/dashboard/lib/types.ts— add the slug to theGatewayProviderunion andGATEWAY_PROVIDERS.apps/dashboard/lib/auth-provider.mjs— addslug: { label, secretName, prefixes }.apps/dashboard/app/api/secrets/route.ts— list the secret inBUILTIN_SECRETSand map it inGATEWAY_SECRETS.scripts/llm-gateway.sh— add acasebranch and register the slug + secret in the auto-resolver order..github/workflows/aeon.yml(andmessages.yml) — pass the new secret into the run'senv:.
Then add a row to the gateway table in the README. Verify the loop end to end: paste a key in the dashboard and run any skill — the log should print ::notice:: gateway=auto resolved to <slug>.
Skill packs are third-party skill collections in their own repos, installable as one bundle. To list yours in the README's Community skill packs table, open a PR that:
- Adds a README table row linking to your public repo, with the skill count and a one-line description.
- Adds a matching entry to
skill-packs.json— the machine-readable mirror. - Confirms the pack has a
skills-pack.jsonmanifest, a clear license, and a per-skillSKILL.md.
Before opening the PR, run the local pre-flight validator against your pack directory — it checks the same structural invariants install-skill-pack enforces (valid manifest, slugs, paths, capability taxonomy, present SKILL.mds) plus the publishing-checklist items, so you catch problems before a reviewer does:
./scripts/validate-pack.sh /path/to/your-pack-dir # add --path <subdir> if skills-pack.json is nestedIt exits 0 when the pack is valid (warnings are advisory) and 1 on any blocking error.
Packs must not monkey-patch Aeon internals or depend on private endpoints. Full schema and trust model: docs/community-skill-packs.md.
Locking gates run on every PR. All are fast and only trigger on the paths they protect:
| Gate | Triggers on | What it enforces |
|---|---|---|
ci-skills-json |
skills/**, aeon.yml, generate-skills-json, skills.json |
skills.json matches a fresh ./generate-skills-json |
ci-packs-json |
packs.config.json, skills.json, generate-packs-json, packs.json |
packs.json matches a fresh ./generate-packs-json |
ci-skill-category |
skills/**, the category-check script |
every SKILL.md declares a valid category: |
ci-capabilities-parity |
install-skill-pack, docs/CAPABILITIES.md, the parity script |
the capabilities taxonomy stays in sync across its sources |
Run the checks locally before pushing: bash scripts/check-skill-categories.sh (categories) and bash scripts/check-capabilities-parity.sh (taxonomy). If ci-packs-json or ci-skills-json fails, you changed a generator input without committing the regen — run ./generate-skills-json && ./generate-packs-json.
Open an issue. For a bug, include the skill name, the relevant (redacted) memory/logs/ entry, whether the failure was an API or sandbox issue, and whether notifications came through. For a feature you'd like Aeon to build itself, label the issue ai-build.
By contributing, you agree that your contributions are licensed under the repository's LICENSE.