カーソルAgents · Cursor
The same trick, for Cursor.
Cursor’s agent loads skills from
.cursor/skills/. This one ships two interchangeable helper scripts, Node and Python (standard library only), so it works whichever runtime your project has.Fastest way
Paste one prompt.
Copy this prompt into Cursor's agent. It creates the files below with the exact contents shown on this page.
Preview the prompt
Install the AnimeAPI agent skill in this project. Create these three files exactly:
.cursor/skills/animeapi/SKILL.md
```markdown
---
name: animeapi
description: Generate anime-style images (characters, scenes, sprites, backgrounds, key art) and short anime video clips with AnimeAPI and save them to disk. Use when the user asks for anime or manga style images, animations or visual assets.
---
# AnimeAPI: anime image and video generation
Generate anime-style images (and, when asked, short anime video clips) through
AnimeAPI and save them into the project.
## Setup
`ANIMEAPI_KEY` must be set in the environment the agent runs in (for example
`export ANIMEAPI_KEY=ank_...` in your shell profile). A `.env` file is not
read automatically. Keys: https://animeapi.ai/dashboard/api-keys
## How to generate
Run:
```bash
node .cursor/skills/animeapi/scripts/generate.mjs "<prompt>" <output-file> [model] [orientation]
```
Needs Node 18+. If Node isn't available, the same arguments work with
`python3 .cursor/skills/animeapi/scripts/generate.py` (standard library only).
- `model`: `anime` (default, $0.003, best for characters, tag-based),
`ultra` ($0.01, follows long sentences, renders legible text) or
`ultra-max` ($0.02, highest detail).
- `orientation`: `portrait` (default), `square` or `landscape`.
- Use a `.webp` output path for `anime` and `.png` for the ultra tiers.
- The script prints `enhanced_prompt` (what the model actually read) and the
remaining `balance_usd`.
## Writing prompts
- anime: describe the character first (who, hair, outfit, pose), then the
place and light. Your prompt is rewritten into Danbooru tags automatically.
It is strongly biased toward drawing a person; for animals or objects as the
subject, use ultra.
- ultra / ultra-max: write a full sentence; an anime style is added
automatically. Put any text that must appear in the image in quotes.
## Errors
- 401 INVALID_KEY: ANIMEAPI_KEY is missing or revoked.
- 402 INSUFFICIENT_BALANCE: tell the user to top up at
https://animeapi.ai/dashboard/billing. Do not retry.
- 400 MODERATION_BLOCKED (ultra tiers): rephrase the prompt.
- 500 GENERATION_FAILED: the charge was refunded; one retry is fine.
Each image costs money. Generate one image, show it, and ask before making
batches.
## Videos
Only when the user asks for a video or animation. Videos are MP4 (24 fps, with
sound), 5 to 15 seconds, billed per second up front: $0.016/s at 480p,
$0.04/s at 768p (a 5-second 768p clip is $0.20). State the price and ask
before anything longer than 5 seconds or more than one clip.
1. Start a job (build the JSON with `jq -n` if the prompt has quotes):
```bash
curl -sS https://animeapi.ai/api/videos -H "Authorization: Bearer $ANIMEAPI_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "<what happens>", "resolution": "768p", "duration": 5, "orientation": "landscape"}'
```
It returns 202 with an `id`. Optional `first_frame` / `last_frame`: an
image URL or a `data:image/png;base64,...` data URI (up to 10 MB) that the
clip starts / ends on; the first frame sets the shape. For a local image,
pipe it through jq into a file (too long for a command-line argument):
```bash
base64 < in.webp | tr -d '\n' | jq -Rs --arg p "<what happens>" \
'{prompt: $p, duration: 5, first_frame: ("data:image/webp;base64," + .)}' > /tmp/video.json
curl -sS https://animeapi.ai/api/videos -H "Authorization: Bearer $ANIMEAPI_KEY" \
-H "Content-Type: application/json" -d @/tmp/video.json
```
2. Poll every 5 seconds until `status` is `succeeded` or `failed`:
```bash
curl -sS https://animeapi.ai/api/videos/<id> -H "Authorization: Bearer $ANIMEAPI_KEY"
```
3. On `succeeded`, download `video_url` into the project (`curl -sS -o out.mp4 <video_url>`).
On `failed`, the charge was refunded and `error` says why.
```
.cursor/skills/animeapi/scripts/generate.mjs
```javascript
#!/usr/bin/env node
// Usage: node generate.mjs "<prompt>" <output-file> [model] [orientation]
import { mkdir, writeFile } from "node:fs/promises";
import { dirname } from "node:path";
const [prompt, out, model = "anime", orientation = "portrait"] = process.argv.slice(2);
if (!prompt || !out) {
console.error('usage: node generate.mjs "<prompt>" <output-file> [model] [orientation]');
process.exit(2);
}
if (!process.env.ANIMEAPI_KEY) {
console.error("Set ANIMEAPI_KEY first: https://animeapi.ai/dashboard/api-keys");
process.exit(2);
}
const res = await fetch("https://animeapi.ai/api/generate", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANIMEAPI_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ prompt, model, orientation, allowNSFW: false }),
});
const data = await res.json();
if (!res.ok) {
console.error(`AnimeAPI error ${res.status} ${data.code ?? ""}: ${data.error}`);
process.exit(1);
}
const image = await fetch(data.image_url);
if (!image.ok) {
console.error(`Could not download ${data.image_url} (${image.status})`);
process.exit(1);
}
await mkdir(dirname(out), { recursive: true });
await writeFile(out, Buffer.from(await image.arrayBuffer()));
console.log(`saved: ${out}`);
console.log(`enhanced_prompt: ${data.enhanced_prompt}`);
console.log(`balance_usd: ${data.balance_usd}`);
```
.cursor/skills/animeapi/scripts/generate.py
```python
#!/usr/bin/env python3
"""Usage: python3 generate.py "<prompt>" <output-file> [model] [orientation]
Standard library only.
"""
import json
import os
import sys
import urllib.error
import urllib.request
from pathlib import Path
API_URL = "https://animeapi.ai/api/generate"
UA = {"User-Agent": "animeapi-skill/1.0"}
def main() -> int:
if len(sys.argv) < 3:
print(__doc__, file=sys.stderr)
return 2
prompt, out = sys.argv[1], Path(sys.argv[2])
model = sys.argv[3] if len(sys.argv) > 3 else "anime"
orientation = sys.argv[4] if len(sys.argv) > 4 else "portrait"
key = os.environ.get("ANIMEAPI_KEY")
if not key:
print("Set ANIMEAPI_KEY first: https://animeapi.ai/dashboard/api-keys", file=sys.stderr)
return 2
body = json.dumps(
{"prompt": prompt, "model": model, "orientation": orientation, "allowNSFW": False}
).encode()
req = urllib.request.Request(
API_URL,
data=body,
headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json", **UA},
)
try:
with urllib.request.urlopen(req, timeout=180) as res:
data = json.load(res)
except urllib.error.HTTPError as err:
detail = json.loads(err.read() or b"{}")
print(f"AnimeAPI error {err.code} {detail.get('code', '')}: {detail.get('error')}", file=sys.stderr)
return 1
out.parent.mkdir(parents=True, exist_ok=True)
with urllib.request.urlopen(urllib.request.Request(data["image_url"], headers=UA), timeout=120) as img:
out.write_bytes(img.read())
print(f"saved: {out}")
print(f"enhanced_prompt: {data['enhanced_prompt']}")
print(f"balance_usd: {data['balance_usd']}")
return 0
if __name__ == "__main__":
sys.exit(main())
```→Or install it by hand
Export your key
The agent runs scripts in your terminal environment. A project
.envis not read automatically.~/.zshrc export ANIMEAPI_KEY="ank_your_key_here"Add SKILL.md
The folder name must match
namein the frontmatter. For every project on your machine, use~/.cursor/skills/animeapi/and adjust the script paths..cursor/skills/animeapi/SKILL.md --- name: animeapi description: Generate anime-style images (characters, scenes, sprites, backgrounds, key art) and short anime video clips with AnimeAPI and save them to disk. Use when the user asks for anime or manga style images, animations or visual assets. --- # AnimeAPI: anime image and video generation Generate anime-style images (and, when asked, short anime video clips) through AnimeAPI and save them into the project. ## Setup `ANIMEAPI_KEY` must be set in the environment the agent runs in (for example `export ANIMEAPI_KEY=ank_...` in your shell profile). A `.env` file is not read automatically. Keys: https://animeapi.ai/dashboard/api-keys ## How to generate Run: ```bash node .cursor/skills/animeapi/scripts/generate.mjs "<prompt>" <output-file> [model] [orientation] ``` Needs Node 18+. If Node isn't available, the same arguments work with `python3 .cursor/skills/animeapi/scripts/generate.py` (standard library only). - `model`: `anime` (default, $0.003, best for characters, tag-based), `ultra` ($0.01, follows long sentences, renders legible text) or `ultra-max` ($0.02, highest detail). - `orientation`: `portrait` (default), `square` or `landscape`. - Use a `.webp` output path for `anime` and `.png` for the ultra tiers. - The script prints `enhanced_prompt` (what the model actually read) and the remaining `balance_usd`. ## Writing prompts - anime: describe the character first (who, hair, outfit, pose), then the place and light. Your prompt is rewritten into Danbooru tags automatically. It is strongly biased toward drawing a person; for animals or objects as the subject, use ultra. - ultra / ultra-max: write a full sentence; an anime style is added automatically. Put any text that must appear in the image in quotes. ## Errors - 401 INVALID_KEY: ANIMEAPI_KEY is missing or revoked. - 402 INSUFFICIENT_BALANCE: tell the user to top up at https://animeapi.ai/dashboard/billing. Do not retry. - 400 MODERATION_BLOCKED (ultra tiers): rephrase the prompt. - 500 GENERATION_FAILED: the charge was refunded; one retry is fine. Each image costs money. Generate one image, show it, and ask before making batches. ## Videos Only when the user asks for a video or animation. Videos are MP4 (24 fps, with sound), 5 to 15 seconds, billed per second up front: $0.016/s at 480p, $0.04/s at 768p (a 5-second 768p clip is $0.20). State the price and ask before anything longer than 5 seconds or more than one clip. 1. Start a job (build the JSON with `jq -n` if the prompt has quotes): ```bash curl -sS https://animeapi.ai/api/videos -H "Authorization: Bearer $ANIMEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{"prompt": "<what happens>", "resolution": "768p", "duration": 5, "orientation": "landscape"}' ``` It returns 202 with an `id`. Optional `first_frame` / `last_frame`: an image URL or a `data:image/png;base64,...` data URI (up to 10 MB) that the clip starts / ends on; the first frame sets the shape. For a local image, pipe it through jq into a file (too long for a command-line argument): ```bash base64 < in.webp | tr -d '\n' | jq -Rs --arg p "<what happens>" \ '{prompt: $p, duration: 5, first_frame: ("data:image/webp;base64," + .)}' > /tmp/video.json curl -sS https://animeapi.ai/api/videos -H "Authorization: Bearer $ANIMEAPI_KEY" \ -H "Content-Type: application/json" -d @/tmp/video.json ``` 2. Poll every 5 seconds until `status` is `succeeded` or `failed`: ```bash curl -sS https://animeapi.ai/api/videos/<id> -H "Authorization: Bearer $ANIMEAPI_KEY" ``` 3. On `succeeded`, download `video_url` into the project (`curl -sS -o out.mp4 <video_url>`). On `failed`, the charge was refunded and `error` says why.Add the helper scripts
.cursor/skills/animeapi/scripts/generate.mjs #!/usr/bin/env node // Usage: node generate.mjs "<prompt>" <output-file> [model] [orientation] import { mkdir, writeFile } from "node:fs/promises"; import { dirname } from "node:path"; const [prompt, out, model = "anime", orientation = "portrait"] = process.argv.slice(2); if (!prompt || !out) { console.error('usage: node generate.mjs "<prompt>" <output-file> [model] [orientation]'); process.exit(2); } if (!process.env.ANIMEAPI_KEY) { console.error("Set ANIMEAPI_KEY first: https://animeapi.ai/dashboard/api-keys"); process.exit(2); } const res = await fetch("https://animeapi.ai/api/generate", { method: "POST", headers: { Authorization: `Bearer ${process.env.ANIMEAPI_KEY}`, "Content-Type": "application/json", }, body: JSON.stringify({ prompt, model, orientation, allowNSFW: false }), }); const data = await res.json(); if (!res.ok) { console.error(`AnimeAPI error ${res.status} ${data.code ?? ""}: ${data.error}`); process.exit(1); } const image = await fetch(data.image_url); if (!image.ok) { console.error(`Could not download ${data.image_url} (${image.status})`); process.exit(1); } await mkdir(dirname(out), { recursive: true }); await writeFile(out, Buffer.from(await image.arrayBuffer())); console.log(`saved: ${out}`); console.log(`enhanced_prompt: ${data.enhanced_prompt}`); console.log(`balance_usd: ${data.balance_usd}`);.cursor/skills/animeapi/scripts/generate.py #!/usr/bin/env python3 """Usage: python3 generate.py "<prompt>" <output-file> [model] [orientation] Standard library only. """ import json import os import sys import urllib.error import urllib.request from pathlib import Path API_URL = "https://animeapi.ai/api/generate" UA = {"User-Agent": "animeapi-skill/1.0"} def main() -> int: if len(sys.argv) < 3: print(__doc__, file=sys.stderr) return 2 prompt, out = sys.argv[1], Path(sys.argv[2]) model = sys.argv[3] if len(sys.argv) > 3 else "anime" orientation = sys.argv[4] if len(sys.argv) > 4 else "portrait" key = os.environ.get("ANIMEAPI_KEY") if not key: print("Set ANIMEAPI_KEY first: https://animeapi.ai/dashboard/api-keys", file=sys.stderr) return 2 body = json.dumps( {"prompt": prompt, "model": model, "orientation": orientation, "allowNSFW": False} ).encode() req = urllib.request.Request( API_URL, data=body, headers={"Authorization": f"Bearer {key}", "Content-Type": "application/json", **UA}, ) try: with urllib.request.urlopen(req, timeout=180) as res: data = json.load(res) except urllib.error.HTTPError as err: detail = json.loads(err.read() or b"{}") print(f"AnimeAPI error {err.code} {detail.get('code', '')}: {detail.get('error')}", file=sys.stderr) return 1 out.parent.mkdir(parents=True, exist_ok=True) with urllib.request.urlopen(urllib.request.Request(data["image_url"], headers=UA), timeout=120) as img: out.write_bytes(img.read()) print(f"saved: {out}") print(f"enhanced_prompt: {data['enhanced_prompt']}") print(f"balance_usd: {data['balance_usd']}") return 0 if __name__ == "__main__": sys.exit(main())
→Use it
- “Generate a square avatar for the demo user, anime style, save to public/avatars/demo.webp”
- “Make a landscape title-screen background for the game into assets/title.webp”
- “Turn assets/title.webp into a 5-second intro clip, save to assets/intro.mp4”
Cursor also reads .claude/skills/, but the Claude Code version uses ${CLAUDE_SKILL_DIR}, which only Claude Code expands. Use this version for Cursor.