feat: make heygen-avatar + heygen-video install cleanly via gh skill#77
Conversation
Each skill (heygen-avatar, heygen-video) now bundles its own references/ directory with the docs it links to. This makes both skills install cleanly via gh skill install (which only copies the skill subdirectory), without breaking the OpenClaw plugin or direct git clone lanes that use the repo-root references/ directory.
- README + INSTALL.md: list gh skill install as Option 1, alongside ClawHub, OpenClaw plugin, and git clone - Add validate-skills.yml CI: runs gh skill install --from-local for both skills and asserts each installed bundle is self-contained (no broken ../ refs, every references/ link resolves) - Cross-skill references in references/ files now use absolute GitHub URLs so they remain clickable on GitHub and never produce broken links inside an installed bundle - .gitignore: skip local CI fixtures (.agents/, _ghskill_test/, _install_test/)
kenchung
left a comment
There was a problem hiding this comment.
Review
Mostly matches the agreed slimmer Option C-1. Two real blockers, one defensible deviation, one out-of-scope expansion worth calling out.
Please fix
1. CI is failing — install path mismatch. .github/workflows/validate-skills.yml:60 runs gh skill install … --scope project from inside _install_test/, but gh skill writes to the repo project root (<repo>/.agents/skills/), not the cwd. The next assertion test -f .agents/skills/heygen-avatar/SKILL.md fails. Same bug at the heygen-video assertion (~line 85). The self-containment validation that's the gating premise of this PR isn't actually running today.
Fix: either cd back to repo root before the assertion, or initialize _install_test as its own project root (e.g. git init) so --scope project lands inside it.
2. No drift-prevention CI step. PR body explicitly punts the diff-check between root references/*.md and heygen-{avatar,video}/references/*.md to a follow-up. Today the 8 truly shared files are byte-identical (verified) — but nothing prevents drift on the next reference edit, and Ken specifically called drift surface out as a concern. A ~5-line diff -q matrix in the same workflow closes it.
Worth calling out (deviations)
3. heygen-avatar/SKILL.md was modified despite plan saying "untouched." The PR adds 3 references/ pointers and a version bump. Defensible — the SKILL had no references/ pointers before, so the new co-located refs would be undiscoverable without them. Not a regression. Just flagging the scope creep.
4. Out-of-scope content split: avatar-discovery.md cleaved. Root file is the unified version. heygen-video/references/avatar-discovery.md is a 179-line video-only cut. New heygen-avatar/references/avatar-creation.md (178 lines) added. This is more than "copy 8 shared files" — it's a content fork. Cross-skill links use absolute github.com URLs (correct for installed bundles). Increases drift surface beyond the single shared-file set originally agreed.
Nit
agent-skillsrepo topic still empty. Repo Settings → Topics, post-merge.
What's good
- All 8 truly shared files in
heygen-video/references/are byte-identical to root (verified locally). - All 9
../refs inheygen-video/SKILL.mdcorrectly rewritten to siblingreferences/andscripts/. Zero../left in either skill. - Root
SKILL.md, rootreferences/, rootscripts/, all 3 plugin manifests untouched. Plugin / ClawHub / direct-clone lanes preserved as promised. heygen-video/scripts/update-check.shfalls back to SKILL.md frontmatter version when repo-rootVERSIONnot present — sound.INSTALL.mdproperly elevatesgh skill installto Option 1 with project/user/@VERSION --pinexamples.
Verdict
Request changes — two CI fixes (install path + drift gate) before merge. Once green, this is the right shape.
Address review feedback:
1. CI install path mismatch: gh skill install --scope project resolves
to the outer repo project root (skills/.agents/), not _install_test/.
Initialize _install_test as its own git repo so it becomes its own
project root and the install lands inside it. Verified locally that
the assertion now actually runs.
2. No drift-prevention CI step: add a references-in-sync job that
diff-checks all 10 shared files (heygen-{avatar,video}/references/X
vs root references/X) for byte-identity. Fails CI on drift with a
helpful diff. New per-skill files (avatar-creation.md, the cleaved
avatar-discovery.md halves) are intentionally NOT in this matrix —
only the truly shared docs are gated.
3. Use bash 3.2-portable parallel array (pipe-delimited pairs) instead
of associative array so the workflow can be sanity-checked locally
on macOS without bash 5+.
|
Thanks for catching the install-path bug — that was a real false-green. Fixed in What changed1. Install target is now an isolated project root. 2. New 3. Bash 3.2-portable arrays (pipe-delimited pairs instead of Re your other pointsAvatar SKILL.md modification. Acknowledged scope creep, kept intentionally. The 3 added 📖 pointers and the avatar-discovery.md cleave. Yes — this PR forks one file into two. Justified: the original The cleave does increase the maintenance surface (the cleaved
CI should be green this run. Re-requesting review. |
kenchung
left a comment
There was a problem hiding this comment.
Approve
Both blockers + nit addressed at a2e8d00. Verified the actual diff, not just the PR description.
| # | Blocker | Status |
|---|---|---|
| 1 | CI install path mismatch | ✅ validate-skills.yml:86-91 initializes _install_test/ as its own git repo (git init -q + bootstrap commit), so gh skill install --scope project lands at _install_test/.agents/skills/. Self-containment assertions then cd _install_test/.agents/skills/<skill> and verify no ../ refs + every relative ref resolves inside the installed bundle. The validation actually runs now. |
| 2 | Drift-prevention CI step | ✅ New references-in-sync job (:23-50) with explicit PAIRS=(...) list of 10 truly-shared files, diff -q per pair, fails CI with a unified-diff snippet on any drift. Per-skill files (avatar-creation.md, the cleaved avatar-discovery.md halves) intentionally excluded. Currently 10/10 byte-identical. |
| Nit | agent-skills repo topic |
✅ Added (gh api repos/heygen-com/skills/topics confirms: ["agent-skills","ai-agent","avatar-video","claude-code","heygen","openclaw","skill","video"]). |
Bonus quality
- Bash 3.2 portable (pipe-delimited parallel arrays, not associative). Macs and CI both work.
- Spec validation runs as advisory (
continue-on-error: true) — surfaces the known root-SKILL.md publish-validation warning as a follow-up signal without blocking merge. - Self-containment assertion catches both classes of breakage:
../refs in installed SKILL.md and missing relative-path files in the bundle. Both directions covered.
CI green: install path fixed, drift gate live, spec validation advisory. Mergeable. Good to merge.
Deviations from the original "copy 8 shared files" plan (heygen-avatar/SKILL.md +7 additive lines, avatar-discovery.md cleave) acknowledged in PR body and Slack thread — kept as intentional.
Audit of references/*.md outcome: - 7 files: actively linked from skills, kept as-is - 1 file (prompt-styles.md): linked only from root SKILL.md, orphaned in the gh-skill subdir bundle. Wired into heygen-video/SKILL.md alongside the existing prompt-craft / motion-vocabulary pointers. - 1 file (reviewer-prompt.md): zero links from any SKILL.md, dead in the workflow. Deleted from root references/ and heygen-video/references/. Sync workflow (Option 2): - Add scripts/sync-references.sh — propagates root references/ to per-skill copies (--check mode for CI drift detection, no args for sync). - validate-skills.yml now invokes ./scripts/sync-references.sh --check instead of inlining the pair list (single source of truth). - New CI orphan-check: every bundled references/* / scripts/* file in an installed bundle must be linked from SKILL.md. - CONTRIBUTING.md documents the references layout + editor workflow.
|
Audit + cleanup pushed in 1. Purged orphans
2. Wired previously-orphaned
|
| File | Lines | Verdict | Action |
|---|---|---|---|
| troubleshooting.md | 151 | Essential (5 inbound links) | Kept |
| asset-routing.md | 86 | Essential (3 links) | Kept |
| avatar-discovery.md | 213 | Essential (cleaved per-skill) | Kept |
| frame-check.md | 98 | Essential | Kept |
| prompt-craft.md | 273 | Essential | Kept |
| motion-vocabulary.md | 191 | Useful | Kept |
| official-prompt-guide.md | 116 | Useful | Kept |
| prompt-styles.md | 251 | Was orphaned in subdir | Wired in |
| reviewer-prompt.md | 95 | Dead | Deleted |
Net: +143 lines (sync script + CI orphan-check + CONTRIBUTING docs), -227 lines (reviewer-prompt × 2 copies + simplified CI workflow).
Re-requesting review.
kenchung
left a comment
There was a problem hiding this comment.
Re-approve at 675b0ee
Verified the 3 new changes since my prior approval:
| Change | Status |
|---|---|
Delete reviewer-prompt.md (orphan) |
✅ Removed from references/ and heygen-video/references/, removed from sync-script PAIRS list. Stale tree-diagram entry in CLAUDE.md:30 is allowed — non-functional. |
Wire prompt-styles.md from heygen-video/SKILL.md |
✅ heygen-video/SKILL.md:447 links sibling references/prompt-styles.md, sits in the Prompt Craft section alongside prompt-craft.md, motion-vocabulary.md, official-prompt-guide.md. |
| Sync script + orphan check | ✅ scripts/sync-references.sh --check exits non-zero on drift with ::error:: annotations and unified diff. validate-skills.yml:30 calls it (no inline PAIRS array). New orphan-check at lines 105/140 grep-asserts every bundled references/* / scripts/* file is linked from SKILL.md. CONTRIBUTING.md:50-59 documents editor workflow. |
All 4 CI checks green. Mergeable. Good to merge.
Worth flagging for follow-up (not a blocker): the question of whether root references/ should disappear entirely (Option E) or root SKILL.md should follow (Option F) — both are larger structural cleanups that warrant a separate PR.
* feat: add heygen-translate skill (video translation / dubbing) Adds a third skill, heygen-translate/, for translating and dubbing existing videos into 175+ languages with voice cloning and lip-sync. Built on the same independent-skill structure as heygen-avatar and heygen-video. What: - heygen-translate/SKILL.md (4-phase workflow: Discovery → Pre-flight → Submit+Poll → Deliver) with the same API Mode Detection ladder as heygen-video (OpenClaw plugin → CLI w/ HEYGEN_API_KEY → MCP → CLI fallback). All operations shown with MCP and CLI side-by-side, no raw curl. - heygen-translate/references/troubleshooting.md (errors → action map, polling patterns, harness-specific notes for Claude Code / OpenClaw / Cursor) - heygen-translate/references/language-locale-guide.md (regional variant defaults, formality registers, RTL caption collisions, tonal compression/expansion table, lip-sync ceiling per language) - heygen-translate/references/proofreads-workflow.md (the high-stakes review-edit-render path: extract SRT → glossary discipline → register fixes → upload edited SRT → final render) - heygen-translate/references/asset-routing.md (URL vs asset_id vs local upload routing, HEAD-check pattern, auth-walled URL fallbacks, 32 MB limit handling) Replaces PR #46 with the new repo structure (independent skills, no root SKILL.md, references inside the skill, validate-skills.yml self-contained checks, MCP+CLI transport not raw API). Why: - PR #46's SKILL.md frontmatter declared 'allowed-tools: mcp__heygen__*' but every example used raw curl against api.heygen.com. Mismatch fixed here by using the heygen video-translate CLI (with MCP fallthrough) per the established pattern in heygen-avatar/heygen-video. - PR #46 was authored against the pre-#79 structure (root SKILL.md + shared references/). Repo restructured 24h ago — each skill now owns its own SKILL.md and references/. This PR matches. - PR #46 lacked embedded translation expertise. This SKILL.md adds: speaker-count discipline, source-quality triage, locale-pair gotchas (formality registers in ja/ko/de/th/hi, RTL caption collisions, tonal compression for en→zh/ja/ko, regional variants for es/pt/zh), lip-sync ceiling, captions burned-in vs sidecar, audio-only as a different deliverable not a workaround, cost/time math, and a failure-mode decoder. - PR #46 used 'video-translate/' breaking the heygen-avatar/heygen-video prefix pattern. Renamed to 'heygen-translate/' for consistency in ls output and plugin manifest paths. - Adds a true proofreads workflow (extract SRT → user/agent edits → upload corrected SRT → render) — this is the missing high-stakes path that distinguishes the skill from API docs. Plumbing: - .claude-plugin/marketplace.json registers heygen:translate - .claude-plugin/plugin.json updates description + keywords - .codex-plugin/plugin.json updates description, keywords, longDescription, defaultPrompt - .cursor-plugin/plugin.json adds heygen-translate to skills array, plus keywords/tags - .github/workflows/validate-skills.yml adds heygen-translate to path filter and runs the same self-contained-bundle checks as the other two skills - release-please-config.json adds heygen-translate/SKILL.md as a release-please extra-files target so the version bumps in lockstep - README.md, INSTALL.md, INSTALL_FOR_AGENTS.md, CLAUDE.md, CONTRIBUTING.md all updated to reference the third skill Out of scope (followups): - platforms/nanoclaw/heygen-translate/ NanoClaw container variant - Eval scenarios for heygen-translate (mirror of R17-R23 pattern from heygen-video) - gh skill / agentskills.io spec compliance check (handled by the spec-validate-soft job already in validate-skills.yml) - Mark PR #46 as superseded once this lands Refs: PR #46 (predecessor), #79 (independent-skills restructure), #77 (gh skill install path) * docs(heygen-translate): document what the proofread CLI actually performs Per Ken's ask in #tmp-vt-skill: rewrite proofreads-workflow.md (and the Phase 3 proofread snippet in SKILL.md) against verified live behavior of the heygen video-translate proofreads commands, not assumed/inferred behavior. Verified against the live API + CLI on Apr 27 with two real proofread sessions (b84c8e8d... silent-source failure, 8ce0fba6c... Spanish Sintel-trailer success). Now documented: - Five subcommands mapped to real REST endpoints: create POST /v3/video-translations/proofreads get GET /v3/video-translations/proofreads/{id} srt get GET /v3/video-translations/proofreads/{id}/srt srt update PUT /v3/video-translations/proofreads/{id}/srt generate POST /v3/video-translations/proofreads/{id}/generate - What the engine actually does between create and completed (downloads source, runs ASR for original_srt_url, translates to srt_url, no render yet). - Real response shapes for create / get / srt get / srt update / generate with verified JSON examples and field-by-field meanings. - Real status enum: processing | completed | failed (NOT pending|running — that's the translation-render endpoint, which is a different state machine the resource graduates into after generate). - Polling cadence verified empirically: 3-5 min for SRT extraction on a 50-second source. Hard timeout 30 min for stuck sessions. - SRT format: standard SRT (UTF-8), well-formed timecodes, editable by hand or sed. - File naming: <title>_proofread.srt and <title>_proofread_original.srt. - original_srt_url is auto-populated source-language transcription, not a copy of any user-provided SRT. Useful as ground truth, never re-uploaded as target-language SRT. Critical correction: heygen asset create does NOT accept SRT files. The CLI exposes both URL and asset_id shapes for srt update, but the asset_id upload path is currently BLOCKED: {"error":{"code":"invalid_parameter", "message":"Content type not supported application/x-subrip"}} heygen asset create only accepts png/jpeg/mp4/webm/mp3/wav/pdf. Renaming .srt to .txt or .mp3 does not bypass it (server sniffs content, not extension). The asset_id route is in the request schema for forward compatibility but cannot currently be exercised through the standard upload path. Use the URL route. The reference now documents practical hosts that work (gist raw URLs, GitHub raw URLs, S3 public-read, presigned URLs >=2h, Vercel/static). Two new failure_message strings added to troubleshooting.md from real API responses: - 'Failed to download video from url, please check the url is valid or the video is public' (instant-fail on bad/auth-walled source URL) - 'Your video's audio is missing or corrupted, please try with another video' (~30s fail when source has no speech) Other documented quirks: - proofreads create returns proofread_ids (plural, one per language) plus a session-level status — per-id status comes from proofreads get. - After generate, polling shifts from proofreads get to video-translate get because the resource graduates from proofread to translation. - Captions on generate are independent of the proofread session's SRT — --captions controls whether the FINAL video burns captions in. - Proofread session TTL ~24h. Out of scope for this commit (still in followup queue): - NanoClaw platform variant - Eval scenarios for heygen-translate - File issue/PR upstream re: SRT asset upload (worth surfacing to HeyGen CLI team — the asset_id route in the schema can't be reached today) * fix(heygen-translate): auth gate, duration question, open-ended language input Three improvements from dogfooding: - Add auth verification step before Phase 1: runs `heygen auth status` in CLI mode, asks for API key and persists via `heygen auth login` if missing. One-time setup that survives across sessions. - Add duration flexibility question to Phase 1 discovery: asks whether output must match source length, explains quality tradeoff, controls `enable_dynamic_duration` flag instead of hardcoding true. - Make target language question explicitly open-ended: no picker, no pre-assigned choices. User types freely, validation in Phase 2. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(heygen-translate): align dynamic_duration references with Phase 1 question SKILL.md:335 and references/language-locale-guide.md:49 both said "Always enable_dynamic_duration: true", contradicting the new Phase 1 duration flexibility question. Updated both to reference the user's choice and warn about quality degradation on high-compression pairs when fixed-length is chosen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: David Chou <david.chou@heygen.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Makes
heygen-avatarandheygen-videoinstall cleanly viagh skill install(the agentskills.io registry that 12+ agents now use: Claude Code, Cursor, Codex, Gemini CLI, Copilot, Junie, Goose, OpenHands, Amp, Cline, OpenCode, Warp, etc.).Supersedes #64 (closed) — same goal, smaller blast radius, zero regression on existing distribution lanes.
The bug
gh skill installonly copies the skill subdirectory to the user's machine. It does not pull parent-directory resources. With our previous layout,heygen-video/SKILL.mdhad 9 cross-references like[../references/troubleshooting.md](../references/troubleshooting.md)andSee the root [SKILL.md](../SKILL.md). Aftergh skill install, those references all pointed to files that were not in the installed bundle — silent breakage.Verified empirically against current master with
gh2.91.0:Approach: co-locate references inside each skill (no file moves at the repo level)
Each skill now bundles its own
references/directory with the docs it links to. This makes both skills install cleanly viagh skill installwhile keeping every existing distribution lane untouched.@heygen/openclaw-plugin-heygen)SKILL.md+references/clawhub install heygen-skills).from rootgh skill install heygen-com/skills heygen-{avatar,video}../refs in heygen-video./heygen-avatar/,./heygen-video/Root
SKILL.md, rootreferences/, rootscripts/, all three plugin manifests, the.mcp.json,release-please-config.json,clawhubignore, andINSTALL_FOR_AGENTS.mdare untouched. Plugin-mode and direct-clone consumers see no change.What changed
heygen-avatar/heygen-avatar/references/:avatar-creation.md(new) — full creation API surface (photo / prompt / digital twin), file input formats, identity field → enum mapping, response shape. Cleaved from the old monolithicreferences/avatar-discovery.mdwhich was mixing avatar creation (heygen-avatar's job) and avatar discovery (heygen-video's job).asset-routing.md(copy of root) — when to use URL vs asset_id vs base64 for photo uploads.troubleshooting.md(copy of root) — known issues, retry patterns, broken voice previews.heygen-avatar/SKILL.mdgets📖pointers it was missing (Phase 2 → avatar-creation.md, file options → asset-routing.md, Error Handling → troubleshooting.md). The skill previously had zero references/ pointers despite the docs existing at root.version: 2.3.0 # x-release-please-versionto frontmatter soupdate-check.shcan resolve the version from inside an installed bundle.heygen-video/heygen-video/references/with the 9 files it actually links to:frame-check.md,motion-vocabulary.md,official-prompt-guide.md,prompt-craft.md,prompt-styles.md,reviewer-prompt.md,asset-routing.md,troubleshooting.md, plusavatar-discovery.md(cleaved video half — Path 0 / Path A / Path C / "How Avatar/Voice Are Passed").heygen-video/scripts/update-check.sh— copy of root script with one fix: it now reads VERSION fromSKILL.mdfrontmatter when no repo-rootVERSIONfile is reachable (the gh-skill install case). The repo-root path remains the canonical source.heygen-video/SKILL.mdcross-references rewritten:../references/X.md→references/X.md(9 places).heygen-video/SKILL.md"API Mode Detection" section: previously said "See the root SKILL.md for canonical XOR rules" with a../SKILL.mdlink. Now inlines the canonical rules (the OpenClaw plugin / CLI / MCP / Neither ladder, the OpenClaw plugin-modevideo_generatecall signature, MCP tool name list, CLI command groups). Source of truth for plugin-mode + direct-clone is still root SKILL.md — this is a copy for self-containment.version: 2.3.0 # x-release-please-versionto frontmatter.Cross-skill links use absolute GitHub URLs
The two cleaved halves (
heygen-avatar/references/avatar-creation.mdandheygen-video/references/avatar-discovery.md) reference each other for "see the other skill's docs" pointers. Inside an installed bundle, relative../../cross-skill paths would be broken (the other skill isn't installed there). They use absolutehttps://github.com/heygen-com/skills/blob/master/...URLs so they remain clickable on GitHub and never produce a broken file:// path.CI:
validate-skills.ymlNew workflow that runs
gh skill install --from-localfor both skills against a temporaryskills/staging directory, then asserts each installed bundle is self-contained:../parent-dir refs survive in installedSKILL.mdreferences/X.mdandscripts/X.shlink in installedSKILL.mdresolves to a file inside the installed bundlePlus an advisory
gh skill publish --dry-runjob (continue-on-error) so we know when rootSKILL.mdvalidation drift would affect a future agentskills.io registry publish.Docs
gh skill installas Option 1, with project / user scope and@VERSION --pinexamples. ClawHub stays as Option 2, OpenClaw plugin as Option 3, git clone as Option 4 / fallback.Verified empirically
$ gh skill install ./_ghskill_test heygen-avatar --from-local --scope project Installed heygen-avatar heygen-avatar/ ├── SKILL.md └── references/ ├── asset-routing.md ├── avatar-creation.md └── troubleshooting.md $ gh skill install ./_ghskill_test heygen-video --from-local --scope project Installed heygen-video heygen-video/ ├── SKILL.md ├── references/ (9 files) └── scripts/ └── update-check.sh # Self-containment: ✓ heygen-avatar: no ../ refs in SKILL.md, all references/ links resolve ✓ heygen-video: no ../ refs in SKILL.md, all references/ links resolve # update-check.sh inside installed bundle: $ HEYGEN_SKILLS_STATE=/tmp/test bash .agents/skills/heygen-video/scripts/update-check.sh --force UPGRADE_AVAILABLE 2.3.0 2.3.1Out of scope (follow-up)
SKILL.mddoesn't satisfygh skill publishvalidation rules (name: heygen-skillsdoesn't match repo-root directory.). This blocks publishing to the agentskills.io registry but does not blockgh skill install(which silently skips invalid root-level files). Tracked as advisory CI warning here; addressing it would mean either renaming root SKILL.md or restructuring the repo, both of which would touch plugin manifests and other lanes — explicitly out of scope to keep this PR low-blast-radius.references/and the newheygen-{avatar,video}/references/copies is currently manual. If drift becomes a real problem, follow-up with ascripts/sync-references.sh+ CI gate.Credits
Original bug spotted by @somanshreddy in #64.