From 4a4903b49a8e1c8cf5ffa91add6c1d9032e79317 Mon Sep 17 00:00:00 2001 From: WaterrrForever Date: Tue, 14 Jul 2026 20:41:42 +0800 Subject: [PATCH] feat(skills): group core skills in the skills add picker (#2412) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `core-skills` entry to .claude-plugin/marketplace.json declaring the core skill set (the /hyperframes router, the hyperframes-* domain skills, and media-use). The upstream vercel-labs/skills CLI reads that entry's `skills` array for its interactive picker: the core set renders under a "Core Skills" group and everything else falls into "Other", so a human running `npx skills add heygen-com/hyperframes --full-depth` can tell the always-needed core set apart from the on-demand creation workflows — mirroring the core/on-demand tiers `hyperframes skills update` already enforces (isCoreSkill in skillsManifest.ts). The array deliberately lives on a separate marketplace entry, NOT on plugin.json or the `hyperframes` entry: Claude Code treats a manifest `skills` array as that plugin's skill allowlist (verified against claude CLI), so attaching it to the full plugin would narrow it from all skills to the core 8. As a side effect the new entry is itself a coherent Claude Code plugin — `core-skills@hyperframes` installs just the core set — while `hyperframes@hyperframes` keeps auto-discovering everything. A new pin test keeps the marketplace list in lockstep with isCoreSkill and the skills/ tree (alongside the existing FALLBACK_CORE_SKILLS pin), and asserts the full plugin carries no allowlist. Agent installs are unaffected — the upstream CLI detects agent environments and installs non-interactively, and the hyperframes CLI always passes explicit --skill flags. Co-authored-by: Claude Fable 5 --- .claude-plugin/marketplace.json | 17 +++++++ README.md | 2 +- docs/guides/skills.mdx | 2 +- packages/cli/src/utils/skillsManifest.test.ts | 49 +++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 16b0e6753..98c75187f 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -12,6 +12,23 @@ "description": "HyperFrames by HeyGen. Write HTML, render video. Compositions, GSAP and runtime adapter animations, captions, voiceovers, audio-reactive visuals, and website-to-video capture for HyperFrames.", "homepage": "https://hyperframes.heygen.com", "license": "Apache-2.0" + }, + { + "name": "core-skills", + "source": "./", + "description": "The always-needed HyperFrames core set: the /hyperframes router, the hyperframes-* domain skills, and media-use. The full plugin (all skills) is `hyperframes`.", + "homepage": "https://hyperframes.heygen.com", + "license": "Apache-2.0", + "skills": [ + "./skills/hyperframes", + "./skills/hyperframes-animation", + "./skills/hyperframes-cli", + "./skills/hyperframes-core", + "./skills/hyperframes-creative", + "./skills/hyperframes-keyframes", + "./skills/hyperframes-registry", + "./skills/media-use" + ] } ] } diff --git a/README.md b/README.md index 8d0009b4d..1ffff1895 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ The skills teach agents the HyperFrames production loop: plan the video, write v HyperFrames ships 20 skills agents load on demand. Read `/hyperframes` first — it's the router and capability map; it picks a workflow for any "make me a…" request — video, deck, or composition port — and points to the domain skills below. -Run `npx skills add heygen-com/hyperframes --full-depth` for the interactive picker, `npx skills add heygen-com/hyperframes --all --full-depth` to install all 20 at once (skips the picker), or `npx skills add heygen-com/hyperframes --skill --full-depth` for just one (bare name, no leading `/`). Keep `--full-depth` — it installs the current `main`; without it `skills add` fetches the skills.sh blob, which lags by hours. +Run `npx skills add heygen-com/hyperframes --full-depth` for the interactive picker — it lists the **core set** under the "Core Skills" group and the on-demand creation workflows under "Other". Use `npx skills add heygen-com/hyperframes --all --full-depth` to install all 20 at once (skips the picker), or `npx skills add heygen-com/hyperframes --skill --full-depth` for just one (bare name, no leading `/`). Keep `--full-depth` — it installs the current `main`; without it `skills add` fetches the skills.sh blob, which lags by hours. Installs stay lean after that: `npx hyperframes init` keeps the **core set** fresh (the router, the `hyperframes-*` domain skills, and `media-use` — plus whatever is already installed; `/figma` stays on demand) and never expands a partial install; the creation workflows install **on demand** — the router runs `npx hyperframes skills update ` before entering one. Nothing re-pulls the full set behind your back. diff --git a/docs/guides/skills.mdx b/docs/guides/skills.mdx index 33aee727e..c2b1030d2 100644 --- a/docs/guides/skills.mdx +++ b/docs/guides/skills.mdx @@ -19,7 +19,7 @@ The skills split into three groups: npx skills add heygen-com/hyperframes --full-depth ``` - Opens a picker so you can choose which skills to add. Keep `--full-depth`: it installs the current `main`. Without it, `skills add` fetches the skills.sh registry blob, which lags `main` by hours, so you may get an older copy of a skill. Works with [Claude Code](https://claude.ai/claude-code), [Cursor](https://cursor.sh), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Codex CLI](https://github.com/openai/codex), [GitHub Copilot CLI](/guides/copilot-cli), and [Google Antigravity](/guides/antigravity). + Opens a picker so you can choose which skills to add — the core set (the router, the `hyperframes-*` domain skills, and `media-use`) is listed under the "Core Skills" group, the on-demand creation workflows under "Other". Keep `--full-depth`: it installs the current `main`. Without it, `skills add` fetches the skills.sh registry blob, which lags `main` by hours, so you may get an older copy of a skill. Works with [Claude Code](https://claude.ai/claude-code), [Cursor](https://cursor.sh), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Codex CLI](https://github.com/openai/codex), [GitHub Copilot CLI](/guides/copilot-cli), and [Google Antigravity](/guides/antigravity). ```bash diff --git a/packages/cli/src/utils/skillsManifest.test.ts b/packages/cli/src/utils/skillsManifest.test.ts index f5a56728c..586ffab6b 100644 --- a/packages/cli/src/utils/skillsManifest.test.ts +++ b/packages/cli/src/utils/skillsManifest.test.ts @@ -139,6 +139,55 @@ describe("FALLBACK_CORE_SKILLS pin", () => { }); }); +describe(".claude-plugin/marketplace.json core-skills pin", () => { + // The marketplace `core-skills` entry's `skills` array drives two surfaces: + // the upstream `skills add` picker groups the listed skills under + // "Core Skills" (everything unlisted falls into "Other"), and Claude Code + // treats the array as that plugin's skill allowlist. That makes it a third + // enumeration of core membership — pin it to isCoreSkill and the skills/ + // tree so neither surface can silently drift from the tiers `init` / + // `skills update` actually enforce. It lives on a separate marketplace + // entry (not plugin.json, and not the `hyperframes` entry) precisely so + // the full `hyperframes` plugin keeps auto-discovering all skills. + it("lists exactly the core skills present in the repo's skills/ tree", () => { + const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", ".."); + const marketplace = JSON.parse( + readFileSync(join(repoRoot, ".claude-plugin", "marketplace.json"), "utf-8"), + ) as { plugins?: { name?: string; skills?: string[] }[] }; + const entry = marketplace.plugins?.find((p) => p.name === "core-skills"); + if (!entry?.skills) { + throw new Error("marketplace.json is missing the core-skills entry's `skills` array"); + } + const declared = entry.skills.map((p) => p.replace(/^\.\/skills\//, "")).sort(); + + const skillsRoot = join(repoRoot, "skills"); + const coreOnDisk = readdirSync(skillsRoot) + .filter((n) => existsSync(join(skillsRoot, n, "SKILL.md"))) + .filter((n) => isCoreSkill(n)) + .sort(); + + expect(declared).toEqual(coreOnDisk); + // Upstream resolves each entry relative to the repo root — the "./skills/" + // prefix is load-bearing (see vercel-labs/skills plugin-manifest.ts). + for (const p of entry.skills) { + expect(p.startsWith("./skills/")).toBe(true); + } + // The full plugin must NOT carry a skills allowlist: Claude Code would + // narrow it to the listed subset instead of auto-discovering all skills. + const full = marketplace.plugins?.find((p) => p.name === "hyperframes"); + expect(full).toBeDefined(); + expect(full?.skills).toBeUndefined(); + // Same for plugin.json (the direct-install manifest for the full plugin) — + // and upstream lets plugin.json groupings override marketplace ones, so a + // skills array here would also rename the picker group back to + // "Hyperframes". + const plugin = JSON.parse( + readFileSync(join(repoRoot, ".claude-plugin", "plugin.json"), "utf-8"), + ) as { skills?: string[] }; + expect(plugin.skills).toBeUndefined(); + }); +}); + describe("diffSkills", () => { const latest: SkillsManifest = { source: "test",