${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.
- WebFetch the canonical docs URL — record it as a comment in the SKILL.md and in the PR body's "Sources researched" section.
- Identify exact endpoints, required headers, auth scheme, response schema, and rate limits.
- Note every required environment variable / API key.
- Determine fallback strategy when an optional API key isn't set (WebSearch / WebFetch / cached data / public endpoint).
Research bar (soft): at least one confirmed source URL or exemplar (a working docs page or a public repo using the API). If none, do not hard-abort — log CREATE_SKILL_INSUFFICIENT_RESEARCH, ask the operator via ./notify with what was tried and why each source failed, and stop. The operator can re-dispatch with a clearer prompt or a source hint.
New-secret guard. Secrets values are never inspectable from the workflow — only names are listed. Use gh api repos/:owner/:repo/actions/secrets --jq '.secrets[].name' to read the names of secrets already configured (this endpoint returns names only, never values). Cross-reference with env-var usage in aeon.yml and existing workflows. For each env var the new skill needs:
- Name present → continue.
- Name missing → record as
NEW_SECRET_REQUIRED. The generated skill must gracefully degrade or skip when the secret is absent (no hard crash). Add a ### Required secrets section to the PR body listing what the operator must add to GitHub Actions secrets before enabling.
If the secret has no graceful fallback, the generated skill's step 1 must do:
if [ -z "$VAR" ]; then ./notify "{skill} skipped: VAR not set"; exit 0; fi
Design the skill. Decide:
- Skill name — lowercase, hyphenated, 2-3 words max (e.g.,
gas-alert, hn-papers). Must not collide with any existing entry under skills/.
- Description — one sentence, starts with a verb, ≤90 chars.
- Tags — pick from:
content, crypto, dev, meta, news, research, social. Max 3.
- Variable behavior — what
${var} controls; what happens when empty (sane default OR clean abort with notify).
- Steps — 4-8 numbered, following the standard pattern: read context → fetch/search → process/analyze → write output → log → notify.
- Schedule suggestion — choose a cron slot. Read existing schedules in
aeon.yml; avoid co-scheduling at the same minute as heavy skills (article, repo-scanner, deep-research, telegram-digest) unless the new skill is lightweight (<30s expected). Prefer a :30 minute offset if the natural hour is already crowded.
- Model — default
claude-opus-4-7. Pick claude-sonnet-4-6 if the skill is high-frequency aggregation/digestion (cost optimization). Document the choice in the PR body.
- Category — the pack the skill joins. Pick one:
research dev crypto onchain-security social productivity meta. (core and fleet are curated in packs.config.json, not chosen here.) If none fits, omit it and the skill lands in the Lab catch-all for later triage. See docs/skill-packs.md.
Write the SKILL.md draft at skills/{skill-name}/SKILL.md with this exact structure:
---
name: {Display Name}
category: {category}
description: {One-sentence description starting with a verb}
var: ""
tags: [{tags}]
---
> **${var}** — {What the variable controls}. {If-empty behavior}.
Today is ${today}. {One sentence describing the task.}
## Steps
1. **{Step title}.** {Specific instructions — endpoints, commands, formats.}
2. **{Step title}.** {More instructions. Code blocks for curl/bash when relevant.}
...
N-1. **Log.** Append to `memory/logs/${today}.md`:
- Skill: {skill-name}
- What was done and key outputs
N. **Notify.** Send via `./notify`:
{Output format template — specify ≤4000 chars, clickable URLs}
## Network note
{How this skill reaches the network — ./secretcurl with an {ENV_NAME} placeholder for auth'd APIs, gh api for GitHub, curl + WebFetch fallback for public}
Hard rules for the generated content:
- Complete
curl commands with proper headers and URL encoding (no pseudo-code).
jq parsing for JSON APIs.
- Notification character limit explicitly stated (under 4000 chars total).
- Every link clickable (full URLs, not placeholders).
- Fallback behavior defined for every optional secret.
- Use only
${var} and ${today} template variables — no other invented variables.
- No TODOs, no placeholders, no "fill in later".
- Mandatory
## Network note section (accurate model — see the ## Network note in this skill for the canonical wording; there is no network sandbox).
Quality enforcement (self-edit pass). Score the draft 1-5 across:
| Criterion |
What to check |
| Frontmatter complete |
name, category, description, var, tags present and well-formed |
| Var doc |
Single > block-quote line; if-empty behavior defined |
| API calls complete |
Curl + headers + jq, not pseudo-code |
| Fallback behavior |
Graceful degradation for every optional secret |
| Output spec |
Char limits, clickable URLs, format template explicit |
| Network note |
Present and matches the auth pattern of the API used (./secretcurl for auth'd, gh api for GitHub, WebFetch fallback for public) |
Any criterion <4 → rewrite that section once. Still <4 after one rewrite → exit CREATE_SKILL_VALIDATION_FAILED with a notify listing failed criteria. Do not ship a low-quality skill.
Post-write validation. Re-read the SKILL.md from disk and verify:
- Frontmatter YAML is parseable; required keys present.
- No literal substring matches:
TODO, FIXME, XXX, placeholder, fill in, lorem, <your-, your_api_key_here, example.com.
- Every
${...} template variable resolves to ${var} or ${today}.
- At least one
./notify invocation appears in the body.
- At least one
memory/logs/${today}.md write appears.
## Network note section exists.
Any failure → delete the partial file and any other writes, exit CREATE_SKILL_VALIDATION_FAILED with a notify listing the failed checks. No partial state.
Register in aeon.yml. Insert the new skill in the appropriate time-slot section:
- Format:
{skill-name}: { enabled: false, schedule: "{suggested_cron}" }
- Add
model: "claude-sonnet-4-6" if chosen in step 5.
- Add
var: "" if the skill takes a default var.
- Add a brief trailing comment if the name doesn't make purpose obvious.
- Place near related skills (crypto with crypto, content with content, etc.).
- Always
enabled: false. Operator decides when to turn it on.
Verify YAML still parses after the edit. If parsing fails, revert the change and exit CREATE_SKILL_VALIDATION_FAILED.
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.
1---2name: create-skill3description: Generate a complete new skill from a one-line prompt and ship it as a PR4---5> **${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"`.67<!-- autoresearch: variation B — sharper output via PR-first workflow + quality enforcement + exit taxonomy + new-secret guard -->89If `${var}` is empty, exit `CREATE_SKILL_NO_VAR`:10```bash11./notify "create-skill aborted: var empty — pass a description e.g. \"monitor X for Y\""12```13Then stop.1415Today 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`**.1617## Steps18191. **Parse the request.** Extract from `${var}`:20 - Core action verb (monitor, fetch, generate, analyze, alert, track, scan, etc.)21 - Data source(s) — APIs, websites, RSS, on-chain, GitHub, etc.22 - Output format — notification, article, file, PR, dashboard, etc.23 - Configurable parameter(s) the new skill will accept via its own `${var}`24 - Suggested cadence (daily, hourly, weekly, on-demand)2526 Save a one-paragraph structured request summary; you'll use it in the PR body.27282. **Duplicate detection (deep — not just `ls`).** Find functional overlap, not just name collision.29 ```bash30 keywords=$(echo "${var}" | tr '[:upper:]' '[:lower:]' | grep -oE '[a-z]{4,}' \31 | 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)$' \32 | sort -u)33 for kw in $keywords; do34 grep -liE "$kw" skills/*/SKILL.md | head -535 done36 ```37 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=`?38 - **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.39 - **Functionally adjacent** → design the new skill to complement (different angle/cadence/output). Document the boundary in the PR body.40413. **Research the data sources.** For every API or data source the new skill needs:42 - **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.43 - **WebFetch** the canonical docs URL — record it as a comment in the SKILL.md and in the PR body's "Sources researched" section.44 - Identify exact endpoints, required headers, auth scheme, response schema, and rate limits.45 - Note every required environment variable / API key.46 - Determine fallback strategy when an optional API key isn't set (WebSearch / WebFetch / cached data / public endpoint).4748 **Research bar (soft):** at least one confirmed source URL or exemplar (a working docs page or a public repo using the API). If none, do **not** hard-abort — log `CREATE_SKILL_INSUFFICIENT_RESEARCH`, ask the operator via `./notify` with what was tried and why each source failed, and stop. The operator can re-dispatch with a clearer prompt or a source hint.49504. **New-secret guard.** Secrets values are never inspectable from the workflow — only names are listed. Use `gh api repos/:owner/:repo/actions/secrets --jq '.secrets[].name'` to read the **names** of secrets already configured (this endpoint returns names only, never values). Cross-reference with env-var usage in `aeon.yml` and existing workflows. For each env var the new skill needs:51 - **Name present** → continue.52 - **Name missing** → record as `NEW_SECRET_REQUIRED`. The generated skill **must** gracefully degrade or skip when the secret is absent (no hard crash). Add a `### Required secrets` section to the PR body listing what the operator must add to GitHub Actions secrets before enabling.5354 If the secret has no graceful fallback, the generated skill's step 1 must do:55 ```bash56 if [ -z "$VAR" ]; then ./notify "{skill} skipped: VAR not set"; exit 0; fi57 ```58595. **Design the skill.** Decide:60 - **Skill name** — lowercase, hyphenated, 2-3 words max (e.g., `gas-alert`, `hn-papers`). Must not collide with any existing entry under `skills/`.61 - **Description** — one sentence, starts with a verb, ≤90 chars.62 - **Tags** — pick from: `content`, `crypto`, `dev`, `meta`, `news`, `research`, `social`. Max 3.63 - **Variable behavior** — what `${var}` controls; what happens when empty (sane default OR clean abort with notify).64 - **Steps** — 4-8 numbered, following the standard pattern: read context → fetch/search → process/analyze → write output → log → notify.65 - **Schedule suggestion** — choose a cron slot. Read existing schedules in `aeon.yml`; avoid co-scheduling at the same minute as heavy skills (article, repo-scanner, deep-research, telegram-digest) unless the new skill is lightweight (<30s expected). Prefer a `:30` minute offset if the natural hour is already crowded.66 - **Model** — default `claude-opus-4-7`. Pick `claude-sonnet-4-6` if the skill is high-frequency aggregation/digestion (cost optimization). Document the choice in the PR body.67 - **Category** — the pack the skill joins. Pick one: `research` `dev` `crypto` `onchain-security` `social` `productivity` `meta`. (`core` and `fleet` are curated in `packs.config.json`, not chosen here.) If none fits, omit it and the skill lands in the **Lab** catch-all for later triage. See `docs/skill-packs.md`.68696. **Write the SKILL.md draft** at `skills/{skill-name}/SKILL.md` with this exact structure:7071 ```markdown72 ---73 name: {Display Name}74 category: {category}75 description: {One-sentence description starting with a verb}76 var: ""77 tags: [{tags}]78 ---79 > **${var}** — {What the variable controls}. {If-empty behavior}.8081 Today is ${today}. {One sentence describing the task.}8283 ## Steps8485 1. **{Step title}.** {Specific instructions — endpoints, commands, formats.}8687 2. **{Step title}.** {More instructions. Code blocks for curl/bash when relevant.}8889 ...9091 N-1. **Log.** Append to `memory/logs/${today}.md`:92 - Skill: {skill-name}93 - What was done and key outputs9495 N. **Notify.** Send via `./notify`:96 {Output format template — specify ≤4000 chars, clickable URLs}9798 ## Network note99100 {How this skill reaches the network — ./secretcurl with an {ENV_NAME} placeholder for auth'd APIs, gh api for GitHub, curl + WebFetch fallback for public}101 ```102103 Hard rules for the generated content:104 - Complete `curl` commands with proper headers and URL encoding (no pseudo-code).105 - `jq` parsing for JSON APIs.106 - Notification character limit explicitly stated (under 4000 chars total).107 - Every link clickable (full URLs, not placeholders).108 - Fallback behavior defined for every optional secret.109 - Use only `${var}` and `${today}` template variables — no other invented variables.110 - No TODOs, no placeholders, no "fill in later".111 - Mandatory `## Network note` section (accurate model — see the `## Network note` in this skill for the canonical wording; there is no network sandbox).1121137. **Quality enforcement (self-edit pass).** Score the draft 1-5 across:114115 | Criterion | What to check |116 |-----------|---------------|117 | Frontmatter complete | `name`, `category`, `description`, `var`, `tags` present and well-formed |118 | Var doc | Single `>` block-quote line; if-empty behavior defined |119 | API calls complete | Curl + headers + jq, not pseudo-code |120 | Fallback behavior | Graceful degradation for every optional secret |121 | Output spec | Char limits, clickable URLs, format template explicit |122 | Network note | Present and matches the auth pattern of the API used (`./secretcurl` for auth'd, `gh api` for GitHub, WebFetch fallback for public) |123124 Any criterion <4 → rewrite that section once. Still <4 after one rewrite → exit `CREATE_SKILL_VALIDATION_FAILED` with a notify listing failed criteria. **Do not ship a low-quality skill.**1251268. **Post-write validation.** Re-read the SKILL.md from disk and verify:127 - Frontmatter YAML is parseable; required keys present.128 - No literal substring matches: `TODO`, `FIXME`, `XXX`, `placeholder`, `fill in`, `lorem`, `<your-`, `your_api_key_here`, `example.com`.129 - Every `${...}` template variable resolves to `${var}` or `${today}`.130 - At least one `./notify` invocation appears in the body.131 - At least one `memory/logs/${today}.md` write appears.132 - `## Network note` section exists.133134 Any failure → delete the partial file and any other writes, exit `CREATE_SKILL_VALIDATION_FAILED` with a notify listing the failed checks. No partial state.1351369. **Register in `aeon.yml`.** Insert the new skill in the appropriate time-slot section:137 - Format: ` {skill-name}: { enabled: false, schedule: "{suggested_cron}" }`138 - Add `model: "claude-sonnet-4-6"` if chosen in step 5.139 - Add `var: ""` if the skill takes a default var.140 - Add a brief trailing comment if the name doesn't make purpose obvious.141 - Place near related skills (crypto with crypto, content with content, etc.).142 - **Always** `enabled: false`. Operator decides when to turn it on.143144 Verify YAML still parses after the edit. If parsing fails, revert the change and exit `CREATE_SKILL_VALIDATION_FAILED`.1451469b. **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:147 ```bash148 DRYRUN_VERDICT="output/.dry-run/$name.json" bash scripts/dry-run.sh run "$name" || true149 ```150 - The script self-checks the `SKILL_DRYRUN` repo variable (default on) and returns a `skipped` verdict when it is `0`.151 - 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.152 - Put the verdict JSON in the PR body under a `## Dry-run` section either way, so a reviewer sees the gate ran.153 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.15415510. **Open as a PR (never commit to `main`).**156 ```bash157 name="{skill-name}"158 git checkout -b create-skill/$name159 git add skills/$name/SKILL.md aeon.yml160 git commit -m "create skill: $name161162 {one-sentence description}163164 Generated by create-skill from var: \"{request summary, ≤80 chars}\""165 git push -u origin create-skill/$name166 gh pr create --title "create skill: $name" --body "$(cat <<'EOF'167 ## Skill168 **Name**: `{skill-name}`169 **Description**: {description}170 **Tags**: {tags}171 **Schedule**: `{cron}` (disabled by default)172 **Model**: {model}173 **Var**: {var-doc}174175 ## Request176 ```177 ${var}178 ```179180 ## Sources researched181 - {URL 1}182 - {URL 2}183 - {URL 3}184185 ## Required secrets186 {list of NEW_SECRET_REQUIRED env vars OR "None — uses existing secrets"}187188 ## Quality scores189 | Criterion | Score |190 |-----------|-------|191 | Frontmatter | X/5 |192 | Var doc | X/5 |193 | API calls | X/5 |194 | Fallback behavior | X/5 |195 | Output spec | X/5 |196 | Network note | X/5 |197198 ## Trigger manually199 Workflow dispatch with `skill={skill-name}` and `var={example-var}`.200 EOF201 )"202 ```203 Capture the PR URL.20420511. **Log.** Append to `memory/logs/${today}.md`:206 ```207 ### create-skill208 - Request: {var, ≤80 chars}209 - Created: skills/{skill-name}/SKILL.md210 - Registered in aeon.yml: schedule={cron}, model={model}211 - Required secrets: {list or "none"}212 - Quality scores: F/V/A/Fb/O/N = X/X/X/X/X/X213 - PR: {url}214 - Exit: CREATE_SKILL_OK (or CREATE_SKILL_NEW_SECRET_REQUIRED)215 ```21621712. **Notify.** Send via `./notify`:218 ```219 *create-skill — {skill-name}*220 {one-line description}221 Schedule: `{cron}` (disabled by default)222 {Required secrets line if any}223 PR: {url}224 Trigger: dispatch skill=`{skill-name}` var=`{example}`225 ```226227## Exit taxonomy228229| Code | When | Action |230|------|------|--------|231| `CREATE_SKILL_OK` | New skill created, validated, PR opened | Notify with PR link |232| `CREATE_SKILL_NEW_SECRET_REQUIRED` | Same as OK plus operator must add a new secret before enabling | Notify with PR link + secret call-out |233| `CREATE_SKILL_NO_VAR` | `${var}` empty | Notify abort reason; stop |234| `CREATE_SKILL_DUPLICATE` | Existing skill covers the request | Notify with existing-skill suggestion; stop |235| `CREATE_SKILL_INSUFFICIENT_RESEARCH` | Couldn't confirm ≥1 working data source after WebSearch + WebFetch | Notify with what was tried; stop |236| `CREATE_SKILL_VALIDATION_FAILED` | Quality enforcement or post-write checks failed | Delete partial files; revert aeon.yml; notify with failed criteria; stop |237| `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 |238239## Network note240241There 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).242243## Constraints244245- **Never** commit a generated skill directly to `main`. Always open a PR.246- **Never** enable a generated skill in `aeon.yml` (`enabled: false` always — operator decides).247- **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.248- **Never** ship a skill that fails validation. Aborting cleanly is always better than shipping broken.249- **Never** overwrite an existing `skills/{name}/SKILL.md` — name collisions are blocking errors.