Skip to main content
ClaudeWave
Skill714 repo starsupdated 2d ago

schedule-ads

Manage paid ads on AdManage.ai from declarative config - default schedules launches across Meta/TikTok/Snapchat/Pinterest/LinkedIn (always PAUSED); create provisions Meta campaigns and ad sets.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/aeonfun/aeon /tmp/schedule-ads && cp -r /tmp/schedule-ads/skills/schedule-ads ~/.claude/skills/schedule-ads
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

> **${var}** selects the flow. Empty/unset = **schedule** (launch ads into existing ad sets). `create` = **create-campaign** (provision Meta campaigns + ad sets). Both are config-driven, PAUSED-by-default, and make the AdManage API calls **in-run** via `./secretcurl` (the `{ADMANAGE_API_KEY}` placeholder keeps the key off the command line), behind fail-closed spend guardrails.

Reads a declarative config, computes what to do, and makes the AdManage.ai API calls **in-run** via `./secretcurl`. The calls are an irreversible outbound side-effect (real ad spend), so they are each branch's **final** actions and run only behind the guardrails below (PAUSED-by-default, `dailySpendCap` circuit-breaker, dry-run). `ADMANAGE_API_KEY` is injected in-run via this skill's `requires:` — always write it as the `{ADMANAGE_API_KEY}` placeholder, never a bare `$ADMANAGE_API_KEY` (the Bash permission layer refuses that).

## Preamble (both branches)

1. Read `memory/MEMORY.md` for context. Read the last ~3 days of `memory/logs/` for recent launch / provisioning activity — don't re-report a signal already logged.
2. Parse `${var}`:
   - empty / unset → run the **Schedule branch** below.
   - `create` → run the **Create branch** below.
   - anything else → log `SCHEDULE_ADS_UNKNOWN_SELECTOR: <value>` and exit cleanly (no notify).
3. Both branches spend real money on ad platforms. The shared safety posture (see each branch) is: PAUSED by default, config-only (never invent campaigns/creative/targeting), dry-run available, and exit silently when there's nothing to do.

---

# Schedule branch (default — empty `${var}`)

Reads `skills/schedule-ads/config.yaml`, picks schedule entries matching today, and launches those ads **in-run** via AdManage.ai (`POST /v1/launch` through `./secretcurl`), behind the spend guardrails below.

## Safety defaults (schedule)

This branch **spends real money on ad platforms**. Guardrails, in priority order:

1. **PAUSED by default.** Every launch request sets the entity to PAUSED. The operator has to resume manually in the AdManage dashboard before spend starts. `launchPaused: false` in config is the explicit opt-out.
2. **Daily spend cap.** Before launching, the branch checks `GET /v1/spend/daily` for today. If spend ≥ `dailySpendCap` in the config, all launches are skipped and a warning is notified. If the spend figure can't be verified (malformed / empty response), **fail closed** — skip and notify, don't launch. This is a circuit breaker, not a budget enforcer — platform budgets still apply.
3. **Dry-run mode.** If `DRY_RUN=true` in env or `dryRun: true` in config, the branch builds the payloads, writes them to `.pending-admanage/dryrun/`, notifies what *would* launch, and exits without calling the API.
4. **Config-only.** The branch does not invent campaigns, creative, or targeting. If there's no schedule for today, it exits cleanly with no API calls.
5. **Single source of truth.** All ads/campaigns/targeting live in `config.yaml`. The branch never generates new creative on the fly.

## Network note (schedule)

Launching ads is an irreversible outbound side-effect (real ad spend), so it is the branch's **final** action and runs only after the guardrails above pass:

- Auth'd calls go through `./secretcurl` with the `{ADMANAGE_API_KEY}` placeholder — never a bare `$ADMANAGE_API_KEY` (the Bash permission layer refuses that). `ADMANAGE_API_KEY` is injected in-run via `requires:`.
- The branch checks the daily spend cap (`GET /v1/spend/daily`), then per batch calls `POST /v1/launch`, polls `GET /v1/batch-status/{id}` to a terminal state, and reports via `./notify`.
- If `ADMANAGE_API_KEY` is unset, or the launch/spend call fails, skip the launch and notify — do not retry blindly. There is no deferred postprocess fallback.

