fix(cli): install the published skill set, not every SKILL.md in the repo (#3636)

Bare `hyperframes skills` passed `--skill '*'` to the upstream installer, which discovers every SKILL.md in the clone, so a full install put 26 skills on disk against the 20 the manifest publishes (the six repo-internal ones under .claude/skills / .agents/skills). It now resolves the canonical manifest and installs its names, the same path `skills update` uses; offline it warns and degrades to the pinned core set instead of the wildcard. Docs point the full set at `npx hyperframes skills` and say what `skills add --all` really pulls.
This commit is contained in:
WaterrrForever
2026-09-04 23:25:43 +08:00
committed by GitHub
parent c5224ac7e3
commit c8300c69a1
7 changed files with 72 additions and 13 deletions
+2 -1
View File
@@ -8,7 +8,8 @@ This repo ships AI agent skills via [vercel-labs/skills](https://github.com/verc
```bash
npx hyperframes skills update # default: installs/refreshes the core set — workflows install on demand
npx skills add heygen-com/hyperframes # interactive picker (terminal only — non-interactive without --skill installs everything)
npx hyperframes skills # all 20 published skills at once
npx skills add heygen-com/hyperframes # interactive picker (terminal only; --all also pulls the repo-internal skills under .claude/skills)
```
**Creation workflows** route through one entry skill — read `/hyperframes` first: it orients you to the whole surface, confirms the brief up front (the intent layer), and maps "make me a…" intent — usually a video, but also a navigable deck (`/slideshow`) or a composition port (`/remotion-to-hyperframes`) — to a concrete workflow. Consult it before invoking a specific workflow:
+2 -2
View File
@@ -8,8 +8,8 @@ This repo ships 20 AI agent skills via [vercel-labs/skills](https://github.com/v
```bash
npx hyperframes skills update # default: installs/refreshes the core set — workflows install on demand
npx skills add heygen-com/hyperframes # interactive picker (terminal only — non-interactive without --skill installs all 20)
npx skills add heygen-com/hyperframes --all # all 20 at once — only on explicit request
npx hyperframes skills # all 20 published skills at once — only on explicit request
npx skills add heygen-com/hyperframes # interactive picker (terminal only; --all also pulls the 6 repo-internal skills under .claude/skills)
npx skills add heygen-com/hyperframes --skill <name> # just one (bare name, no leading slash)
```
+1 -1
View File
@@ -41,7 +41,7 @@ Install the HyperFrames skills, then describe the video you want:
npx skills add heygen-com/hyperframes
```
> The picker opens with nothing pre-selected — the **Core Skills** group is all you need: the `/hyperframes` router installs each creation workflow on demand. Agents and non-interactive runs should use `npx hyperframes skills update` instead — it installs exactly the core set, whereas a non-interactive `skills add` without `--skill` installs all 20.
> The picker opens with nothing pre-selected — the **Core Skills** group is all you need: the `/hyperframes` router installs each creation workflow on demand. Agents and non-interactive runs should use `npx hyperframes skills update` instead — it installs exactly the core set, whereas `skills add --all` installs every `SKILL.md` in the repo — the 20 published skills plus six repo-internal ones under `.claude/skills` / `.agents/skills`. For the full published set use `npx hyperframes skills`.
>
> `skills add` resolves the skills.sh registry blob, which can lag `main` by hours. `npx hyperframes skills update` installs from the current `main`, so reach for it when you need the newest copy of a skill.
+4 -2
View File
@@ -110,12 +110,14 @@ You can invoke a workflow directly when you already know it is the right one, bu
## Install more only when you need it
Install the complete set deliberately with:
Install the complete published set deliberately with:
```bash
npx skills add heygen-com/hyperframes --all
npx hyperframes skills
```
(`npx skills add heygen-com/hyperframes --all` also works, but it installs every `SKILL.md` in the repository, including repo-internal skills that are not part of the published set.)
Or install one named skill:
```bash
+1 -1
View File
@@ -72,7 +72,7 @@ The installer shows a picker. Select the **core skills** below — every project
| `/figma` | A Figma file / frame / URL → imported assets, brand tokens, and reconstructed motion |
<Tip>
To skip the picker and install everything (core + every workflow) in one shot, run `npx skills add heygen-com/hyperframes --all`. And start HyperFrames prompts with `/hyperframes` (or invoke the skill another way for non-Claude agents) — it loads the routing + composition context explicitly so the agent picks the right workflow and gets the rules right the first time.
To skip the picker and install everything (core + every workflow) in one shot, run `npx hyperframes skills`. And start HyperFrames prompts with `/hyperframes` (or invoke the skill another way for non-Claude agents) — it loads the routing + composition context explicitly so the agent picks the right workflow and gets the rules right the first time.
</Tip>
+39 -3
View File
@@ -240,7 +240,11 @@ describe("hyperframes skills", () => {
"add",
"https://github.com/heygen-com/hyperframes",
"--skill",
"*",
"hyperframes",
"--skill",
"hyperframes-core",
"--skill",
"pr-to-video",
...GLOBAL_ARGS_TAIL,
],
],
@@ -253,7 +257,11 @@ describe("hyperframes skills", () => {
"add",
"https://github.com/heygen-com/hyperframes",
"--skill",
"*",
"hyperframes",
"--skill",
"hyperframes-core",
"--skill",
"pr-to-video",
...GLOBAL_ARGS_TAIL,
],
],
@@ -270,7 +278,11 @@ describe("hyperframes skills", () => {
"add",
"https://github.com/heygen-com/hyperframes",
"--skill",
"*",
"hyperframes",
"--skill",
"hyperframes-core",
"--skill",
"pr-to-video",
...GLOBAL_ARGS_TAIL,
],
],
@@ -799,6 +811,30 @@ describe("hyperframes skills update <names>", () => {
expect(await commandExitCode()).toBe(0);
});
it("bare `skills` offline never falls back to the wildcard: warns and installs the pinned core set", async () => {
setPlatform("linux");
const { checkSkills, presentSkills, FALLBACK_CORE_SKILLS } =
await import("../utils/skillsManifest.js");
const clack = await import("@clack/prompts");
vi.mocked(clack.log.warn).mockClear();
vi.mocked(checkSkills).mockRejectedValue(new Error("offline"));
vi.mocked(presentSkills)
.mockImplementationOnce(() => [])
.mockImplementation((names: readonly string[]) => [...names]);
const { default: skillsCmd } = await import("./skills.js");
await skillsCmd.run?.({ args: {}, rawArgs: [], cmd: skillsCmd } as never);
const args = state.spawnCalls[0]?.args ?? [];
// The upstream `*` would rediscover the repo-internal skills (26 vs 20).
expect(skillFlagValues(args)).not.toContain("*");
expect(skillFlagValues(args).sort()).toEqual([...FALLBACK_CORE_SKILLS].sort());
expect(vi.mocked(clack.log.warn)).toHaveBeenCalledWith(
expect.stringContaining("published skill set"),
);
expect(await commandExitCode()).toBe(0);
});
// The `check || update` CI contract: offline, a bare update can't verify
// freshness — exiting 0 would let the chain pass while everything stays
// stale. It must fail loudly instead.
+23 -3
View File
@@ -301,7 +301,12 @@ export interface UpdateSkillsResult {
* - the requested names (a workflow being routed to, e.g. `pr-to-video`),
* - the core set (entry router + shared domain skills — see skillsManifest),
* - with `refreshInstalled`, whatever is already installed (refreshed, so an
* update never *expands* a deliberate partial install).
* update never *expands* a deliberate partial install),
* - with `all`, every skill the manifest publishes. The upstream `*` wildcard
* is not used on any path: it also sweeps up the repo-internal skills under
* `.claude/skills` / `.agents/skills` (26 installed vs 20 published,
* observed 2026-09-04). Offline, where the published set is unknowable, the
* full install warns and degrades to the pinned core set instead.
*
* Only targets that are actually missing or outdated are passed to
* `skills add` (one spawn, one `--skill` flag per name); when everything is
@@ -313,6 +318,8 @@ export async function updateSkills(
opts: {
requested?: readonly string[];
refreshInstalled?: boolean;
/** Every skill the manifest publishes (bare `hyperframes skills`). */
all?: boolean;
strict?: boolean;
cwd?: string;
} = {},
@@ -347,7 +354,19 @@ export async function updateSkills(
}
check = null; // manifest unreachable (offline / rate-limited) — presence mode below
}
if (!check) return updateSkillsOffline(requested, { strict, cwd: opts.cwd });
if (!check) {
if (opts.all) {
// The published set is unknowable offline. Say so and degrade to the
// pinned core set — never the upstream wildcard, which would quietly
// reinstate the 26-skill sweep this path exists to avoid.
clack.log.warn(
c.warn(
"Can't resolve the published skill set (manifest unreachable) — installing the pinned core set only. Re-run `hyperframes skills` online for the full set.",
),
);
}
return updateSkillsOffline(requested, { strict, cwd: opts.cwd });
}
// "removed" entries are lock-attributed leftovers, not manifest skills —
// they are `skills update`'s prune concern, never an update target.
@@ -365,6 +384,7 @@ export async function updateSkills(
const targets = manifestSkills.filter(
(s) =>
opts.all === true ||
requested.includes(s.name) ||
isCoreSkill(s.name) ||
(opts.refreshInstalled === true && s.status !== "missing"),
@@ -785,7 +805,7 @@ export default defineCommand({
// the positional so bare `hyperframes skills` installs, while
// `hyperframes skills check|update` does not also re-install.
if (!args._?.[0]) {
await installSkills("*");
await updateSkills({ all: true });
// Same as updateSkills: a full install supersedes the background
// nudge's cached pre-install verdict.
invalidateSkillsCache();