Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 45 additions & 1 deletion packages/cli/src/commands/upgrade.project.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,51 @@ vi.mock("../utils/updateCheck.js", async (orig) => ({
})),
}));

import { upgradeProjectPins } from "./upgrade.js";
import { resolveProjectArgs, upgradeProjectPins } from "./upgrade.js";

describe("resolveProjectArgs", () => {
it("reclaims a flag eaten as the --project value", () => {
expect(resolveProjectArgs("--check", { check: false, json: false })).toEqual({
dir: ".",
check: true,
json: false,
});
expect(resolveProjectArgs("--json", { check: false, json: false })).toEqual({
dir: ".",
check: false,
json: true,
});
});

it("defaults to the current directory for a bare or boolean --project", () => {
expect(resolveProjectArgs(true, { check: true, json: false })).toEqual({
dir: ".",
check: true,
json: false,
});
expect(resolveProjectArgs("", { check: false, json: false })).toEqual({
dir: ".",
check: false,
json: false,
});
});

it("passes a real directory through untouched", () => {
expect(resolveProjectArgs("apps/site", { check: false, json: true })).toEqual({
dir: "apps/site",
check: false,
json: true,
});
});

it("drops an unrelated eaten flag instead of treating it as a directory", () => {
expect(resolveProjectArgs("--yes", { check: false, json: false })).toEqual({
dir: ".",
check: false,
json: false,
});
});
});

describe("upgradeProjectPins", () => {
const dirs: string[] = [];
Expand Down
32 changes: 28 additions & 4 deletions packages/cli/src/commands/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ export default defineCommand({
const checkOnly = args.check === true;

if (args.project !== undefined) {
const dir = typeof args.project === "string" && args.project.length ? args.project : ".";
const res = await upgradeProjectPins(resolve(dir), { json: useJson, check: checkOnly });
if (useJson) {
const p = resolveProjectArgs(args.project, { check: checkOnly, json: useJson });
const res = await upgradeProjectPins(resolve(p.dir), { json: p.json, check: p.check });
if (p.json) {
console.log(JSON.stringify(withMeta(res), null, 2));
return;
}
printProjectPinResult(res, checkOnly);
printProjectPinResult(res, p.check);
return;
}

Expand Down Expand Up @@ -157,6 +157,30 @@ function printManualCommands(displayCmd: string, npxFallback: string): void {
clack.outro(c.success("Run one of the commands above to upgrade."));
}

/**
* citty parses `--project` as a string option, so a bare `--project` followed
* by another flag consumes that flag as its value (`upgrade --project --check`
* arrives here as project="--check"). A leading dash can never be a real
* directory argument, so reclaim the eaten token as the flag the user wrote
* and fall back to the current directory.
*/
export function resolveProjectArgs(
project: string | boolean,
opts: { check: boolean; json: boolean },
): { dir: string; check: boolean; json: boolean } {
if (typeof project !== "string" || project.length === 0) {
return { dir: ".", check: opts.check, json: opts.json };
}
if (project.startsWith("-")) {
return {
dir: ".",
check: opts.check || project === "--check",
json: opts.json || project === "--json",
};
}
return { dir: project, check: opts.check, json: opts.json };
}

export async function upgradeProjectPins(
dir: string,
opts: { json: boolean; check: boolean },
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/templates/_shared/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ npx hyperframes docs <topic> # reference docs in terminal
> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground
> command — it will time out and the server will die, breaking the browser preview.

> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project --check` (shows the delta), then `npx hyperframes@latest upgrade --project` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.
> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project . --check` (shows the delta), then `npx hyperframes@latest upgrade --project .` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.

## Documentation

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/templates/_shared/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ npx hyperframes docs <topic> # reference docs in terminal
> In Claude Code, always run it with `run_in_background: true`. Never run it as a foreground
> command — it will time out and the server will die, breaking the browser preview.

> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project --check` (shows the delta), then `npx hyperframes@latest upgrade --project` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.
> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project . --check` (shows the delta), then `npx hyperframes@latest upgrade --project .` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.

## Documentation

Expand Down
4 changes: 2 additions & 2 deletions skills-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
"files": 1
},
"hyperframes": {
"hash": "07dfc1f0b31ff8ed",
"hash": "de6908d7047b8702",
"files": 6
},
"hyperframes-animation": {
"hash": "78f333feba8bd836",
"files": 102
},
"hyperframes-cli": {
"hash": "05f7c65e6f9db100",
"hash": "79ab5a7856a42d68",
"files": 11
},
"hyperframes-core": {
Expand Down
2 changes: 1 addition & 1 deletion skills/hyperframes-cli/references/upgrade-info-misc.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ npx hyperframes upgrade --yes # print upgrade commands without promptin

Compares the installed CLI version against npm latest.

`--project [dir]` bumps a **project's** pinned scripts instead of the global install: it rewrites every `npx …hyperframes@<version>…` in `<dir>/package.json` (default cwd) to npm-latest. Always invoke it unpinned (`npx hyperframes@latest upgrade --project`) — a project scaffolded on an old CLI stays frozen otherwise. `--project --check` reports the delta without writing; add `--json` for `{ changed, from, to, path }`.
`--project [dir]` bumps a **project's** pinned scripts instead of the global install: it rewrites every `npx …hyperframes@<version>…` in `<dir>/package.json` (default cwd) to npm-latest. Always invoke it unpinned (`npx hyperframes@latest upgrade --project`) — a project scaffolded on an old CLI stays frozen otherwise. `--project . --check` reports the delta without writing; add `--json` for `{ changed, from, to, path }`. Pass the dir explicitly whenever another flag follows `--project` — on older releases a bare `--project` consumes the next flag as its directory value.

## compositions, docs

Expand Down
10 changes: 10 additions & 0 deletions skills/hyperframes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ Continue with source adapters in § 2. A direct or resumed workflow route skips

If a fresh request does not identify the subject or input, ask what the video is about before routing. Check preferences and recipes before asking anything (§ 4, step 1).

### Keep the project's CLI current

A scaffolded project pins `hyperframes@<version>` in its `package.json` scripts so renders stay reproducible; the pin never advances on its own, and a pinned run of an older CLI prints no warning about it. When resuming a project whose scripts carry a pin, probe once before the first render-affecting command:

```bash
npx hyperframes@latest upgrade --project . --check
```

The probe is read-only and reports the pin against the latest release; keep the explicit `.` — on older CLI releases a bare `--project` followed by another flag consumes that flag as its directory value. When it reports the project behind — or any CLI output already shows it (the stderr notice `This project pins hyperframes@… (latest …)`, or `_meta.updateAvailable: true` in a `--json` result from a pinned script) — apply with `npx hyperframes@latest upgrade --project .`, then verify with `npx hyperframes check`. A passing check confirms the project's compositions still validate on the new version — not that rendered output is frame-identical to the old pin — so a successful bump is never silent: name the old and new version in the run's summary. A project with no composition yet needs no verification. If the check fails, revert the `package.json` change, continue on the pinned version, and report which version the project stays on and why. Act on the signal rather than relaying it to the user; never leave a bumped pin unverified.

## 2. Adapt orthogonal inputs before routing

A Figma source changes **how assets and design enter the project**, not which workflow owns the deliverable.
Expand Down
Loading