## Steps (schedule)

1. **Load config.** Read `skills/schedule-ads/config.yaml`. If the file doesn't exist, log `SCHEDULE_ADS_NOT_CONFIGURED` and exit cleanly (no notify, no error). The example template lives next to this file as `config.example.yaml`.

2. **Validate config shape.** Required top-level keys: `defaults` (with `adAccountId`, `workspaceId`, `page`), and `schedules` (array). If either is missing, file an issue in `memory/issues/` per the CLAUDE.md issue tracker convention, notify once, and exit.

3. **Pick today's schedule entries.** For each entry in `schedules`, match against today's date:
   - `when.everyDay: true` → always matches.
   - `when.dayOfWeek: monday` (or any weekday name, lowercase) → matches if today is that weekday (UTC).
   - `when.date: "2026-04-25"` → matches only on that exact date.
   - `when.dates: ["2026-04-25", "2026-05-02"]` → matches if today is in the list.
   - `when.cron: "0 8 * * 1"` → (advanced) matches if today satisfies the cron. Optional — skip if it's too much parsing effort.

   If no entries match today, log `SCHEDULE_ADS_NOTHING_TODAY` and exit cleanly (no notify).

4. **Build launch payloads.** For each matching schedule entry, construct the AdManage `POST /v1/launch` body:
   ```json
   {
     "ads": [
       {
         "adName": "<templated from ad.adName, {date} replaced>",
         "adAccountId": "<from defaults or entry override>",
         "workspaceId": "<from defaults or entry override>",
         "title": "<from ad>",
         "description": "<from ad>",
         "cta": "<from ad or defaults.cta>",
         "link": "<from ad>",
         "page": "<from defaults>",
         "insta": "<from defaults, Meta only>",
         "adSets": [ { "value": "<id>", "label": "<name>" } ],
         "media": [ { "url": "<media url>" } ],
         "status": "PAUSED"
       }
     ]
   }
   ```
   Enforce `status: PAUSED` on every ad unless `defaults.launchPaused` is explicitly `false`. Never strip it silently.

   Template substitutions inside string fields:
   - `{date}` → today's ISO date (YYYY-MM-DD)
   - `{dateHuman}` → "April 21, 2026" style

5. **Pre-flight validation.** For each payload:
   - `media[*].url` must be a
aeonSkill

Set up and run an Aeon agent instance — get started from scratch, pick which skills to turn on or install more from packs, reschedule or change what runs, edit what an existing skill does, fix a skill that isn't firing, set the STRATEGY.md north star and soul/ voice, turn a coding-agent chat into a scheduled Aeon skill, and mine past coding-agent conversations for recurring work worth automating as a skill. Use when the user mentions Aeon, aeon.yml, an Aeon skill / instance / routine / pack, asks to schedule, enable, edit, or debug an agent that runs on a cron, or asks what of their repeated/manual work Aeon could take over.

[REPLACE: SKILL_NAME]Skill

Mention/keyword sweep on social platforms for [REPLACE: KEYWORDS] — trends, sentiment, top posts

action-converterSkill

5 concrete real-life actions, leverage-scored against open loops with specificity and anti-fluff gates

aeon-doctorSkill

Static config-correctness linter for this instance - catches the silent-failure class (unquoted schedules, duplicate keys, unconfigured skills, mode typos, broken requires/MCP refs) that no run-based health skill can see. Notifies only on problems.

aeon-updateSkill

Pull framework updates from the upstream Aeon repo into this instance - 3-way merges canon's new commits into a PR, never clobbering operator config.

articleSkill

Write a publication-ready article in one of three angles - a trending long-form piece, a watched-repo thesis, or a project-through-a-lens essay. Optional Replicate hero image with --visual.

auto-mergeSkill

Automatically merge open PRs that have passing CI, no blocking reviews, and no conflicts

auto-workflowSkill

Two-mode aeon.yml workflow builder - analyze inspects URLs and emits a tiered, signal-verified skill-enablement plan plus an aeon.yml diff; enable flips slugs to enabled:true and opens a PR.