From creative-media-generation
Internal provider executor for the creative-media-generation plugin — creates a persistent HeyGen avatar (a reusable face + voice identity) via HeyGen Avatar V, prompt-based by default with optional photo upload for real-person twins. Invoked by the orchestrator (creative-media-generation), not selected directly from user chat. The orchestrator handles request understanding, provider discovery, spend-safety, consent grounding, levers, and artifacts, then delegates the raw create/train here. If the user names HeyGen directly, route to the orchestrator first. Returns avatar_id + voice_id for the orchestrator to pass into heygen-video.
How this skill is triggered — by the user, by Claude, or both
Slash command
/creative-media-generation:heygen-avatar [name_or_description][name_or_description]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
> **Vendored provider executor.** This skill is bundled inside the `creative-media-generation` plugin. Route through the orchestrator (`/creative-media-generation:creative-media-generation`) before any credit-consuming run or durable `media/` write — the orchestrator owns provider discovery, the spend-safety confirmation, and the artifact contract. Do not spend or write durable artifacts directly.
Vendored provider executor. This skill is bundled inside the
creative-media-generationplugin. Route through the orchestrator (/creative-media-generation:creative-media-generation) before any credit-consuming run or durablemedia/write — the orchestrator owns provider discovery, the spend-safety confirmation, and the artifact contract. Do not spend or write durable artifacts directly.
CMG executor mode (binding when invoked inside the creative-media-generation plugin)
When this skill runs as a vendored executor inside the
creative-media-generationplugin, the orchestrator governs routing, billing, and the artifact contract. In CMG mode this skill MUST:
- Use ONLY the orchestrator-supplied
provider/transport/billing_pool. Do NOT make its ownHEYGEN_API_KEYbilling decision and do NOT apply any "API-key short-circuits MCP, no question asked" logic — the orchestrator's HeyGen API-key footgun stop decides the wallet.HEYGEN_API_KEYis optional here (MCP-first via the orchestrator).- Write any raw assets ONLY to the orchestrator-supplied
output_dir(media/<instance>/assets/). Do NOT write local logs, do NOT read or write workspace-root identity files (SOUL.md,IDENTITY.md,AVATAR-*.md,AVATAR-AGENT.md,AVATAR-USER.md, role symlinks), and do NOT use/tmp/openclaw/uploads/. The orchestrator owns identity persistence asmedia/_identities/<id>.md(with a consent note) — return the resolvedavatar_id/voice_idto it rather than writing AVATAR files yourself.- Not use OpenClaw paths. Do NOT call OpenClaw
video_generateormessage(action:send)— those are upstream/standalone-only and are NOT a CMG path. Surface voice previews / results by returning paths to the orchestrator.- Return
avatar_id/voice_id(the identity handles), anyjob_id/session_id, andasset_paths(plus theprovider/transport/billing_poolused) to the orchestrator. The orchestrator owns the durablemedia/writes; this skill never writes them.Sections below marked (standalone/upstream only — NOT in CMG mode) describe the bare OpenClaw/workspace behavior and are superseded by this block whenever the skill runs inside the plugin.
Create and manage HeyGen avatars for anyone: the agent, the user, or named characters. Handles identity extraction, avatar generation, voice selection, and saves everything to AVATAR-<NAME>.md for consistent reuse.
This skill reads and writes the following. No other files are accessed without explicit user instruction.
CMG mode overrides this table. Inside the plugin, do NOT read or write workspace-root identity files (
SOUL.md,IDENTITY.md,AVATAR-*.md, role symlinks) and do NOT use/tmp/openclaw/uploads/. Return the resolvedavatar_id/voice_idto the orchestrator, which persists identities undermedia/_identities/<id>.md. Rows marked (standalone/upstream only) apply only outside the plugin.
| Operation | Path | Purpose |
|---|---|---|
| Read (standalone/upstream only — NOT in CMG mode) | SOUL.md, IDENTITY.md | Extract identity details when creating an avatar for the agent. In CMG mode, identity context comes from the orchestrator. |
| Read (standalone/upstream only — NOT in CMG mode) | AVATAR-<NAME>.md | Load existing avatar identity (for variant looks, voice updates). Not read in CMG mode. |
| Write (standalone/upstream only — NOT in CMG mode) | AVATAR-<NAME>.md | Save new avatar identity after creation. In CMG mode, return avatar_id/voice_id to the orchestrator — it writes media/_identities/<id>.md. |
| Write (standalone/upstream only — NOT in CMG mode) | AVATAR-AGENT.md, AVATAR-USER.md (symlinks) | Role aliases, see Phase 5. Not written in CMG mode. |
| Temp write (standalone/upstream only — NOT in CMG mode) | /tmp/openclaw/uploads/ | Voice preview audio. Not used in CMG mode — keep raw outputs under the orchestrator-supplied output_dir. |
| Remote upload | HeyGen (via heygen asset create or MCP) | User-provided photos uploaded to HeyGen for digital-twin creation |
Assets are only uploaded to HeyGen when the user explicitly provides them.
Detect the user's language from their first message. Store as user_language (e.g., en, ja, es, ko, zh, fr, de, pt).
user_language.user_language. When designing or selecting a voice, specify the target language so the voice library returns matches that speak it.Young Adult, Realistic, landscape, etc.) are API-level and not translated.SOUL.md, IDENTITY.md, AVATAR-*.md at the workspace root contain identity. Check them first. Only ask the user for what's genuinely missing. In CMG mode, do NOT read workspace-root identity files — identity context comes from the orchestrator.Default target = the agent. The primary use of this skill is giving the agent a face + voice so it can present videos. Route to "user" only on explicit "my avatar" / "me" / "my photo" language. When in doubt, make the agent's avatar.
Do NOT batch-ask questions. No "give me a photo, voice preference, duration, target platform, tone, key message" all at once. Walk phases in order. Each phase asks at most one or two things at a time.
For agent avatars: read SOUL.md and IDENTITY.md first, then go straight to prompt-based creation (standalone/upstream only — NOT in CMG mode; in CMG mode identity context comes from the orchestrator, not workspace files). Do NOT ask the user for a photo or appearance details first. The agent's identity lives in those workspace files. Only ask the user for traits that are genuinely missing.
Prompt-based is the default creation path. Photo is opt-in, only relevant when the user explicitly wants a real-person digital twin of themselves. Agents and named characters almost always use prompt-based creation.
CMG mode skips this section. Do NOT read
SOUL.md/IDENTITY.md/AVATAR-*.mdfrom the workspace root. The orchestrator supplies identity context and the write location.
Try to read SOUL.md from the workspace root.
⚠️ AVATAR file caveat: Ignore any AVATAR-*.md files found in the workspace that belong to a different person or agent (e.g., AVATAR-Eve.md when creating an avatar for Claude). Only use an AVATAR file if its name matches the subject you're creating for right now.
⚠️ Do NOT fetch HeyGen avatars yet. That's a Phase 0 sub-step (only after target detection). Fetching before Phase 0 causes the agent to frame the conversation around "your existing avatars" when the default should be creating one for the agent itself.
Mode selection is silent. Detect once at the start of the session, pick a mode, move on. Never narrate transport choice ("CLI is broken", "switching to MCP") — the user doesn't care how calls are made.
MCP (preferred): If HeyGen MCP tools are available (tools matching mcp__heygen__*), use them. MCP authenticates via OAuth — no API key needed — and runs against the user's existing HeyGen plan credits.
CLI fallback: If MCP tools are not available AND the heygen binary runs cleanly (heygen --version exits 0), use it. Auth: HEYGEN_API_KEY env OR heygen auth login (persists to ~/.heygen/credentials). If the CLI is missing, errors on --version, or auth is not set, silently skip it — do not retry MCP. In CMG mode, do NOT treat HEYGEN_API_KEY presence as a reason to prefer CLI — use the orchestrator-supplied transport; the orchestrator's footgun stop already chose the wallet (key presence is never an auto-green-light).
Neither available: Only if MCP is unavailable AND the CLI doesn't work, tell the user once: "To use this skill, connect the HeyGen MCP server or install the HeyGen CLI: curl -fsSL https://static.heygen.ai/cli/install.sh | bash then heygen auth login."
API: v3 only. Never call v1 or v2 endpoints.
Docs-first rule: Before calling any endpoint you're unsure about:
GET https://developers.heygen.com/llms.txt — full sitemap.md to the URL for clean markdownheygen <noun> <verb> --helpEvery avatar gets one file: AVATAR-<NAME>.md at the workspace root.
AVATAR-EVE.md ← agent (named, canonical)
AVATAR-KEN.md ← user (named, canonical)
AVATAR-CLEO.md ← character (named, canonical)
The skill also maintains two role-based symlinks alongside the named files, for generic lookups by consumer skills (e.g., heygen-video) when the request doesn't carry a specific name ("make a video of yourself" → read the agent alias; "make a video of me" → read the user alias):
AVATAR-AGENT.md → AVATAR-<CURRENT-AGENT-NAME>.md (symlink)
AVATAR-USER.md → AVATAR-<CURRENT-USER-NAME>.md (symlink)
Named files are the single source of truth; aliases are pointers and never drift. Phase 5 of the workflow maintains them. Named characters get NO role alias — they are referenced by name only.
Format:
# Avatar: <Name>
## Appearance
- Age: <natural language>
- Gender: <natural language>
- Ethnicity: <natural language>
- Hair: <natural language>
- Build: <natural language>
- Features: <natural language>
- Style: <natural language>
- Reference: <optional workspace-relative path or URL>
## Voice
- Tone: <natural language>
- Accent: <natural language>
- Energy: <natural language>
- Think: <one-line analogy>
## HeyGen
- Group ID: <character identity anchor — THE stable reference, never changes>
- Voice ID: <matched or designed voice>
- Voice Name: <human-readable>
- Voice Designed: <true if custom-designed, false if picked from catalog>
- Voice Seed: <seed value used, if designed>
- Looks: landscape=<look_id>, portrait=<look_id>, square=<look_id>
- Last Synced: <ISO timestamp>
⚠️ look_ids are ephemeral — always resolve fresh from group_id at runtime via `heygen avatar looks list --group-id <id>` (or MCP `list_avatar_looks`). Never hardcode look_id as the primary avatar reference.
Top sections (Appearance, Voice) are portable natural language. Any platform can use them. HeyGen section is runtime config with API IDs. Skills read this to make API calls.
Start every invocation with:
🎭 Using: heygen-avatar — creating an avatar for [name]
DO NOT batch-ask questions upfront. Walk phases in order. Each phase asks at most one thing at a time, and only if needed.
See the Start Here block above for the default-to-agent rule. Only route to "user" or "named character" when the phrasing is unambiguous.
Routing signals (in priority order):
IDENTITY.md for name.When unsure, default to agent. Do NOT ask the user for their name, appearance, or voice on an ambiguous request — that's the wrong first move. If after reading IDENTITY.md + SOUL.md the intent still feels ambiguous, ask one short clarifying question to disambiguate (phrase it naturally — something like "quick check: this avatar is for you, or for me?").
Then check AVATAR-<NAME>.md at the workspace root:
Role alias staleness check. Before proceeding, also check whether the role alias for this target is already pointing at the right named file:
AVATAR-AGENT.md (follow symlink) and
compare to AVATAR-<CURRENT-AGENT-NAME>.md. If they differ (e.g.,
AVATAR-AGENT.md → AVATAR-OLD-NAME.md because the agent identity
changed since the last run), re-link in Phase 5 even if no other
changes are made. The named file is canonical, but the alias must
match the current identity, not the historical one.AVATAR-USER.md.Optional existing-avatar check (only useful on the user path when the user might already have avatars in their HeyGen account). If Phase 0 target = user AND no AVATAR-<USER>.md exists, list their HeyGen avatars first:
MCP: list_avatar_groups(ownership=private)
CLI: heygen avatar list --ownership private
If the list is non-empty, present the options and ask which to use or whether to create new. If empty, proceed to Phase 1. Skip this check entirely for agent and named-character targets — those live in AVATAR-*.md, not the HeyGen catalog.
Order matters. Files first, questions second. Prompt-based creation is the default path — photo is an opt-in upgrade.
For the agent (Phase 0 target = agent):
SOUL.md, IDENTITY.md, and any existing AVATAR-<NAME>.md from the workspace root.For users/named characters (Phase 0 target = user or named):
user_language.Write AVATAR-<NAME>.md with the Appearance and Voice sections filled in. Leave the HeyGen section empty until Phase 2 succeeds.
Only run this step when Phase 0 target = user (real-person digital twin) OR when the user explicitly asks for photo realism.
Branch:
upload_asset or heygen asset create --file <path>, then Type B (photo) creation in Phase 2.For agents and named characters, skip this entire step — go straight to Type A (prompt) creation.
📖 Full creation API surface (photo / prompt / digital twin), file input formats, identity field → enum mapping, response shape → references/avatar-creation.md
Two modes:
Mode 1 — New character (omit avatar_group_id):
Creates a brand new character with its own group.
Mode 2 — New look (include avatar_group_id):
Adds a variation to an existing character. Read the Group ID from the AVATAR file.
Two creation types:
Type A — From prompt (AI-generated appearance):
MCP: create_prompt_avatar(name=<name>, prompt=<appearance>, avatar_group_id=<optional>)
CLI: heygen avatar create -d '{"type":"prompt","name":"...","prompt":"...","avatar_group_id":"..."}' (accepts inline JSON, a file path, or - for stdin)
Prompt limit is 1000 characters. Be descriptive — include style, features, expression, lighting. The API spec says 200 but the actual enforced limit is 1000.
Type B — From reference image:
MCP: create_photo_avatar(name=<name>, file=<file_object>, avatar_group_id=<optional>)
CLI: heygen avatar create -d '{"type":"photo","name":"...","file":{"type":"url","url":"..."},"avatar_group_id":"..."}'
File options for Type B:
{ "type": "url", "url": "https://..." } — public image URL{ "type": "asset_id", "asset_id": "<id>" } — from heygen asset create --file <path>{ "type": "base64", "media_type": "image/png", "data": "<base64>" } — inline📖 When to use each (URL vs asset_id vs base64), upload routing, and edge cases → references/asset-routing.md
Response: Returns avatar_item.id (look ID) and avatar_item.group_id (character identity).
Map identity fields to HeyGen enums for the prompt:
Show the prompt to the user before creating:
Appearance: "[prompt]" Settings: Young Adult | Woman | East Asian | Realistic Look good? (yes / adjust / completely different)
⛔ STOP. Wait for the user to approve or adjust. Do NOT call the avatar creation API until the user confirms.
Two paths: Design (describe what you want, get matched voices) or Browse (filter the catalog manually).
Ask whether they want voice design (describe what they want) or catalog browsing. Communicate in user_language.
Default to Design if the AVATAR file has a Voice section with personality traits.
Find matching voices via semantic search using the Voice section from the AVATAR file. This searches HeyGen's full voice library. No new voices are generated and no quota is consumed.
Language matching: The voice design prompt should specify the target language from user_language. Example for Japanese: "A calm, warm female voice. Professional but approachable. Japanese speaker." This ensures semantic search returns voices in the correct language.
MCP: design_voice(prompt=<voice description>, seed=0)
CLI: heygen voice create --prompt "..." --seed 0 (also accepts --gender, --locale)
Returns 3 voice options per seed. Present all 3 with inline audio previews:
preview_audio_url to a temp path (any standard download method works — no HeyGen auth needed, these are public S3 URLs). In CMG mode, download into the orchestrator-supplied output_dir, not /tmp/openclaw/.message(action:send, media:"<path>", caption:"Option <n>: <voice_name> — <gender>, <language>") so it plays inline in Telegram/Discord. In CMG mode, return the preview paths to the orchestrator instead of calling message(...).⛔ STOP. Wait for the user to pick a voice via buttons or text. Do NOT select a voice yourself or proceed to Phase 4 until the user explicitly chooses.
If none match:
"None of these hitting right? I can try a different set (same description, different variations) or you can tweak the description."
Increment seed and call again. Different seeds give completely different voice options from the same prompt.
Browse HeyGen's existing voice library:
MCP: list_voices(type=private) then list_voices(type=public, language=<lang>, gender=<gender>)
CLI: heygen voice list --type private / heygen voice list --type public --language <lang> --gender <gender>
CMG mode: do NOT write
AVATAR-<NAME>.md. Return the resolvedavatar_id(group_id) andvoice_idto the orchestrator; it persists the identity undermedia/_identities/<id>.mdwith a consent note. The format below is the standalone persistence shape only.
Update the HeyGen section of AVATAR-<NAME>.md to match the canonical format:
## HeyGen
- Group ID: <avatar_item.group_id — THE stable reference, never changes>
- Voice ID: <chosen voice_id>
- Voice Name: <voice name>
- Voice Designed: <true if custom-designed, false if picked from catalog>
- Voice Seed: <seed value used, if designed>
- Looks: <orientation>=<avatar_item.id> (e.g., landscape=<look_id>, portrait=<look_id>)
- Last Synced: <ISO timestamp>
⚠️ look_ids are ephemeral — always resolve fresh from group_id at runtime via `heygen avatar looks list --group-id <id>` (or MCP `list_avatar_looks`). Never hardcode look_id as the primary avatar reference.
Confirm the avatar is saved and that other skills (like heygen-video) will pick it up automatically. Communicate in user_language.
CMG mode skips Phase 5 entirely. Do NOT create
AVATAR-AGENT.md/AVATAR-USER.mdsymlinks at the workspace root. The orchestrator resolves identities frommedia/_identities/— no role aliases are written.
After writing the named AVATAR-<NAME>.md, create or update a role-based
symlink alongside it so other skills can do generic lookups without
resolving the agent / user name first.
Based on the Phase 0 target:
AVATAR-AGENT.md → AVATAR-<NAME>.mdAVATAR-USER.md → AVATAR-<NAME>.mdAVATAR-CLEO.md); they are not the agent or the user.Implementation (run from the workspace root, with fs-fallback):
The cd to workspace root is mandatory — bare relative paths in ln -s
resolve from the agent's current working directory, not where SOUL.md
lives. The || echo clause handles filesystems that reject symlinks
(Windows without dev mode, some cloud-mounted storage) without aborting
Phase 5.
# Agent
cd "$WORKSPACE_ROOT" && ln -sf AVATAR-<NAME>.md AVATAR-AGENT.md \
|| echo "role alias skipped: fs doesn't support symlinks"
# User
cd "$WORKSPACE_ROOT" && ln -sf AVATAR-<NAME>.md AVATAR-USER.md \
|| echo "role alias skipped: fs doesn't support symlinks"
Use a relative link target (just the filename, no path prefix) so the alias survives if the workspace is moved or copied.
ln -sf is unlink-then-symlink under the hood, not strictly atomic.
Fine for single-user workspaces; if concurrent agents ever write the
same alias, expect interleaving and add explicit locking then.
Why symlink, not copy: removes the duplicate-file drift class
(content can never diverge between named file and alias). It does NOT
remove staleness drift — if IDENTITY.md changes the agent name without
re-running heygen-avatar, AVATAR-AGENT.md keeps pointing at the old
named file. Phase 0 mismatch-and-re-alias handles this on the next
invocation; until then, the alias is stale-but-pointing-somewhere-valid,
not broken.
Multi-agent workspace caveat: one role alias per workspace is
last-writer-wins. If two agents ever share a workspace and both run
heygen-avatar, only the most recent run's identity is reachable via
AVATAR-AGENT.md. Named files for both still exist. We accept this
limit — multi-agent shared workspaces are out of scope for v1.
If the user wants to see their avatar in action:
MCP: create_video_agent(avatar_id=<avatar_id>, voice_id=<voice_id>, prompt=<greeting>)
CLI: heygen video-agent create --avatar-id <id> --voice-id <id> --prompt "..." --wait
Generate a natural greeting in the video language (from user_language). Examples: English "Hi, I'm [name]. Nice to meet you!", Japanese "[name]です。はじめまして!", Spanish "Hola, soy [name]. ¡Mucho gusto!", Korean "안녕하세요, [name]입니다. 만나서 반갑습니다!"
When the user wants to refine:
Default to Mode 2 (new look under same group). Only create a new group when the user explicitly wants a different character identity. This keeps the account clean and makes looks reusable across skills.
Each iteration updates the AVATAR file. The file is always the source of truth.
Be interactive at checkpoints, silent everywhere else. Stop and wait at avatar approval and voice selection. Between checkpoints, work silently — don't narrate reasoning or explain next steps. After voice pick: save + confirm in one message.
heygen-video reads AVATAR files for group_id and voice_id. Resolution
order:
AVATAR-EVE.md.AVATAR-AGENT.md (symlink to current agent's named file).AVATAR-USER.md (symlink to current user's named file).The alias targets are resolved by the OS at read time, so consumer skills
simply cat AVATAR-AGENT.md and get whatever the current agent's avatar is.
📖 Known issues, retry patterns, broken voice previews, error → action mapping → references/troubleshooting.md
npx claudepluginhub cmgramse/skill-development --plugin creative-media-generationGenerates brand assets: logos (55+ styles, Gemini AI), CIP mockups, HTML slides (Chart.js), banners (22 styles), SVG icons (15 styles), and social media photos. Routes to sub-skills for design tokens and UI styling.