diff --git a/README.md b/README.md index 2b883705..bdbae71b 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ The JS and Python versions are kept in lock-step by `release-please` and guarded ## Extensions Catalog -This repository contains **2 marketplace(s)** with **68 extensions** (58 skills, 10 plugins). +This repository contains **2 marketplace(s)** with **69 extensions** (59 skills, 10 plugins). ### large-codebase @@ -115,11 +115,12 @@ OpenHands skills for interacting, improving, and refactoring large codebases Official skills and plugins for OpenHands — the open-source AI software engineer. -**64 extensions** (56 skills, 8 plugins) +**65 extensions** (57 skills, 8 plugins) | Name | Type | Description | Commands | |------|------|-------------|----------| | add-skill | skill | Add (import) an OpenHands skill from a GitHub repository into the current workspace. | — | +| aeon | skill | Set up and operate an Aeon autonomous-agent instance from your coding agent - enable, schedule, edit, and debug skill... | — | | agent-canvas-environment | skill | Work effectively inside a local Agent Canvas environment, including local agent-server auth, safe workspace hygiene, ... | — | | agent-creator | skill | Create file-based sub-agents as Markdown files — no Python code required. Guides the user through a structured interv... | `/agent-creator` | | agent-memory | skill | Persist and retrieve repository-specific knowledge using AGENTS.md files. Use when you want to save important informa... | `/remember` | diff --git a/marketplaces/openhands-extensions.json b/marketplaces/openhands-extensions.json index afa26ff8..130a7b09 100644 --- a/marketplaces/openhands-extensions.json +++ b/marketplaces/openhands-extensions.json @@ -10,6 +10,20 @@ "homepage": "https://github.com/OpenHands/extensions" }, "plugins": [ + { + "name": "aeon", + "source": "./skills/aeon", + "description": "Set up and operate an Aeon autonomous-agent instance from your coding agent - enable, schedule, edit, and debug skills that run on a schedule in GitHub Actions, wire secrets and channels, set strategy and voice, and turn past coding-agent chats into scheduled skills.", + "category": "integrations", + "keywords": [ + "aeon", + "agent", + "skills", + "automation", + "github-actions", + "cron" + ] + }, { "name": "agent-creator", "source": "./skills/agent-creator", diff --git a/skills/aeon/.claude-plugin b/skills/aeon/.claude-plugin new file mode 120000 index 00000000..665797f0 --- /dev/null +++ b/skills/aeon/.claude-plugin @@ -0,0 +1 @@ +.plugin \ No newline at end of file diff --git a/skills/aeon/.codex-plugin b/skills/aeon/.codex-plugin new file mode 120000 index 00000000..665797f0 --- /dev/null +++ b/skills/aeon/.codex-plugin @@ -0,0 +1 @@ +.plugin \ No newline at end of file diff --git a/skills/aeon/.plugin/plugin.json b/skills/aeon/.plugin/plugin.json new file mode 100644 index 00000000..b8ccd7ce --- /dev/null +++ b/skills/aeon/.plugin/plugin.json @@ -0,0 +1,19 @@ +{ + "name": "aeon", + "version": "1.0.0", + "description": "Set up and operate an Aeon autonomous-agent instance from your coding agent - enable, schedule, edit, and debug skills that run on a schedule in GitHub Actions, wire secrets and channels, set strategy and voice, and turn past coding-agent chats into scheduled skills.", + "author": { + "name": "Aeon Inc" + }, + "homepage": "https://aeon.fun", + "repository": "https://github.com/aeonfun/aeon", + "license": "MIT", + "keywords": [ + "aeon", + "agent", + "skills", + "automation", + "github-actions", + "cron" + ] +} diff --git a/skills/aeon/README.md b/skills/aeon/README.md new file mode 100644 index 00000000..5213eec2 --- /dev/null +++ b/skills/aeon/README.md @@ -0,0 +1,34 @@ +# Aeon + +Set up and operate an [Aeon](https://github.com/aeonfun/aeon) autonomous-agent instance from your coding agent. Aeon is an open-source framework that runs your own skills on a schedule in GitHub Actions. This skill is the operator console for an Aeon instance. + +## Triggers + +This skill is activated by the following keywords: + +- `aeon` +- `aeon.yml` +- `aeon instance` +- `aeon skill` + +## What it does + +The skill routes to the mode you need: + +- **Start** - stand up a new instance from scratch and get one real notification fast. +- **Reschedule** - change times, cadence, or what a skill focuses on (all cron in `aeon.yml` is UTC). +- **Unblock** - work through why a skill "did not run", in order, stopping at the first cause. +- **Chat to skill** - turn what you just did into a scheduled skill. +- **Edit a skill** - change what an existing skill does without breaking its survival machinery. +- **What to turn on** - pick skills, browse packs, install more. +- **Strategy and voice** - set `STRATEGY.md` (the north star) and `soul/` (the tone). +- **Mine history** - surface repeated manual work from past coding-agent chats worth automating. + +## Details + +Aeon runs on the user's own GitHub repo via Actions. A skill is a Markdown file (`skills//SKILL.md`); `aeon.yml` says which ones run and when. Config writes go through the repo's `./aeon` CLI, and everything routes through `gh`, so the skill first confirms `gh` points at the user's instance (not upstream `aeonfun/aeon`) before any write. + +- Repository: https://github.com/aeonfun/aeon +- Homepage: https://aeon.fun + +See [`SKILL.md`](SKILL.md) for the full operator playbook. diff --git a/skills/aeon/SKILL.md b/skills/aeon/SKILL.md new file mode 100644 index 00000000..fd87a78e --- /dev/null +++ b/skills/aeon/SKILL.md @@ -0,0 +1,455 @@ +--- +name: aeon +description: Set up and run an Aeon autonomous-agent instance from your coding agent - get started from scratch, turn skills on or off, schedule or reschedule what runs, edit what a skill does, debug a skill that will not fire, set the STRATEGY.md north star and soul voice, and turn past coding-agent chats into scheduled skills. Use when the user mentions Aeon, aeon.yml, an Aeon skill / instance / routine / pack, or asks to schedule, enable, edit, or debug an agent that runs on a cron. Aeon runs on the user's own GitHub repo via Actions (github.com/aeonfun/aeon). +triggers: +- aeon +- aeon.yml +- aeon instance +- aeon skill +--- + +# Aeon + +Aeon is an agent that runs on the user's own GitHub repo via Actions. A skill is a Markdown file (`skills//SKILL.md`); `aeon.yml` says which ones run and when. + +Pick the mode they're asking for: + +| | | +|---|---| +| **1 · Start** | No instance yet, or set one up from scratch | +| **2 · Reschedule** | Change times, cadence, or what a skill focuses on | +| **3 · Unblock** | "It didn't run" / "nothing happened" | +| **4 · Chat → skill** | Turn what we just did into a scheduled skill | +| **5 · Edit a skill** | Change what an existing skill does | +| **6 · What to turn on** | Pick skills, browse packs, install more | +| **7 · Strategy & voice** | `STRATEGY.md` and `soul/` - the north star and the tone | +| **8 · Mine history → skill** | "What of my repeated work could Aeon do for me?" - surface it from past coding-agent chats | + +## Preflight (every mode) + +1. Find the repo: current dir → `gh repo set-default` → ask. Clone it if it isn't local. +2. **Confirm `gh` points at THEIR instance, before any command that writes.** + + ```bash + gh repo view --json nameWithOwner -q .nameWithOwner + ``` + + If that prints `aeonfun/aeon` and they aren't working on upstream itself, stop and run `gh repo set-default /`. `gh` prefers an `upstream` remote over `origin` when no default is pinned, and every Aeon write (`auth`, `secrets set`, `skills run`, config pushes) is a `gh -R ` call - so it will cheerfully put their API keys on the upstream repo and dispatch runs there. It looks like success: no error, a real run id, and the skill just never fires on their instance. +3. `gh auth status` - everything routes through `gh`. If it fails, tell them to run `gh auth login` and stop. +4. Use the `./aeon` CLI for all config writes. It preserves comments in `aeon.yml` and validates. Never hand-edit the YAML - with one exception: the CLI cannot *create* an entry for a brand-new skill (see Mode 4 step 4). + +**Don't trust "disabled" for a skill you just created.** The read path lists skills from disk and defaults a missing `aeon.yml` entry to `enabled: false`, so "not configured" and "disabled" look identical. One command tells them apart: + +```bash +comm -23 <(ls skills/*/SKILL.md | cut -d/ -f2 | sort) \ + <(grep -oE '^ [a-z0-9-]+:' aeon.yml | tr -d ' :' | sort) +``` + +Anything it prints is on disk but unconfigured. **Orientation - what's installed, what's on, and where everything lives: `references/layout.md`.** + +**Setting any key or token:** read `references/secrets.md` - it has every secret and repo variable with the exact page to get it from. Always set secrets with `./aeon secrets set NAME --stdin`, never as a command argument. + +--- + +## Mode 1 - Start on Aeon + +Goal: one real notification in their phone, fast. Do not configure a schedule first. + +1. **Get a repo. Ask public or private before you run anything** - it changes the command, and switching later means moving the repo. + + **Public** (recommend this): Actions minutes are free, and upstream skill updates arrive with one command. + + ```bash + gh repo fork aeonfun/aeon --clone && cd aeon + gh repo set-default /aeon # REQUIRED - see below + ``` + + **Private**: a fork of a public repo is always public, so a private instance is a mirror, not a fork. + + ```bash + gh repo create --private + git clone --bare https://github.com/aeonfun/aeon.git + git -C aeon.git push --mirror https://github.com//.git + rm -rf aeon.git && git clone https://github.com//.git && cd + git remote add upstream https://github.com/aeonfun/aeon.git + gh repo set-default / # REQUIRED - see below + ``` + + Say both costs out loud before they pick private: Actions minutes bill against the account quota (2,000/mo on Free - scheduled skills burn it), and updates come from `git fetch upstream && git merge upstream/main` instead of `gh repo sync`. + + **Pin the default repo before any other command - both paths.** Both end up with an `upstream` remote (`gh repo fork --clone` adds one for you), and with no default pinned **`gh` prefers `upstream` over `origin`**. Everything in Aeon routes through `gh -R $(gh repo view …)`, so an unpinned checkout silently writes secrets to and dispatches runs against `aeonfun/aeon` instead of their instance - with no error, because the commands genuinely succeed on the wrong repo. Verify: + + ```bash + gh repo view --json nameWithOwner -q .nameWithOwner # must print THEIR repo + ``` + + Everything after this step is identical either way. +2. **Auth a model.** At least one is required. Fastest is `./aeon auth --oauth` (Claude Pro/Max, opens a browser), or `./aeon auth --key `, which detects the provider **from the key prefix** - `sk-ant-oat` (OAuth), `sk-or-` (OpenRouter), `bk_` (Bankr), `inf_` (Surplus), `xai-` (Grok); anything else lands in `ANTHROPIC_API_KEY`. + + **UsePod and Venice keys have no prefix** and are undetectable, so a bare `--key` files them as a plain Anthropic key and the run fails later with a confusing auth error. They must be named: + + ```bash + ./aeon auth --key --provider usepod # same for venice + ``` + + `--dry-run` prints the resolved `method=… → secret …` without calling `gh` or `claude` - worth running whenever the provider is in doubt. + + **Don't assume they have a Claude subscription:** eight providers work, including OpenRouter, Grok, and crypto-settled gateways. See "Providers and harnesses". +3. **Wire one channel.** Telegram is the fastest: create a bot with @BotFather, then `./aeon secrets set TELEGRAM_BOT_TOKEN --stdin` and `TELEGRAM_CHAT_ID`. Skip Discord/Slack/email for now - one channel is enough to prove it works. +4. **Run one skill now.** Pick it with Mode 6 - ask what they want handled, propose one - then `./aeon skills run `. Wait for it, then `./aeon runs logs `. They should get a Telegram message. +5. **Only then, schedule it.** `./aeon skills enable ` and set a time (see Mode 2). + +Good first skills: `digest` (topic briefing), `github-monitor` (their repos), `heartbeat` (already on by default, reports only when something needs attention). + +--- + +## Mode 2 - Reschedule / change the routine + +Show them their day as a **timeline in their own timezone**, not a config file: + +``` +07:00 digest "solana" +09:00 pr-review your repos +18:00 heartbeat health check +``` + +Build it from `./aeon skills ls --enabled --json`. (`--enabled` matters: plain `ls` prints a `SCHEDULE` column for *disabled* skills too - that's their `aeon.yml` entry, not proof anything fires.) No CLI, or want the raw file? `references/layout.md` has grep-only equivalents. Then take plain-language edits and apply them: + +| They say | You do | +|---|---| +| "move the digest to 7am" | `./aeon skills schedule digest "0 6 * * *"` | +| "weekdays only" | `... "0 6 * * 1-5"` | +| "too noisy, twice a week" | `... "0 6 * * 1,4"` | +| "stop the crypto one" | `./aeon skills disable token-movers` | +| "make it about rust instead" | `./aeon skills set digest --var rust` | + +Rules: +- **All cron in `aeon.yml` is UTC.** Convert from their timezone, and say so: "7am Paris = `0 6 * * *` UTC (5am in summer - want it pinned to local time?" There is no local-time option, so if DST matters, tell them which half of the year is off by an hour. +- Confirm back the **next 3 fire times in their timezone** after any change. +- `--dry-run` first on anything ambiguous, show the diff, then apply. +- Changes need a push to take effect. The CLI does it; confirm it landed. +- **Then check the value came out quoted** - one grep, every time: + + ```bash + grep '^ :' aeon.yml + ``` + + The scheduler only reads `schedule: "…"` **with double quotes**. The CLI writes a *new* key unquoted, so an entry that had no `schedule:` yet comes back as `schedule: 0 12 * * *` and the skill is skipped forever. Details below. + +Skills with `schedule: workflow_dispatch` are on-demand only - they never fire on cron. `reactive` ones fire on conditions, not time. + +--- + +## Mode 3 - Unblock + +"It didn't run." Check in this order and stop at the first hit: + +1. **Is it on?** `./aeon skills ls --enabled` - is it listed? +2. **Duplicate key?** `node scripts/validate-config.js`. A repeated skill name in `aeon.yml` silently shadows the first one. Common after hand-edits. +3. **Is it even cron?** `workflow_dispatch` and `reactive` never fire on a schedule. +4. **Are Actions disabled?** `gh api repos/{owner}/{repo}/actions/permissions`. GitHub auto-disables scheduled workflows after 60 days of repo inactivity - this silently kills forks and nothing in Aeon surfaces it. Re-enable in repo Settings. +5. **Is the schedule quoted?** `grep '^ :' aeon.yml` - the value must be `schedule: "0 12 * * *"`, **with double quotes**. + + ``` + schedule: "0 12 * * *" ✅ fires + schedule: 0 12 * * * ❌ never fires, no error anywhere + ``` + + `scheduler.yml` matches schedules with the bash regex `schedule: *"([^"]+)"`. An unquoted value doesn't match, `$SCHED` is empty, and the match loop hits `[ -z "$SCHED" ] && continue` - skipped silently, every tick, forever. + + How it gets that way: the CLI edits `aeon.yml` through a YAML document model that preserves an *existing* quoted node but writes a **newly added** key in plain style. So `./aeon skills schedule "0 12 * * *"` is safe on an entry that already had a quoted `schedule:`, and quietly breaks one that didn't. Same for a first-time `--var`. + + **Nothing else detects this.** The file is valid YAML, `validate-config.js` reports CLEAN, and `./aeon skills ls --enabled` lists the skill with its schedule - because they all parse YAML properly and only the scheduler uses a regex. Fix by adding the quotes by hand. +6. **Did it run and fail?** `./aeon runs ls` then `./aeon runs logs `. A failed skill retries after a 30-minute cooldown. + +Three more, if the above are clean: + +- **It ran against the wrong repo.** The giveaway is a command that reported success with a run id, but `./aeon runs ls` on their instance shows nothing. `gh` prefers `upstream` over `origin` when no default is pinned, so an unpinned checkout sends every write to `aeonfun/aeon`. + + ```bash + gh repo view --json nameWithOwner -q .nameWithOwner # if this isn't their repo: + gh repo set-default / + ``` + + Then **clean up what landed upstream** - re-running against the right repo does not undo it. Any key set while mispointed is now a secret on someone else's repo: + + ```bash + gh secret list -R aeonfun/aeon # timestamps matching the misfire = theirs + ``` + + **Rotate it at the provider first, always** - it sat on a repo whose collaborators can land a workflow that reads it. Then re-set it on their instance with `./aeon secrets set NAME --stdin`. + + **Don't blind-delete it.** `gh secret list` shows only *last-updated*, so it cannot tell you whether the upstream repo already had that secret and the misfire **overwrote** it. Ask before removing: + - Upstream never had it → `gh secret delete -R `. + - Upstream had its own → deleting breaks *their* scheduled runs. The owner must re-set upstream's own value; the overwrite is not reversible from here. + + If the delete 403s, they never had write access - nothing was ever written, and the earlier command failed while only *looking* fine. +- **Missing secret.** Skills declare keys in `requires:`. Check them against `./aeon secrets ls --set`. A missing optional key (`KEY?`) means it degrades quietly, not that it breaks. +- **"No MCP tools available."** On the Claude harness a single unresolved `${VAR}` in `.mcp.json` disables **every** MCP server for that run, not just the broken one (`::warning::.mcp.json references secret(s) not set:` … `Skipping MCP this run.`). Grok degrades per-server instead. If an OAuth server broke a run *after* working, suspect a rotated refresh token that couldn't be saved - `references/mcp.md`. +- **It ran but sent nothing.** That's usually correct. Aeon's convention is silence on no signal - a clean run sends nothing rather than an empty report. + +Note: GitHub only delivers ~10% of `*/5` cron ticks, so the scheduler catches up missed slots for up to 12 hours. A skill firing 40 minutes late is normal. + +--- + +## Mode 4 - Turn this chat into a skill + +They just did something in this chat and want it to happen on a schedule. + +1. **Write the skill file.** `skills//SKILL.md` - frontmatter, then the prompt. Derive it from what actually happened in the session: + - the prompt body = what they asked for, plus the steps that worked + - `mode:` = `read-only` unless it needs to commit or open PRs + - `requires:` = any API key the work hit (`KEY?` if it can degrade without it) + - `category:` = one of `core evolution basics dev crypto productivity` + - if they liked the output, paste a trimmed sample into the body as the format spec + +2. **Fix the three things that break unattended runs:** + - **Nobody's there.** Any point where you asked them a question has to become a default or a rule. + - **Stay silent on nothing.** Add an explicit "if there's nothing worth reporting, log and exit without notifying." Otherwise it gets muted in a week. + - **Don't repeat yesterday.** Add "check the last 3 days of `memory/logs/` and skip anything already reported." + +3. **Check it can actually run there.** No local filesystem, no logged-in tools. If the session read their home directory or used a local MCP server, say so plainly - that part won't work unattended unless it's wired as a repo secret / `.mcp.json`. Wiring an MCP server for unattended use (dashboard Connect, OAuth refresh, the rotating-token PAT): `references/mcp.md`. + +4. **Add the `aeon.yml` entry yourself.** A new skill on disk has no entry, and `./aeon skills enable|schedule` **will not create one** - they only flip entries that already exist, and report `no change - already in that state`, which is false. Add it by hand, disabled, before the fallback `heartbeat:` line: + + ```yaml + my-skill: { enabled: false, schedule: "0 12 * * *" } + ``` + + **Include the quoted `schedule:` even though it's disabled - the quotes are load-bearing.** Writing a bare `{ enabled: false }` and letting `./aeon skills schedule` add the key later produces an *unquoted* value the scheduler cannot read, and the skill never fires (Mode 3, check 5). Seeding a quoted node here means every later CLI edit preserves the quotes. + + Match the inline `{ … }` form the other 61 entries use, on one line. `aeon.yml:367` reads per-skill `model:`/`harness:` overrides with a single-line grep, so an entry split across lines takes the global default instead. + + This is the one sanctioned exception to "never hand-edit the YAML". Validate after: `node scripts/validate-config.js` - but note it only checks structure, and will not catch an unquoted value. + +5. **Regenerate BOTH catalogs, then ship it as a PR.** A new skill trips three CI gates. Run them locally - **nothing blocks a merge on red**, `main` is unprotected and has no rulesets, so an unrun gate just fails after the fact: + + ```bash + bash scripts/check-skill-categories.sh # category is one of the six + bin/generate-skills-json # catalog/skills.json + bin/generate-packs-json # catalog/packs.json - NOT optional + ``` + + `generate-packs-json` is the one everyone forgets: `catalog/skills.json` is itself a trigger path for `ci-packs-json`, so committing the skills catalog without the pack catalog goes red on a workflow you never touched. Commit both files. + + Full gate list, triggers, and the `ci-tests` / `ci-apps` commands: `references/ci.md`. + +6. **Run it once** (`./aeon skills run `), show them the output, then schedule it via Mode 2. + +### Skill file shape + +```yaml +--- +name: my-skill +description: One line - what it does and what it sends. +metadata: + title: My Skill + mode: read-only + category: basics + var: "" + tags: + - content + requires: + - SOME_API_KEY? +--- + +Today is ${today}. + +## Steps +1. + +## Network note + + +## Log +Report via `./notify` (use `./notify -f file.md` for anything multi-line). +Send nothing if there's nothing worth reporting. +Append what you did to `memory/logs/${today}.md` under a `### ` heading. +``` + +Bodies run 133-757 lines (~306 median) - a skill is a prompt in prose, not a config file. `## Steps` / `## Network note` / `## Constraints` / `## Log` is the house shape. + +Four things that bite when authoring - full detail in `references/skill-anatomy.md`: + +- **`requires:` is a least-privilege allowlist - the run exports only the keys named here.** Inline (`requires: [KEY?]`) and block (`- KEY` lines) both parse, top-level or nested under `metadata:`. The catch is the value: only names matching `^[A-Z][A-Z0-9_]{2,}$` (trailing `?` = optional) are injected; a lowercase or malformed entry is silently dropped. +- **A typo'd `mode:` grants write.** Unknown values fall back to `write`, never to the safer tier. The exact string is `read-only`. +- **`${today}` / `${var}` are not templated.** Nothing rewrites `SKILL.md`; the workflow puts the date and var in the surrounding prompt and the model resolves them in context. Inventing `${my_thing}` yields a literal `${my_thing}`. +- **Never put a secret on a command line.** Use `./secretcurl` with a `{ENV_NAME}` placeholder in braces - Claude Code's permission analyzer blocks `$SECRET` expansions at run time. + +Schedules do **not** go in `SKILL.md` - they live in `aeon.yml`. 10 upstream skills carry a `schedule:` or `cron:` frontmatter line anyway; **nothing reads it** (`scheduler.yml` parses `aeon.yml` only). Don't copy that pattern, and don't trust one you find - check `aeon.yml`. + +--- + +## Mode 5 - Change what an existing skill does + +"Make the digest shorter", "stop covering X", "add a source". More common than authoring a new skill. + +**First, check whether it's a config change, not a file edit.** Most skills take a topic, filter, or mode through `var` - read the skill's `var:` line and the comment on its `aeon.yml` entry before touching the body. If `var` covers it, you're done: + +```bash +./aeon skills set digest --var "rust" # no file edit at all +``` + +Otherwise edit `skills//SKILL.md`: + +1. **Read the whole body first.** These files run long (200-750 lines) and carry judgment rules, exit taxonomies, and scoring rubrics that a targeted edit can silently contradict. +2. **Don't strip the survival machinery.** Whatever else changes, the skill must keep: the `./notify` path, the silent-on-no-signal exit, the `memory/logs/${today}.md` append under `### `, and any already-reported dedup. Edits that "tighten" a skill often delete these. The `### ` heading is parsed by the health loop and the dedup rule reads the last 3 days of logs - breaking either makes the skill re-report until it gets muted. Conventions in `references/skill-anatomy.md`. +3. **Update frontmatter if the behaviour moved.** A new data source that needs a key → add it to `requires:`. Now writes files or opens PRs → `mode: write`. Changed `description:`, `name:`, `category:` or `requires:` → regenerate **both** catalogs (`bin/generate-skills-json && bin/generate-packs-json`) and commit both; `skills.json` carries those fields and feeds `packs.json`. See `references/ci.md`. +4. **Warn if it's an upstream skill.** Anything shipped in `aeonfun/aeon` will conflict on the next `git merge upstream/main`. Fine, but say so - the two-repo convention is to keep local edits deliberate and few. +5. **Run it once** (`./aeon skills run `) and read the output before leaving. + +Automated alternative: the in-repo `autoresearch` skill evolves a target skill by generating four scored variations and shipping the winner as a PR. Reach for it when the ask is "make this better" rather than a specific change. + +--- + +## Mode 6 - "What should I turn on?" + +The real first question during onboarding. **Don't dump the catalog.** Ask two or three questions about what they actually want handled while they're away, then propose **three** skills with a one-line reason each. + +Three at a time, not twelve. Every enabled skill is a recurring notification, and the fastest way to kill an instance is to make it noisy on day one. `heartbeat` is already on and stays silent unless something needs attention. + +```bash +./aeon skills ls # all skills - SKILL / ON / SCHEDULE / PACK / DESC +./aeon skills ls --enabled # only what actually runs +./aeon skills ls --pack crypto # one pack +./aeon skills # one skill's detail +./aeon packs ls # the six first-party packs +``` + +`ls` footers with `76 skills · 1 enabled` - read it to them before proposing anything. First run installs the CLI runtime (tsx + yaml, ~12MB); the npm noise is one-time and expected. Grep-only equivalents: `references/layout.md`. + +Packs are a visibility filter, not a runtime switch - revealing one runs nothing. Core (12), Evolution (9) and Basics (18) show by default; Dev (11), Crypto (15) and Productivity (11) are on demand. + +Reasonable starting sets: + +| They care about | Propose | +|---|---| +| Their repos | `github-monitor`, `pr-review`, `changelog` | +| A topic / research | `digest`, `article`, `mention-radar` | +| Markets | `token-movers`, `defi-overview`, `monitor-polymarket` | +| Shipping / traction | `heartbeat`, `shiplog`, `bd-radar` | + +### Installing more + +```bash +bin/install-skill-pack --list # browse the community registry +bin/install-skill-pack / # install a curated pack +bin/add-skill / --list # any repo containing SKILL.md files +``` + +Everything lands **disabled**, security-scanned, with provenance in `skills.lock`. + +**Read a community SKILL.md before enabling it.** Installing a pack means running a stranger's prompt with your secrets injected. The scanner is regex - it can't catch prompt injection. Check that `requires:` matches the stated job, that `capabilities:` is honest, and that nothing instructs the agent to send data somewhere unrelated. + +**Confirm explicitly before enabling anything with real-world blast radius:** `distribute-tokens` (sends USDC), `schedule-ads` (spends money), `send-email` and `vuln-scanner` (contact real people), `deploy-prototype` and `feature` (push to other people's repos). + +--- + +## Mode 7 - Strategy and voice + +Two files that ride in the context of **every** run. Neither is required, both are cheap, and they move output quality more than any per-skill tuning. + +### `STRATEGY.md` - the north star + +Imported into `CLAUDE.md`, so it's in every skill's context: goal, priorities, audience, hard constraints. When a choice isn't otherwise determined, this breaks the tie. Keep it **tight** (it costs tokens on every single run) and **specific** (a vague strategy can't break a tie). + +```bash +./aeon strategy show +./aeon strategy set --file STRATEGY.md +./aeon strategy build "" # dispatches the strategy-builder skill +``` + +`build` reads the brief plus the repo README and `memory/MEMORY.md`, then commits a draft. It runs as an Action, so pull once it finishes. No API key needed. + +### `soul/` - how it sounds + +By default Aeon has no personality. `soul/SOUL.md` (identity, worldview, opinions) and `soul/STYLE.md` (voice, vocabulary, anti-patterns) are read on every run, so notifications and content sound like the operator. `soul/examples/` holds 10-20 calibration samples. + +```bash +./aeon soul show +./aeon soul build --handle --name "" --links +``` + +`XAI_API_KEY` gives the richest read of a real X timeline; without it, `soul-builder` falls back to web search. There's also a gallery of complete example souls at github.com/aeonfun/soul.md to start from. + +**The quality bar: specific enough to be wrong.** *"I think most AI safety discourse is galaxy-brained cope"* is useful. *"I have nuanced views on AI safety"* is not. Push for the first kind - a soul that can't offend anyone won't sound like anyone. + +--- + +## Mode 8 - Mine history for skills to automate + +"What am I doing by hand over and over that Aeon could just do?" Mode 4 turns *this* chat into a skill; Mode 8 mines *past* chats to find which chat is worth turning into one. It reads the operator's local coding-agent transcripts (`~/.claude/projects` or `~/.codex/sessions`), so it only works on their own machine - never inside an Aeon run. + +1. **Scan.** Run the miner from the instance repo root: + + ```bash + node "${PLUGIN_ROOT:-$CLAUDE_PLUGIN_ROOT}/skills/aeon/scripts/mine-history.mjs" --days 45 --top 15 + ``` + + It parses every top-level session in the window (skips subagent sidechains), normalises shell commands to `binary subcommand`, groups session titles, and prints a digest ranked by **distinct sessions × distinct days** - recurrence and cadence, not raw volume. Flags: `--days N` (window, default 120), `--project SUBSTR` (only sessions whose cwd matches - scope to one repo/topic), `--top N`, `--min-sessions N`, `--json`. It has no dependencies and reverts to a clean error if there's no history. Deeper reading of the tables and the candidate rubric: `references/history-mining.md`. + +2. **Read it as a human would.** The digest is raw signal, not a verdict - the judgment is yours: + - **Recurring command workflows** - a `binary subcommand` across many sessions *and* many days is a habit. Universal plumbing (`git status`, `gh auth`, bare `node`/`python3`) is already filtered out, but `gh pr`/`gh api`/`npm run` are substrate too - high everywhere, weak as a skill idea. Look for the *distinctive* recurring call: a named script, a specific CLI (`x-cli`, `langfuse`, `raindrop`), a tight `gh api` pattern. + - **Recurring task themes** - grouped session titles are the strongest signal. A title you've hit across many days at a rough cadence ("check X", "review Y", "digest Z") is almost always the real automation candidate. + - **Tooling / projects** - which MCP servers and repos the work lives in; tells you what a skill would need wired and where to scope `--project`. + +3. **Filter to genuine candidates.** A row is worth proposing only if it's all of: + - **Recurring** - spans several sessions across several days, not one busy afternoon. + - **Fetch/compute/report-shaped** - pulls or checks something and reports. Interactive, decision-heavy, or one-off migration work does *not* automate. + - **Unattended-safe** - no dependence on local files, logged-in desktop apps, or a human answering mid-task (Mode 4 step 2/3 covers hardening). + - **Not already a skill.** Dedup against the instance: `./aeon skills ls`. Much recurring `gh pr` work is already `pr-review`/`pr-check`; a research cadence is already `digest`/`mention-radar`. If an existing skill covers it, the move is Mode 2 (reschedule) or Mode 5 (edit its `var`), **not** a new skill. + +4. **Propose three, with evidence.** Don't dump the digest. Name **three** candidates, each with its recurrence count as proof ("you did X across N sessions over D days"), a one-line skill sketch (what it fetches, what it sends), a suggested `mode:` (`read-only` if it only fetches and reports) and a suggested `schedule:` inferred from the observed cadence (seen ~daily → daily; ~weekly → weekly). Ask which to build. + +5. **Hand off to Mode 4** to author the chosen one - the same skill-file shape, unattended-hardening, quoted-`schedule:` entry, and dual-catalog CI. Mode 8 finds the work; Mode 4 ships it. + +**Privacy:** the transcripts are read locally and only the aggregate digest is surfaced. Don't paste raw prompt bodies or anything sensitive from a session into a channel or a committed file; the counts and titles are enough to decide. + +--- + +## Providers and harnesses + +Two independent axes. Don't confuse them: the **gateway** decides which model answers; the **harness** decides which CLI runs the skill. + +### Gateway - what powers Claude Code + +Set a secret and it's live. `aeon.yml` ships `gateway: { provider: auto }`, which resolves at run time from whichever keys exist, in this priority order: + +``` +claude → anthropic → openrouter → bankr → usepod → venice → surplus → grok +``` + +`direct` is **not** a hop in that chain - it's the placeholder when *none* of the eight secrets is set. It requires nothing and configures nothing, so the run proceeds on whatever `ANTHROPIC_*` env happens to exist and otherwise fails at the first model call. "Resolved to `direct`" in a log means **no key was found**, not that a fallback worked. + +| Provider | Secret | Notes | +|---|---|---| +| Claude subscription | `CLAUDE_CODE_OAUTH_TOKEN` | One-click OAuth, included in Pro/Max | +| Anthropic API | `ANTHROPIC_API_KEY` | Pay-as-you-go | +| OpenRouter | `OPENROUTER_API_KEY` | `sk-or-…` · Anthropic-native passthrough, lowest-risk | +| Bankr | `BANKR_LLM_KEY` | `bk_…` · discounted Opus | +| UsePod | `USEPOD_TOKEN` | No prefix - pass `--provider usepod`. Token sits in the base URL, keep it secret | +| Venice | `VENICE_API_KEY` | No prefix - pass `--provider venice`. Privacy-first, bridged via a sidecar | +| Surplus | `SURPLUS_API_KEY` | `inf_…` · settles USDC on Base - fund the wallet + `approve()` once first | +| Grok (xAI) | `XAI_API_KEY` | `xai-…` · passthrough to `api.x.ai` | + +It runs as a **cascade**, not a single choice: the highest-priority key goes first, and on *any* failure (no credits, rate limit, outage, dud response) the run falls over to the next provider whose key is set. It only errors if every one fails. The log prints `Routing attempt via ''` per hop. + +- **Reorder:** repo variable `GATEWAY_ORDER` (space-separated names). +- **Pin one** (disables failover): `./aeon config set gateway `. +- **Any Anthropic-compatible endpoint:** `ANTHROPIC_API_KEY` plus the repo variable `ANTHROPIC_BASE_URL` - e.g. `https://api.deepseek.com/anthropic`. + +### Harness - which CLI runs the skill + +`claude` (default) or `grok`. The Grok harness runs the `grok` CLI instead of Claude Code and **bypasses the gateway entirely** - it has its own auth. + +- **Set it:** `./aeon config set harness grok` globally, or `harness: "grok"` on a single skill's `aeon.yml` entry - **quoted, on the entry's one inline line**. Per-skill `model:` and `harness:` are read by a single-line grep that requires double quotes (`aeon.yml:367`, `:380`), so an unquoted or line-split override is silently ignored and the skill keeps running the global default - no error, and the log's `model=` line looks normal. After setting either by CLI, re-read the entry and add the quotes if they're missing. +- **Auth:** `XAI_API_KEY`, or an X account (SuperGrok / X Premium+) via the dashboard's **Connect X account**, which stores `GROK_CREDENTIALS`. There is no CLI flag for the X OAuth flow - send them to `./aeon` (the dashboard) for that one. +- **Models:** `grok-4.5` (default, reasoning) or `grok-composer-2.5-fast` (cheap). +- **No free tier.** + +Tell them up front: +- Grok runs report **0 tokens** - its JSON carries no token counts, so cost tracking reads blank. Not a bug. +- The X OAuth session expires. If unattended runs start failing on auth, reconnect. +- `mode: read-only` still applies (maps to `--sandbox read-only`), and MCP works. + +Per-skill grok knobs, in `SKILL.md` frontmatter (ignored on the Claude harness): `max_turns` (default 60), `best_of_n`, `verify`, and `effort` (`low|medium|high|xhigh|max` - reasoning models only; `grok-composer-2.5-fast` rejects it). diff --git a/skills/index.js b/skills/index.js index c7bc3a61..db7ef4d9 100644 --- a/skills/index.js +++ b/skills/index.js @@ -23,6 +23,18 @@ export const SKILLS_CATALOG = [ "category": "agent-authoring", "defaultEnabled": true }, + { + "name": "aeon", + "description": "Set up and run an Aeon autonomous-agent instance from your coding agent - get started from scratch, turn skills on or off, schedule or reschedule what runs, edit what a skill does, debug a skill that will not fire, set the STRATEGY.md north star and soul voice, and turn past coding-agent chats into scheduled skills. Use when the user mentions Aeon, aeon.yml, an Aeon skill / instance / routine / pack, or asks to schedule, enable, edit, or debug an agent that runs on a cron. Aeon runs on the user's own GitHub repo via Actions (github.com/aeonfun/aeon).", + "triggers": [ + "aeon", + "aeon.yml", + "aeon instance", + "aeon skill" + ], + "content": "# Aeon\n\nAeon is an agent that runs on the user's own GitHub repo via Actions. A skill is a Markdown file (`skills//SKILL.md`); `aeon.yml` says which ones run and when.\n\nPick the mode they're asking for:\n\n| | |\n|---|---|\n| **1 · Start** | No instance yet, or set one up from scratch |\n| **2 · Reschedule** | Change times, cadence, or what a skill focuses on |\n| **3 · Unblock** | \"It didn't run\" / \"nothing happened\" |\n| **4 · Chat → skill** | Turn what we just did into a scheduled skill |\n| **5 · Edit a skill** | Change what an existing skill does |\n| **6 · What to turn on** | Pick skills, browse packs, install more |\n| **7 · Strategy & voice** | `STRATEGY.md` and `soul/` - the north star and the tone |\n| **8 · Mine history → skill** | \"What of my repeated work could Aeon do for me?\" - surface it from past coding-agent chats |\n\n## Preflight (every mode)\n\n1. Find the repo: current dir → `gh repo set-default` → ask. Clone it if it isn't local.\n2. **Confirm `gh` points at THEIR instance, before any command that writes.**\n\n ```bash\n gh repo view --json nameWithOwner -q .nameWithOwner\n ```\n\n If that prints `aeonfun/aeon` and they aren't working on upstream itself, stop and run `gh repo set-default /`. `gh` prefers an `upstream` remote over `origin` when no default is pinned, and every Aeon write (`auth`, `secrets set`, `skills run`, config pushes) is a `gh -R ` call - so it will cheerfully put their API keys on the upstream repo and dispatch runs there. It looks like success: no error, a real run id, and the skill just never fires on their instance.\n3. `gh auth status` - everything routes through `gh`. If it fails, tell them to run `gh auth login` and stop.\n4. Use the `./aeon` CLI for all config writes. It preserves comments in `aeon.yml` and validates. Never hand-edit the YAML - with one exception: the CLI cannot *create* an entry for a brand-new skill (see Mode 4 step 4).\n\n**Don't trust \"disabled\" for a skill you just created.** The read path lists skills from disk and defaults a missing `aeon.yml` entry to `enabled: false`, so \"not configured\" and \"disabled\" look identical. One command tells them apart:\n\n```bash\ncomm -23 <(ls skills/*/SKILL.md | cut -d/ -f2 | sort) \\\n <(grep -oE '^ [a-z0-9-]+:' aeon.yml | tr -d ' :' | sort)\n```\n\nAnything it prints is on disk but unconfigured. **Orientation - what's installed, what's on, and where everything lives: `references/layout.md`.**\n\n**Setting any key or token:** read `references/secrets.md` - it has every secret and repo variable with the exact page to get it from. Always set secrets with `./aeon secrets set NAME --stdin`, never as a command argument.\n\n---\n\n## Mode 1 - Start on Aeon\n\nGoal: one real notification in their phone, fast. Do not configure a schedule first.\n\n1. **Get a repo. Ask public or private before you run anything** - it changes the command, and switching later means moving the repo.\n\n **Public** (recommend this): Actions minutes are free, and upstream skill updates arrive with one command.\n\n ```bash\n gh repo fork aeonfun/aeon --clone && cd aeon\n gh repo set-default /aeon # REQUIRED - see below\n ```\n\n **Private**: a fork of a public repo is always public, so a private instance is a mirror, not a fork.\n\n ```bash\n gh repo create --private\n git clone --bare https://github.com/aeonfun/aeon.git\n git -C aeon.git push --mirror https://github.com//.git\n rm -rf aeon.git && git clone https://github.com//.git && cd \n git remote add upstream https://github.com/aeonfun/aeon.git\n gh repo set-default / # REQUIRED - see below\n ```\n\n Say both costs out loud before they pick private: Actions minutes bill against the account quota (2,000/mo on Free - scheduled skills burn it), and updates come from `git fetch upstream && git merge upstream/main` instead of `gh repo sync`.\n\n **Pin the default repo before any other command - both paths.** Both end up with an `upstream` remote (`gh repo fork --clone` adds one for you), and with no default pinned **`gh` prefers `upstream` over `origin`**. Everything in Aeon routes through `gh -R $(gh repo view …)`, so an unpinned checkout silently writes secrets to and dispatches runs against `aeonfun/aeon` instead of their instance - with no error, because the commands genuinely succeed on the wrong repo. Verify:\n\n ```bash\n gh repo view --json nameWithOwner -q .nameWithOwner # must print THEIR repo\n ```\n\n Everything after this step is identical either way.\n2. **Auth a model.** At least one is required. Fastest is `./aeon auth --oauth` (Claude Pro/Max, opens a browser), or `./aeon auth --key `, which detects the provider **from the key prefix** - `sk-ant-oat` (OAuth), `sk-or-` (OpenRouter), `bk_` (Bankr), `inf_` (Surplus), `xai-` (Grok); anything else lands in `ANTHROPIC_API_KEY`.\n\n **UsePod and Venice keys have no prefix** and are undetectable, so a bare `--key` files them as a plain Anthropic key and the run fails later with a confusing auth error. They must be named:\n\n ```bash\n ./aeon auth --key --provider usepod # same for venice\n ```\n\n `--dry-run` prints the resolved `method=… → secret …` without calling `gh` or `claude` - worth running whenever the provider is in doubt.\n\n **Don't assume they have a Claude subscription:** eight providers work, including OpenRouter, Grok, and crypto-settled gateways. See \"Providers and harnesses\".\n3. **Wire one channel.** Telegram is the fastest: create a bot with @BotFather, then `./aeon secrets set TELEGRAM_BOT_TOKEN --stdin` and `TELEGRAM_CHAT_ID`. Skip Discord/Slack/email for now - one channel is enough to prove it works.\n4. **Run one skill now.** Pick it with Mode 6 - ask what they want handled, propose one - then `./aeon skills run `. Wait for it, then `./aeon runs logs `. They should get a Telegram message.\n5. **Only then, schedule it.** `./aeon skills enable ` and set a time (see Mode 2).\n\nGood first skills: `digest` (topic briefing), `github-monitor` (their repos), `heartbeat` (already on by default, reports only when something needs attention).\n\n---\n\n## Mode 2 - Reschedule / change the routine\n\nShow them their day as a **timeline in their own timezone**, not a config file:\n\n```\n07:00 digest \"solana\"\n09:00 pr-review your repos\n18:00 heartbeat health check\n```\n\nBuild it from `./aeon skills ls --enabled --json`. (`--enabled` matters: plain `ls` prints a `SCHEDULE` column for *disabled* skills too - that's their `aeon.yml` entry, not proof anything fires.) No CLI, or want the raw file? `references/layout.md` has grep-only equivalents. Then take plain-language edits and apply them:\n\n| They say | You do |\n|---|---|\n| \"move the digest to 7am\" | `./aeon skills schedule digest \"0 6 * * *\"` |\n| \"weekdays only\" | `... \"0 6 * * 1-5\"` |\n| \"too noisy, twice a week\" | `... \"0 6 * * 1,4\"` |\n| \"stop the crypto one\" | `./aeon skills disable token-movers` |\n| \"make it about rust instead\" | `./aeon skills set digest --var rust` |\n\nRules:\n- **All cron in `aeon.yml` is UTC.** Convert from their timezone, and say so: \"7am Paris = `0 6 * * *` UTC (5am in summer - want it pinned to local time?\" There is no local-time option, so if DST matters, tell them which half of the year is off by an hour.\n- Confirm back the **next 3 fire times in their timezone** after any change.\n- `--dry-run` first on anything ambiguous, show the diff, then apply.\n- Changes need a push to take effect. The CLI does it; confirm it landed.\n- **Then check the value came out quoted** - one grep, every time:\n\n ```bash\n grep '^ :' aeon.yml\n ```\n\n The scheduler only reads `schedule: \"…\"` **with double quotes**. The CLI writes a *new* key unquoted, so an entry that had no `schedule:` yet comes back as `schedule: 0 12 * * *` and the skill is skipped forever. Details below.\n\nSkills with `schedule: workflow_dispatch` are on-demand only - they never fire on cron. `reactive` ones fire on conditions, not time.\n\n---\n\n## Mode 3 - Unblock\n\n\"It didn't run.\" Check in this order and stop at the first hit:\n\n1. **Is it on?** `./aeon skills ls --enabled` - is it listed?\n2. **Duplicate key?** `node scripts/validate-config.js`. A repeated skill name in `aeon.yml` silently shadows the first one. Common after hand-edits.\n3. **Is it even cron?** `workflow_dispatch` and `reactive` never fire on a schedule.\n4. **Are Actions disabled?** `gh api repos/{owner}/{repo}/actions/permissions`. GitHub auto-disables scheduled workflows after 60 days of repo inactivity - this silently kills forks and nothing in Aeon surfaces it. Re-enable in repo Settings.\n5. **Is the schedule quoted?** `grep '^ :' aeon.yml` - the value must be `schedule: \"0 12 * * *\"`, **with double quotes**.\n\n ```\n schedule: \"0 12 * * *\" ✅ fires\n schedule: 0 12 * * * ❌ never fires, no error anywhere\n ```\n\n `scheduler.yml` matches schedules with the bash regex `schedule: *\"([^\"]+)\"`. An unquoted value doesn't match, `$SCHED` is empty, and the match loop hits `[ -z \"$SCHED\" ] && continue` - skipped silently, every tick, forever.\n\n How it gets that way: the CLI edits `aeon.yml` through a YAML document model that preserves an *existing* quoted node but writes a **newly added** key in plain style. So `./aeon skills schedule \"0 12 * * *\"` is safe on an entry that already had a quoted `schedule:`, and quietly breaks one that didn't. Same for a first-time `--var`.\n\n **Nothing else detects this.** The file is valid YAML, `validate-config.js` reports CLEAN, and `./aeon skills ls --enabled` lists the skill with its schedule - because they all parse YAML properly and only the scheduler uses a regex. Fix by adding the quotes by hand.\n6. **Did it run and fail?** `./aeon runs ls` then `./aeon runs logs `. A failed skill retries after a 30-minute cooldown.\n\nThree more, if the above are clean:\n\n- **It ran against the wrong repo.** The giveaway is a command that reported success with a run id, but `./aeon runs ls` on their instance shows nothing. `gh` prefers `upstream` over `origin` when no default is pinned, so an unpinned checkout sends every write to `aeonfun/aeon`.\n\n ```bash\n gh repo view --json nameWithOwner -q .nameWithOwner # if this isn't their repo:\n gh repo set-default /\n ```\n\n Then **clean up what landed upstream** - re-running against the right repo does not undo it. Any key set while mispointed is now a secret on someone else's repo:\n\n ```bash\n gh secret list -R aeonfun/aeon # timestamps matching the misfire = theirs\n ```\n\n **Rotate it at the provider first, always** - it sat on a repo whose collaborators can land a workflow that reads it. Then re-set it on their instance with `./aeon secrets set NAME --stdin`.\n\n **Don't blind-delete it.** `gh secret list` shows only *last-updated*, so it cannot tell you whether the upstream repo already had that secret and the misfire **overwrote** it. Ask before removing:\n - Upstream never had it → `gh secret delete -R `.\n - Upstream had its own → deleting breaks *their* scheduled runs. The owner must re-set upstream's own value; the overwrite is not reversible from here.\n\n If the delete 403s, they never had write access - nothing was ever written, and the earlier command failed while only *looking* fine.\n- **Missing secret.** Skills declare keys in `requires:`. Check them against `./aeon secrets ls --set`. A missing optional key (`KEY?`) means it degrades quietly, not that it breaks.\n- **\"No MCP tools available.\"** On the Claude harness a single unresolved `${VAR}` in `.mcp.json` disables **every** MCP server for that run, not just the broken one (`::warning::.mcp.json references secret(s) not set:` … `Skipping MCP this run.`). Grok degrades per-server instead. If an OAuth server broke a run *after* working, suspect a rotated refresh token that couldn't be saved - `references/mcp.md`.\n- **It ran but sent nothing.** That's usually correct. Aeon's convention is silence on no signal - a clean run sends nothing rather than an empty report.\n\nNote: GitHub only delivers ~10% of `*/5` cron ticks, so the scheduler catches up missed slots for up to 12 hours. A skill firing 40 minutes late is normal.\n\n---\n\n## Mode 4 - Turn this chat into a skill\n\nThey just did something in this chat and want it to happen on a schedule.\n\n1. **Write the skill file.** `skills//SKILL.md` - frontmatter, then the prompt. Derive it from what actually happened in the session:\n - the prompt body = what they asked for, plus the steps that worked\n - `mode:` = `read-only` unless it needs to commit or open PRs\n - `requires:` = any API key the work hit (`KEY?` if it can degrade without it)\n - `category:` = one of `core evolution basics dev crypto productivity`\n - if they liked the output, paste a trimmed sample into the body as the format spec\n\n2. **Fix the three things that break unattended runs:**\n - **Nobody's there.** Any point where you asked them a question has to become a default or a rule.\n - **Stay silent on nothing.** Add an explicit \"if there's nothing worth reporting, log and exit without notifying.\" Otherwise it gets muted in a week.\n - **Don't repeat yesterday.** Add \"check the last 3 days of `memory/logs/` and skip anything already reported.\"\n\n3. **Check it can actually run there.** No local filesystem, no logged-in tools. If the session read their home directory or used a local MCP server, say so plainly - that part won't work unattended unless it's wired as a repo secret / `.mcp.json`. Wiring an MCP server for unattended use (dashboard Connect, OAuth refresh, the rotating-token PAT): `references/mcp.md`.\n\n4. **Add the `aeon.yml` entry yourself.** A new skill on disk has no entry, and `./aeon skills enable|schedule` **will not create one** - they only flip entries that already exist, and report `no change - already in that state`, which is false. Add it by hand, disabled, before the fallback `heartbeat:` line:\n\n ```yaml\n my-skill: { enabled: false, schedule: \"0 12 * * *\" }\n ```\n\n **Include the quoted `schedule:` even though it's disabled - the quotes are load-bearing.** Writing a bare `{ enabled: false }` and letting `./aeon skills schedule` add the key later produces an *unquoted* value the scheduler cannot read, and the skill never fires (Mode 3, check 5). Seeding a quoted node here means every later CLI edit preserves the quotes.\n\n Match the inline `{ … }` form the other 61 entries use, on one line. `aeon.yml:367` reads per-skill `model:`/`harness:` overrides with a single-line grep, so an entry split across lines takes the global default instead.\n\n This is the one sanctioned exception to \"never hand-edit the YAML\". Validate after: `node scripts/validate-config.js` - but note it only checks structure, and will not catch an unquoted value.\n\n5. **Regenerate BOTH catalogs, then ship it as a PR.** A new skill trips three CI gates. Run them locally - **nothing blocks a merge on red**, `main` is unprotected and has no rulesets, so an unrun gate just fails after the fact:\n\n ```bash\n bash scripts/check-skill-categories.sh # category is one of the six\n bin/generate-skills-json # catalog/skills.json\n bin/generate-packs-json # catalog/packs.json - NOT optional\n ```\n\n `generate-packs-json` is the one everyone forgets: `catalog/skills.json` is itself a trigger path for `ci-packs-json`, so committing the skills catalog without the pack catalog goes red on a workflow you never touched. Commit both files.\n\n Full gate list, triggers, and the `ci-tests` / `ci-apps` commands: `references/ci.md`.\n\n6. **Run it once** (`./aeon skills run `), show them the output, then schedule it via Mode 2.\n\n### Skill file shape\n\n```yaml\n---\nname: my-skill\ndescription: One line - what it does and what it sends.\nmetadata:\n title: My Skill\n mode: read-only\n category: basics\n var: \"\"\n tags:\n - content\n requires:\n - SOME_API_KEY?\n---\n\nToday is ${today}. \n\n## Steps\n1. \n\n## Network note\n\n\n## Log\nReport via `./notify` (use `./notify -f file.md` for anything multi-line).\nSend nothing if there's nothing worth reporting.\nAppend what you did to `memory/logs/${today}.md` under a `### ` heading.\n```\n\nBodies run 133-757 lines (~306 median) - a skill is a prompt in prose, not a config file. `## Steps` / `## Network note` / `## Constraints` / `## Log` is the house shape.\n\nFour things that bite when authoring - full detail in `references/skill-anatomy.md`:\n\n- **`requires:` is a least-privilege allowlist - the run exports only the keys named here.** Inline (`requires: [KEY?]`) and block (`- KEY` lines) both parse, top-level or nested under `metadata:`. The catch is the value: only names matching `^[A-Z][A-Z0-9_]{2,}$` (trailing `?` = optional) are injected; a lowercase or malformed entry is silently dropped.\n- **A typo'd `mode:` grants write.** Unknown values fall back to `write`, never to the safer tier. The exact string is `read-only`.\n- **`${today}` / `${var}` are not templated.** Nothing rewrites `SKILL.md`; the workflow puts the date and var in the surrounding prompt and the model resolves them in context. Inventing `${my_thing}` yields a literal `${my_thing}`.\n- **Never put a secret on a command line.** Use `./secretcurl` with a `{ENV_NAME}` placeholder in braces - Claude Code's permission analyzer blocks `$SECRET` expansions at run time.\n\nSchedules do **not** go in `SKILL.md` - they live in `aeon.yml`. 10 upstream skills carry a `schedule:` or `cron:` frontmatter line anyway; **nothing reads it** (`scheduler.yml` parses `aeon.yml` only). Don't copy that pattern, and don't trust one you find - check `aeon.yml`.\n\n---\n\n## Mode 5 - Change what an existing skill does\n\n\"Make the digest shorter\", \"stop covering X\", \"add a source\". More common than authoring a new skill.\n\n**First, check whether it's a config change, not a file edit.** Most skills take a topic, filter, or mode through `var` - read the skill's `var:` line and the comment on its `aeon.yml` entry before touching the body. If `var` covers it, you're done:\n\n```bash\n./aeon skills set digest --var \"rust\" # no file edit at all\n```\n\nOtherwise edit `skills//SKILL.md`:\n\n1. **Read the whole body first.** These files run long (200-750 lines) and carry judgment rules, exit taxonomies, and scoring rubrics that a targeted edit can silently contradict.\n2. **Don't strip the survival machinery.** Whatever else changes, the skill must keep: the `./notify` path, the silent-on-no-signal exit, the `memory/logs/${today}.md` append under `### `, and any already-reported dedup. Edits that \"tighten\" a skill often delete these. The `### ` heading is parsed by the health loop and the dedup rule reads the last 3 days of logs - breaking either makes the skill re-report until it gets muted. Conventions in `references/skill-anatomy.md`.\n3. **Update frontmatter if the behaviour moved.** A new data source that needs a key → add it to `requires:`. Now writes files or opens PRs → `mode: write`. Changed `description:`, `name:`, `category:` or `requires:` → regenerate **both** catalogs (`bin/generate-skills-json && bin/generate-packs-json`) and commit both; `skills.json` carries those fields and feeds `packs.json`. See `references/ci.md`.\n4. **Warn if it's an upstream skill.** Anything shipped in `aeonfun/aeon` will conflict on the next `git merge upstream/main`. Fine, but say so - the two-repo convention is to keep local edits deliberate and few.\n5. **Run it once** (`./aeon skills run `) and read the output before leaving.\n\nAutomated alternative: the in-repo `autoresearch` skill evolves a target skill by generating four scored variations and shipping the winner as a PR. Reach for it when the ask is \"make this better\" rather than a specific change.\n\n---\n\n## Mode 6 - \"What should I turn on?\"\n\nThe real first question during onboarding. **Don't dump the catalog.** Ask two or three questions about what they actually want handled while they're away, then propose **three** skills with a one-line reason each.\n\nThree at a time, not twelve. Every enabled skill is a recurring notification, and the fastest way to kill an instance is to make it noisy on day one. `heartbeat` is already on and stays silent unless something needs attention.\n\n```bash\n./aeon skills ls # all skills - SKILL / ON / SCHEDULE / PACK / DESC\n./aeon skills ls --enabled # only what actually runs\n./aeon skills ls --pack crypto # one pack\n./aeon skills # one skill's detail\n./aeon packs ls # the six first-party packs\n```\n\n`ls` footers with `76 skills · 1 enabled` - read it to them before proposing anything. First run installs the CLI runtime (tsx + yaml, ~12MB); the npm noise is one-time and expected. Grep-only equivalents: `references/layout.md`.\n\nPacks are a visibility filter, not a runtime switch - revealing one runs nothing. Core (12), Evolution (9) and Basics (18) show by default; Dev (11), Crypto (15) and Productivity (11) are on demand.\n\nReasonable starting sets:\n\n| They care about | Propose |\n|---|---|\n| Their repos | `github-monitor`, `pr-review`, `changelog` |\n| A topic / research | `digest`, `article`, `mention-radar` |\n| Markets | `token-movers`, `defi-overview`, `monitor-polymarket` |\n| Shipping / traction | `heartbeat`, `shiplog`, `bd-radar` |\n\n### Installing more\n\n```bash\nbin/install-skill-pack --list # browse the community registry\nbin/install-skill-pack / # install a curated pack\nbin/add-skill / --list # any repo containing SKILL.md files\n```\n\nEverything lands **disabled**, security-scanned, with provenance in `skills.lock`.\n\n**Read a community SKILL.md before enabling it.** Installing a pack means running a stranger's prompt with your secrets injected. The scanner is regex - it can't catch prompt injection. Check that `requires:` matches the stated job, that `capabilities:` is honest, and that nothing instructs the agent to send data somewhere unrelated.\n\n**Confirm explicitly before enabling anything with real-world blast radius:** `distribute-tokens` (sends USDC), `schedule-ads` (spends money), `send-email` and `vuln-scanner` (contact real people), `deploy-prototype` and `feature` (push to other people's repos).\n\n---\n\n## Mode 7 - Strategy and voice\n\nTwo files that ride in the context of **every** run. Neither is required, both are cheap, and they move output quality more than any per-skill tuning.\n\n### `STRATEGY.md` - the north star\n\nImported into `CLAUDE.md`, so it's in every skill's context: goal, priorities, audience, hard constraints. When a choice isn't otherwise determined, this breaks the tie. Keep it **tight** (it costs tokens on every single run) and **specific** (a vague strategy can't break a tie).\n\n```bash\n./aeon strategy show\n./aeon strategy set --file STRATEGY.md\n./aeon strategy build \"\" # dispatches the strategy-builder skill\n```\n\n`build` reads the brief plus the repo README and `memory/MEMORY.md`, then commits a draft. It runs as an Action, so pull once it finishes. No API key needed.\n\n### `soul/` - how it sounds\n\nBy default Aeon has no personality. `soul/SOUL.md` (identity, worldview, opinions) and `soul/STYLE.md` (voice, vocabulary, anti-patterns) are read on every run, so notifications and content sound like the operator. `soul/examples/` holds 10-20 calibration samples.\n\n```bash\n./aeon soul show\n./aeon soul build --handle --name \"\" --links \n```\n\n`XAI_API_KEY` gives the richest read of a real X timeline; without it, `soul-builder` falls back to web search. There's also a gallery of complete example souls at github.com/aeonfun/soul.md to start from.\n\n**The quality bar: specific enough to be wrong.** *\"I think most AI safety discourse is galaxy-brained cope\"* is useful. *\"I have nuanced views on AI safety\"* is not. Push for the first kind - a soul that can't offend anyone won't sound like anyone.\n\n---\n\n## Mode 8 - Mine history for skills to automate\n\n\"What am I doing by hand over and over that Aeon could just do?\" Mode 4 turns *this* chat into a skill; Mode 8 mines *past* chats to find which chat is worth turning into one. It reads the operator's local coding-agent transcripts (`~/.claude/projects` or `~/.codex/sessions`), so it only works on their own machine - never inside an Aeon run.\n\n1. **Scan.** Run the miner from the instance repo root:\n\n ```bash\n node \"${PLUGIN_ROOT:-$CLAUDE_PLUGIN_ROOT}/skills/aeon/scripts/mine-history.mjs\" --days 45 --top 15\n ```\n\n It parses every top-level session in the window (skips subagent sidechains), normalises shell commands to `binary subcommand`, groups session titles, and prints a digest ranked by **distinct sessions × distinct days** - recurrence and cadence, not raw volume. Flags: `--days N` (window, default 120), `--project SUBSTR` (only sessions whose cwd matches - scope to one repo/topic), `--top N`, `--min-sessions N`, `--json`. It has no dependencies and reverts to a clean error if there's no history. Deeper reading of the tables and the candidate rubric: `references/history-mining.md`.\n\n2. **Read it as a human would.** The digest is raw signal, not a verdict - the judgment is yours:\n - **Recurring command workflows** - a `binary subcommand` across many sessions *and* many days is a habit. Universal plumbing (`git status`, `gh auth`, bare `node`/`python3`) is already filtered out, but `gh pr`/`gh api`/`npm run` are substrate too - high everywhere, weak as a skill idea. Look for the *distinctive* recurring call: a named script, a specific CLI (`x-cli`, `langfuse`, `raindrop`), a tight `gh api` pattern.\n - **Recurring task themes** - grouped session titles are the strongest signal. A title you've hit across many days at a rough cadence (\"check X\", \"review Y\", \"digest Z\") is almost always the real automation candidate.\n - **Tooling / projects** - which MCP servers and repos the work lives in; tells you what a skill would need wired and where to scope `--project`.\n\n3. **Filter to genuine candidates.** A row is worth proposing only if it's all of:\n - **Recurring** - spans several sessions across several days, not one busy afternoon.\n - **Fetch/compute/report-shaped** - pulls or checks something and reports. Interactive, decision-heavy, or one-off migration work does *not* automate.\n - **Unattended-safe** - no dependence on local files, logged-in desktop apps, or a human answering mid-task (Mode 4 step 2/3 covers hardening).\n - **Not already a skill.** Dedup against the instance: `./aeon skills ls`. Much recurring `gh pr` work is already `pr-review`/`pr-check`; a research cadence is already `digest`/`mention-radar`. If an existing skill covers it, the move is Mode 2 (reschedule) or Mode 5 (edit its `var`), **not** a new skill.\n\n4. **Propose three, with evidence.** Don't dump the digest. Name **three** candidates, each with its recurrence count as proof (\"you did X across N sessions over D days\"), a one-line skill sketch (what it fetches, what it sends), a suggested `mode:` (`read-only` if it only fetches and reports) and a suggested `schedule:` inferred from the observed cadence (seen ~daily → daily; ~weekly → weekly). Ask which to build.\n\n5. **Hand off to Mode 4** to author the chosen one - the same skill-file shape, unattended-hardening, quoted-`schedule:` entry, and dual-catalog CI. Mode 8 finds the work; Mode 4 ships it.\n\n**Privacy:** the transcripts are read locally and only the aggregate digest is surfaced. Don't paste raw prompt bodies or anything sensitive from a session into a channel or a committed file; the counts and titles are enough to decide.\n\n---\n\n## Providers and harnesses\n\nTwo independent axes. Don't confuse them: the **gateway** decides which model answers; the **harness** decides which CLI runs the skill.\n\n### Gateway - what powers Claude Code\n\nSet a secret and it's live. `aeon.yml` ships `gateway: { provider: auto }`, which resolves at run time from whichever keys exist, in this priority order:\n\n```\nclaude → anthropic → openrouter → bankr → usepod → venice → surplus → grok\n```\n\n`direct` is **not** a hop in that chain - it's the placeholder when *none* of the eight secrets is set. It requires nothing and configures nothing, so the run proceeds on whatever `ANTHROPIC_*` env happens to exist and otherwise fails at the first model call. \"Resolved to `direct`\" in a log means **no key was found**, not that a fallback worked.\n\n| Provider | Secret | Notes |\n|---|---|---|\n| Claude subscription | `CLAUDE_CODE_OAUTH_TOKEN` | One-click OAuth, included in Pro/Max |\n| Anthropic API | `ANTHROPIC_API_KEY` | Pay-as-you-go |\n| OpenRouter | `OPENROUTER_API_KEY` | `sk-or-…` · Anthropic-native passthrough, lowest-risk |\n| Bankr | `BANKR_LLM_KEY` | `bk_…` · discounted Opus |\n| UsePod | `USEPOD_TOKEN` | No prefix - pass `--provider usepod`. Token sits in the base URL, keep it secret |\n| Venice | `VENICE_API_KEY` | No prefix - pass `--provider venice`. Privacy-first, bridged via a sidecar |\n| Surplus | `SURPLUS_API_KEY` | `inf_…` · settles USDC on Base - fund the wallet + `approve()` once first |\n| Grok (xAI) | `XAI_API_KEY` | `xai-…` · passthrough to `api.x.ai` |\n\nIt runs as a **cascade**, not a single choice: the highest-priority key goes first, and on *any* failure (no credits, rate limit, outage, dud response) the run falls over to the next provider whose key is set. It only errors if every one fails. The log prints `Routing attempt via ''` per hop.\n\n- **Reorder:** repo variable `GATEWAY_ORDER` (space-separated names).\n- **Pin one** (disables failover): `./aeon config set gateway `.\n- **Any Anthropic-compatible endpoint:** `ANTHROPIC_API_KEY` plus the repo variable `ANTHROPIC_BASE_URL` - e.g. `https://api.deepseek.com/anthropic`.\n\n### Harness - which CLI runs the skill\n\n`claude` (default) or `grok`. The Grok harness runs the `grok` CLI instead of Claude Code and **bypasses the gateway entirely** - it has its own auth.\n\n- **Set it:** `./aeon config set harness grok` globally, or `harness: \"grok\"` on a single skill's `aeon.yml` entry - **quoted, on the entry's one inline line**. Per-skill `model:` and `harness:` are read by a single-line grep that requires double quotes (`aeon.yml:367`, `:380`), so an unquoted or line-split override is silently ignored and the skill keeps running the global default - no error, and the log's `model=` line looks normal. After setting either by CLI, re-read the entry and add the quotes if they're missing.\n- **Auth:** `XAI_API_KEY`, or an X account (SuperGrok / X Premium+) via the dashboard's **Connect X account**, which stores `GROK_CREDENTIALS`. There is no CLI flag for the X OAuth flow - send them to `./aeon` (the dashboard) for that one.\n- **Models:** `grok-4.5` (default, reasoning) or `grok-composer-2.5-fast` (cheap).\n- **No free tier.**\n\nTell them up front:\n- Grok runs report **0 tokens** - its JSON carries no token counts, so cost tracking reads blank. Not a bug.\n- The X OAuth session expires. If unattended runs start failing on auth, reconnect.\n- `mode: read-only` still applies (maps to `--sandbox read-only`), and MCP works.\n\nPer-skill grok knobs, in `SKILL.md` frontmatter (ignored on the Claude harness): `max_turns` (default 60), `best_of_n`, `verify`, and `effort` (`low|medium|high|xhigh|max` - reasoning models only; `grok-composer-2.5-fast` rejects it).", + "category": "integrations" + }, { "name": "agent-canvas-environment", "description": "Work effectively inside a local Agent Canvas environment, including local agent-server auth, frontend/backend port discovery, safe workspace hygiene, and delegating work to a new local conversation through POST /api/conversations.",