| type | Skill |
| name | Create Skill |
| category | evolution |
| description | Generate a complete new skill from a one-line prompt and ship it as a PR |
| var | |
| tags | ["dev","meta"] |
${var} — A natural-language description of the skill to create. Required. Example: "monitor Hacker News for AI papers and send a summary" or "track gas prices on Ethereum and alert when below 10 gwei".
If ${var} is empty, exit CREATE_SKILL_NO_VAR:
./notify "create-skill aborted: var empty — pass a description e.g. \"monitor X for Y\""
Then stop.
Today is ${today}. Your task is to generate a complete, production-ready skill from ${var}, score it against a quality bar, and ship it as a PR — never commit directly to main.
Steps
-
Parse the request. Extract from ${var}:
- Core action verb (monitor, fetch, generate, analyze, alert, track, scan, etc.)
- Data source(s) — APIs, websites, RSS, on-chain, GitHub, etc.
- Output format — notification, article, file, PR, dashboard, etc.
- Configurable parameter(s) the new skill will accept via its own
${var}
- Suggested cadence (daily, hourly, weekly, on-demand)
Save a one-paragraph structured request summary; you'll use it in the PR body.
-
Duplicate detection (deep — not just ls). Find functional overlap, not just name collision.
keywords=$(echo "${var}" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{4,}' \
| grep -vE '^(send|with|from|that|this|when|each|into|over|some|like|just|than|then|also|will|have|been|using|monitor|track|fetch|alert)$' \
| sort -u)
for kw in $keywords; do
grep -liE "$kw" skills/*/SKILL.md | head -5
done
Read the top 3 candidates fully. For each, judge: does it already do this? Could the request be solved by running an existing skill with a different var=?
- Near-duplicate exists → exit
CREATE_SKILL_DUPLICATE. Notify with the existing skill name and a one-line suggestion ("use existing {skill} with var={...} instead"). Stop.
- Functionally adjacent → design the new skill to complement (different angle/cadence/output). Document the boundary in the PR body.
-
Research the data sources. For every API or data source the new skill needs:
- WebSearch for the current API documentation. Cross-check against a secondary source when feasible (a recent GitHub repo using it, an official changelog, or a Stack Overflow answer dated ≥2026) to confirm the endpoint isn't deprecated.
9b. Dry-run gate (blocks a broken generated skill from auto-merge). Before opening the PR, execute the new skill once with synthetic secrets, so a generated skill never reaches production having only ever run with real credentials:
bash DRYRUN_VERDICT="output/.dry-run/$name.json" bash scripts/dry-run.sh run "$name" || true
- The script self-checks the SKILL_DRYRUN repo variable (default on) and returns a skipped verdict when it is 0.
- Read output/.dry-run/$name.json. passed: true (or skipped: true) means continue. passed: false means delete skills/$name/, revert the aeon.yml edit, and exit CREATE_SKILL_DRYRUN_FAILED with a notify listing the verdict reasons[]. Do not open the PR.
- Put the verdict JSON in the PR body under a ## Dry-run section either way, so a reviewer sees the gate ran.
The gate is structural (exit 0, non-empty output, no write outside the declared mode, no secret outside requires:), and no real credential is ever placed in the run's environment. It does not re-score content; the Haiku scorer already does that.
-
Open as a PR (never commit to main).
name="{skill-name}"
git checkout -b create-skill/$name
git add skills/$name/SKILL.md aeon.yml
git commit -m "create skill: $name
{one-sentence description}
Generated by create-skill from var: \"{request summary, ≤80 chars}\""
git push -u origin create-skill/$name
gh pr create --title "create skill: $name" --body "$(cat <<'EOF'
## Skill
**Name**: `{skill-name}`
**Description**: {description}
**Tags**: {tags}
**Schedule**: `{cron}` (disabled by default)
**Model**: {model}
**Var**: {var-doc}
## Request
${var}
## Sources researched
- {URL 1}
- {URL 2}
- {URL 3}
## Required secrets
{list of NEW_SECRET_REQUIRED env vars OR "None — uses existing secrets"}
## Quality scores
| Criterion | Score |
|-----------|-------|
| Frontmatter | X/5 |
| Var doc | X/5 |
| API calls | X/5 |
| Fallback behavior | X/5 |
| Output spec | X/5 |
| Network note | X/5 |
## Trigger manually
Workflow dispatch with `skill={skill-name}` and `var={example-var}`.
EOF
)"
Capture the PR URL.
-
Log. Append to memory/logs/${today}.md:
### create-skill
- Request: {var, ≤80 chars}
- Created: skills/{skill-name}/SKILL.md
- Registered in aeon.yml: schedule={cron}, model={model}
- Required secrets: {list or "none"}
- Quality scores: F/V/A/Fb/O/N = X/X/X/X/X/X
- PR: {url}
- Exit: CREATE_SKILL_OK (or CREATE_SKILL_NEW_SECRET_REQUIRED)
-
Notify. Send via ./notify:
*create-skill — {skill-name}*
{one-line description}
Schedule: `{cron}` (disabled by default)
{Required secrets line if any}
PR: {url}
Trigger: dispatch skill=`{skill-name}` var=`{example}`
Exit taxonomy
| Code | When | Action |
|---|
CREATE_SKILL_OK | New skill created, validated, PR opened | Notify with PR link |
CREATE_SKILL_NEW_SECRET_REQUIRED | Same as OK plus operator must add a new secret before enabling | Notify with PR link + secret call-out |
CREATE_SKILL_NO_VAR | ${var} empty | Notify abort reason; stop |
CREATE_SKILL_DUPLICATE | Existing skill covers the request | Notify with existing-skill suggestion; stop |
CREATE_SKILL_INSUFFICIENT_RESEARCH | Couldn't confirm ≥1 working data source after WebSearch + WebFetch | Notify with what was tried; stop |
CREATE_SKILL_VALIDATION_FAILED | Quality enforcement or post-write checks failed | Delete partial files; revert aeon.yml; notify with failed criteria; stop |
CREATE_SKILL_DRYRUN_FAILED | The dry-run gate (step 9b) returned passed: false | Delete partial files; revert aeon.yml; notify with verdict reasons; do NOT open the PR; stop |
Network note
There is no network sandbox — curl works, with WebFetch as the fallback for a flaky public GET during research. For an auth'd API the new skill will call, route it through ./secretcurl with a {ENV_NAME} placeholder (the key injected via the skill's requires:), and gh api for GitHub. Irreversible side-effects (email, spend, on-chain writes, deploys) run in-run via ./secretcurl as the skill's final, fail-closed action — there is no deferred/postprocess step, and never defer a read (see CLAUDE.md).
Constraints
- Never commit a generated skill directly to
main. Always open a PR.
- Never enable a generated skill in
aeon.yml (enabled: false always — operator decides).
- Never add an API key/secret to the workflow that isn't already there. Surface as
NEW_SECRET_REQUIRED and document in the PR body.
- Never ship a skill that fails validation. Aborting cleanly is always better than shipping broken.
- Never overwrite an existing
skills/{name}/SKILL.md — name collisions are blocking errors.