Commit Graph
1998 Commits
Author SHA1 Message Date
Miguel Ángel 077de9bfe9 test(cli): the publish abort test checks the summary hint is absent (#6726) 2026-10-29 07:37:49 +00:00
Miguel Ángel ba8ffffa0b fix(cli): a failed render names its failure code for the Desktop app (#6725) 2026-10-29 07:36:32 +00:00
Miguel Ángel cf15892425 fix(cli): a snapshot still shows what playback shows, whatever other times are asked for (#6720)
* fix(cli): a snapshot still shows what playback shows, whatever other times are asked for

* test(cli): pin that still-capture pages ask for the skipped-frame replay
2026-10-29 06:44:51 +00:00
Miguel Ángel 98a28cc4b2 fix(cli): the lint summary points at hyperframes lint, not a flag only render takes (#6723) 2026-10-29 06:44:01 +00:00
Miguel Ángel 17f5d67643 chore: release v0.8.215 (#6719) 2026-10-29 06:19:54 +00:00
Miguel Ángel bb4deb8014 fix(cli): an unknown command like search says which command does that (#6710)
* fix(cli): an unknown command like search says which command does that

* fix(cli): the synonym hint is checked before the spelling match, so list, find and browse reach it
2026-10-29 05:03:43 +00:00
Miguel Ángel 2f8c4b1daf fix(cli): check says how many browser-playable copies are done while it makes them (#6712) 2026-10-29 05:02:58 +00:00
Miguel Ángel 11312b359b fix(cli): check says a lint error is why the browser pass was skipped (#6709)
* fix(cli): check says a lint error is why the browser pass was skipped

* style(cli): drop a doc comment the comment ratchet would count
2026-10-29 04:08:41 +00:00
Miguel Ángel f0c5db6ea2 fix(cli): capture names the network's proxy when it refuses the connection to the site (#6706)
* fix(cli): capture names the network's proxy when it refuses the connection to the site

* fix(cli): capture words a refused tunnel and an unreachable proxy apart, and echoes no raw address

* fix(cli): shorten the proxy failure comment
2026-10-29 04:08:34 +00:00
Miguel Ángel 6c17659337 fix(cli): check waits as long as the --timeout it was given before it counts as idle (#6705)
* fix(cli): check waits as long as the --timeout it was given before it counts as idle

* style(cli): shorten a comment line

* style(cli): drop the idleWindow doc comment the comment ratchet counts
2026-10-29 04:08:29 +00:00
James RussoandClaude Opus 5.5 1c3fcf4d23 feat(telemetry): identify signed-in CLI users by their HyperFrames user id, and say signed-in usage is not anonymous (#6703)
* docs(telemetry): say signed-in usage is linked to your account, not anonymous

After hyperframes auth login the CLI sends a PostHog $identify with the
account email (username as fallback) and merges the install's earlier
anonymous usage; media-use does the same when it finds a signed-in
account. Several places still called all telemetry anonymous. Say it is
anonymous until you sign in, in the CLI reference (media-use, --skill,
feedback, events, telemetry), the hyperframes-cli skill references, and
the CLI help and notice strings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat(telemetry): identify signed-in CLI users by their HyperFrames user id

The CLI and media-use identified a signed-in install by account email, so
CLI usage landed on a different PostHog person than the same account in
the HyperFrames web app, which keys on toUUID(username). Identify by that
same sha256-derived id instead, and $set email plus heygen_username on the
person. heygen_username is the cross-project join column for the Snowflake
event table (HeyGen's own project stores it as `name`). An account with no
username still falls back to the lowercased email.

Notices and docs now say usage is keyed on an id derived from the username
with email and username stored on it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* chore(telemetry): trim comments to the package's comment share

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* feat(telemetry): put heygen_username on every event while signed in

The Snowflake event sync may not carry PostHog person properties, so a
$set alone does not let an event row join across projects. The CLI now
adds the signed-in HeyGen username (raw, from ~/.heygen/credentials) to
every event and drops it once the credentials hold no user; media-use
does the same on each event it sends.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* ci: count auth/paths.ts as an edit-accuracy bench input

Telemetry now imports it to read the signed-in username.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(telemetry): stat and read the credentials through one file handle

CodeQL flagged the stat-then-read as a file system race.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(telemetry): key signed-in events on the account id; fix stale anonymity copy

Genesis review on #6703:
- telemetry --help listed 'Any personally identifiable information' as
  never collected, and media-use's meta.md still said anonymous.
- Installs already identified by email under an older CLI never merge
  into the new person, so send signed-in events with distinct_id = the
  account id (back to the install id after logout), in the CLI and in
  media-use, and stop claiming earlier usage always merges.
- A user block with no credential no longer counts as signed in.
- A UUID-shaped username passes through unhashed, as on the web.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-29 02:57:48 +00:00
Jerrai 4fa5f54519 fix(cli): audio windows read beats off the whole clip; beats snap to the signal and mark silence (#6692)
* fix(cli): audio windows read beats off the whole clip; beats snap to the signal and mark silence

A film query that covers part of a clip measures beats, downbeats, bpm and sections on the
whole clip and cuts them to the window, as #6653 did for kind, so a window under 6 s still
has its beats.

One step after every provider ladder moves a provider's beats and downbeats onto the
signal's own beats within a quarter beat (the snapping api did alone) and marks each with
`silent`, true where the beat falls in silence. Beats are marked, never dropped; the JSON
change is the additive `silent` array.

— Jerrai

* fix(cli): beats with no pulse are unheard; a window with none answers no steady beat

Review fixes for #6692:
- The per-beat flag is now `unheard` (renamed from `silent`, not released): true in silence
  or where no onset falls within 50 ms of the beat, so hiss, room tone or a held pad no
  longer pass as beats.
- A window keeps the whole clip's beats only when at least two of its beats are heard;
  otherwise it answers "no steady beat" as before, with no downbeats and no bpm.
- A window's bpm is the tempo of its heard beats, never the whole clip's.
- Comment lines trimmed so analyze.ts, run.ts and api.ts are at or under main's share.

— Jerrai

* fix(cli): a beat is unheard only where two beats in a row have no onset

Scored on the ground-truth set, flagging every onset-less beat also flagged heard beats the
onset detector misses: the drop downbeat after a snare roll, single beats under narration,
quiet beats late in a fade. A pulse gap now needs two onset-less beats in a row; hiss, room
tone and held pads span many beats and stay flagged, and silence is flagged as before.

— Jerrai

* fix(cli): a window with no pulse on the clip's grid reads its own beats, as before

Genesis found a window over a section far under the clip's loudest part answered "no steady
beat" where main, reading the window alone, found every beat: the clip's grid and onsets
skip a section that quiet (from about 40 dB under, for clicks). When a window keeps fewer than
two heard beats from the clip's grid, its beats, downbeats and bpm now come from the window
alone, as they did on main. Hiss still answers "no steady beat" on its own.

The hiss test's noise() repeats every 10,466 samples (0.22 s at 48 kHz), which the window's
own reading took for a pulse; that test now uses a xorshift generator.

— Jerrai

* fix(audio): unheard comes from level-free rhythm evidence, so quiet music stays heard

A beat was flagged by an absolute -50 dBFS silence and by onsets under 10% of the clip's
loudest hit, so music 30 dB or more under the loudest part read as unheard. Now:
- core localBeatEvidence reads the cached standardised onset curve over the 6 s around each
  beat: the tracker's steady-beat confidence there (clarity at the local beat lag times the
  beat's own lift) and the beat's lift over the span's mean.
- A beat is unheard in digital silence (-80 dBFS), or when its span keeps no steady beat
  (confidence under 0.15) and its own onset does not stand out (lift under 1.5). Lift alone
  keeps beats under narration heard, where syllables lower the span's clarity.
- A window keeps the clip's beats when at least two, and at least half, of them are heard.

— Jerrai
2026-10-29 01:52:48 +00:00
Miguel Ángel a5496b1887 fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot (#6701)
* fix(producer): a BeginFrame capture that draws no first frame in 20 s switches to screenshot

* test(cli): cover the Chrome launch failure messages a failed render prints
2026-10-29 00:49:04 +00:00
Miguel Ángel cfa653af4d fix(cli): media-treatment --capability preset finds the presets area (#6700) 2026-10-28 23:27:27 +00:00
Miguel Ángel ac449555f4 fix(cli): a Windows render no longer prints taskkill's already-exited message (#6698)
* fix(cli): a Windows render no longer prints taskkill's already-exited message

* fix(cli): fold the taskkill note into the hidden() doc comment
2026-10-28 23:04:48 +00:00
Miguel Ángel 0620067eb0 fix(cli): snapshot reads https video in the Desktop app instead of failing at ffprobe (#6699)
* fix(cli): snapshot reads https video in the Desktop app instead of failing at ffprobe

* fix(cli): give a remote clip download the same 30 s a stalled host had before
2026-10-28 22:52:28 +00:00
Miguel Ángel 81959201f0 fix(cli): a stopped model download resumes on the next run instead of starting over (#6694)
* fix(cli): a stopped model download resumes on the next run instead of starting over

* fix(cli): resume whisper models too and take over stale download locks safely

* style(cli): shorten a download lock doc line
2026-10-28 21:14:09 +00:00
Miguel Ángel 2c3d1e07fe fix(cli): media-treatment finds an area by the name its overview prints (#6675)
* fix(cli): media-treatment finds an area by the name its overview prints

* fix(cli): drop the comment lines the ratchet counts
2026-10-28 20:29:47 +00:00
Miguel Ángel 9830059bdb fix(engine): downloads refuse hosts that resolve to private addresses (#6571)
* fix(engine): downloads refuse hosts that resolve to private addresses

* fix(core): shorten the env proxy doc comment

* test(producer): remote-media tests stub the download fetch seam
2026-10-28 20:08:50 +00:00
Miguel Ángel 9f65efb19c fix(cli): check no longer reports outline text as not painted (#6693) 2026-10-28 19:03:27 +00:00
Miguel Ángel ec10aae54f fix(cli): beats --json answers with no track when the composition has no music (#6690)
* fix(cli): beats --json answers with no track when the composition has no music

* fix(cli): drop a comment the name already says
2026-10-28 18:57:53 +00:00
Miguel Ángel 203a08dd7c fix(cli): check no longer treats a transparent canvas as covering the frame (#6681)
* fix(cli): check no longer treats a transparent canvas as covering the frame

* fix(cli): a canvas with an opaque CSS background still counts as covering
2026-10-28 17:48:26 +00:00
Vance Ingalls 4719543e97 chore: release v0.8.214 (#6684) 2026-10-28 10:17:04 -07:00
Vance IngallsandClaude Opus 5.5 09b1290474 feat(core): grade groups grade a whole lane from one hf-color-group (#6678)
An inert <hf-color-group> holds a lane's data-color-grading; <video>, <img>
and <canvas> join with data-color-group. A member's own grading overrides
its group's. The runtime discovers members, regrades them when the group's
grading or a member's data-color-group changes, and ungrades them when the
group element or its grading goes, drawing only members on screen.

Render keeps group-graded video at full size like directly graded video,
and lint accepts (and still validates) grading on hf-color-group.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 10:10:42 -07:00
Vance IngallsandClaude Opus 5.5 c9017f45f4 feat(core): bake a colour grade into a 3D LUT (.cube) (#6679)
bakeHfColorGradingCube evaluates the runtime shader's colour path on the
CPU (primary, tonal wheels, RGB and hue curves, secondaries, output LUT,
intensity mix) through the same 8-bit texture quantisation, and writes a
red-fastest .cube for ffmpeg's lut3d. Grades with spatial stages (grain,
vignette, effects) throw, as does a grade whose LUT was not passed in.

A Chromium test draws a 4,096-colour lattice through the real runtime
and compares every tile with the bake.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 16:45:15 +00:00
Miguel Ángel c6af88d2e6 chore: release v0.8.213 (#6673) 2026-10-28 16:40:44 +00:00
Miguel Ángel 5b5633fbbc fix(cli): website capture ends with what it has instead of running until it is killed (#6662)
* fix(cli): website capture stops inlining slow images and stylesheets and says what it skipped

* fix(cli): a capture ends at four minutes with its partial bundle unless told otherwise

* fix(cli): a capture's deadline starts once its browser is up, and the kept-image count is exact

* fix(cli): an image whose fetch the deadline cut off always counts as kept
2026-10-28 13:56:48 +00:00
Miguel Ángel cd3b90d1ed fix(cli): snapshot decodes each video once for all the times it shows (#6667)
* fix(cli): snapshot decodes each video once for all the times it shows

* fix(cli): shorten a comment line
2026-10-28 13:48:44 +00:00
Miguel Ángel a1733f4e40 fix(cli): check treats text inside a closed details element as not rendered (#6669) 2026-10-28 13:41:27 +00:00
Miguel Ángel 8fe0ce660b fix(cli): check probes each audio file once, however many clips are cut from it (#6654)
* fix(cli): check probes each audio file once, however many clips are cut from it

* fix(cli): drop an empty stray test file from the audio probe change
2026-10-28 13:39:17 +00:00
Miguel Ángel 5a9fdd524b fix(cli): a late teardown error no longer turns a finished command into exit 1 (#6659)
* fix(cli): a late teardown error no longer turns a finished command into exit 1

* test(cli): keep only the live invariant in the late-rejection test comment

* test(cli): shorten the late-rejection test comment
2026-10-28 13:13:07 +00:00
Xuanru LiandJerrai 1df31d8717 fix(audio): a sound is voice only with a voice-range pitch, judged on the whole clip (#6653)
* fix(audio): a sound is voice only with a voice-range pitch, judged on the whole clip

Two ways the kind guess called a non-voice sound voice:

- A short hit, click or whoosh can have a speech-shaped envelope. classify() now also
  measures the share of 40 ms frames with a 70-500 Hz fundamental (YIN, moved from the
  CLI into core) and calls a sound voice only when that share reaches 0.15. Speech
  measured 0.22-0.43; impacts, clicks and whooshes 0.
- A range query judged a clip's kind on the slice it covered, so 1.7 s of a music bed
  read as voice. A film clip's kind is now judged on all of the clip that plays.

Even-paced speech (a 64 s TTS take) moves less at syllable rate and was called music;
with no beat and a voiced share of 0.25 or more it is now voice.

* chore(audio): keep each touched file's comment share at or under its base

The Comments check failed on run.ts, targets.ts and audioAnalysis.ts because the
PR added comment lines to files whose share may only go down. Drops the added
doc comments; no code change.

— Jerrai

* fix(audio): preserve clip evidence and stereo pitch during analysis

* fix(audio): bound and cache whole-clip kind analysis

* fix(audio): retain native-rate kind evidence when available

---------

Co-authored-by: Jerrai <jerrai@heygen.com>
2026-10-28 12:48:08 +00:00
Miguel Ángel eba596d026 chore: release v0.8.212 (#6660) 2026-10-28 12:29:35 +00:00
James RussoandClaude Opus 5.5 2d3033c719 fix(cli): exit 2 for remaining flag validation errors (#6639)
* fix(cli): exit 2 for remaining flag validation errors

Flag and argument checks that commands make themselves still exited 1 and
reported runtime_error under --json. They now take the existing usage-error
path (failUsage / CliUsageError): exit 2 and a usage_error envelope. This
covers lambda and cloudrun render/render-batch (--width/--height, --fps,
positive-number, enum and --output-resolution flags, unknown subcommands),
capture, feedback, benchmark --runs, tts, init, snapshot, compare,
grade-compare, check, normalize-audio, media-treatment, play/present --port,
models, docs, browser and telemetry unknown subcommands, cloud render
conflicting inputs, cloud delete without --no-confirm, and inline --variables.
A few checks now run before project or stack lookup so a bad flag is reported
first. Runtime failures (missing files, network, render errors) still exit 1.

An unattended spinner now writes its start and progress text as plain stderr
lines, not only the outcome, so tools watching init can see "Transcoding".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): reject a bad snapshot --frames, quiet spinner progress, drop added comments

- `snapshot --frames abc` (or 0, -3, 2.5) is now a usage error: exit 2, and a
  usage_error envelope under --json. It used to fall back to 5 frames silently.
- An unattended spinner prints its start line once and its outcome, but no
  longer one line per progress update, which flooded stderr during
  remove-background and the Chrome download.
- Removes the comment lines this branch added, so the comment ratchet passes.
- The render --json test now expects exit 2 and usage_error for bad inline
  --variables.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 11:27:49 +00:00
Miguel Ángel b84762230f fix(cli): snapshot warns when a video on screen has no source to read a frame from (#6656) 2026-10-28 11:14:19 +00:00
James RussoandClaude Opus 5.5 85efaece7e fix(cli): every --json failure answers with one envelope, and --json only where supported (#6622)
* fix(cli): every --json failure answers with one envelope, and --json only where supported

Under --json, failures came back as a bare string (`"error": "Command failed"`
from lint), a command-specific shape, or nothing at all on stdout (info,
compositions, render, snapshot, cloud get). Now every failure is
`{"ok": false, "error": {"code", "message", "hint"?}}`:

- jsonFailure() in utils/jsonError.ts builds it. The commands that printed
  `{ok: false, error: "<text>"}` use it, keeping their extra fields (lint's
  findings, validate's errors). A refused timeline edit now writes it to stdout
  instead of stderr, and keeps its `reason` and `fix` fields.
- When a --json run fails without writing anything to stdout, cli.ts writes the
  envelope itself: usage errors carry their own code (unknown_flag,
  unknown_command, json_unsupported, confirmation_required), anything else is
  runtime_error with the message the command printed on stderr.

--json is no longer accepted by every command. A command supports it by
declaring a `json` arg; the others reject it with exit 2 and the code
json_unsupported, instead of accepting the flag and printing prose.

The contract test now checks every command's --json path, and packages/cli
gets an AGENTS.md with the rules: stdout is data, stderr is messages, never
prompt without a TTY, exit 2 for usage errors, --json always returns an
envelope.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): put the real failure in the --json envelope, never the generic text or a notice

lint --json and check --json in a directory with no project answered
"message": "Command failed". The project check reports the problem, then
throws a presented "Command failed" with the real error as its cause, and the
command's own catch serialized that generic text. failureMessage() now returns
the cause, or else what the command reported through errorBox, and every
command catch that writes a --json failure uses it (lint, check, validate,
layout, snapshot, compare, grade-compare, media-treatment, tts,
remove-background, add, skills, lambda policies). compare, grade-compare and
add share failCaught() for that catch.

The boundary envelope follows the same order: the error's own message unless
it is the generic one, then its cause, then the errorBox title and hint, and
only then the stderr text. Notices (the telemetry disclosure, deprecation
lines, diag progress) are no longer captured, so validate, layout and inspect
no longer put "is deprecated" in the error message.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): render --json keeps human output off stdout so a failure answers with the envelope

Under --json a single render printed its header, lint findings and progress on
stdout, so a later failure such as `render --variables '{bad' --json` ended
with no JSON envelope. --json now silences that output the way --batch --json
already did, and --variables is parsed before the browser check so a bad value
fails without a Chrome download.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): a --strict lint abort says why, even under --json

Under --json the render is quiet, so a --strict lint abort printed nothing and
the envelope said only "Command failed". The abort is now an error box on
stderr in every mode, naming the error and warning counts and the first
finding, and the --json envelope carries the same message. Lint now runs
before the browser check, so the abort never waits on a Chrome download.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cli): time the render setup cancel from the wrapper's exit

The 1 s budget started when the command did. With lint now running before the
browser check, its subprocess (about 250 ms here) sat inside that budget, and
the test failed once under load. The budget now starts when the wrapper exits,
which is the latency the test is about.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 10:42:10 +00:00
Miguel Ángel b6eaed7cae fix(cli): a check page forces a WebGL drawing buffer but keeps no list of 2d canvases (#6650) 2026-10-28 10:41:22 +00:00
Miguel Ángel ecad35a225 fix(cli): resume a model download that stalls partway instead of failing (#6649)
* fix(cli): resume a model download that stalls partway instead of failing

* fix(cli): pin the idle-only timeout; one-line doc for the resume

* fix(cli): resume from the bytes on disk and check the size before caching

* refactor(cli): split the download response handling into named steps
2026-10-28 10:27:00 +00:00
James RussoandClaude Opus 5.5 e2c9151320 fix(cli): add help [cmd], suggest the closest command or flag, and quiet the nags (#6621)
* fix(cli): add help [cmd], suggest the closest command or flag, and quiet the nags

`hyperframes help` printed "Unknown command help" with raw ANSI codes and no
suggestion. It is now an alias: `help [cmd...]` is `[cmd...] --help`.

An unknown command (at the root or inside a group like `cloud`) prints
"Unknown command: rendr", "Did you mean 'hyperframes render'?" and the group's
usage, all on stderr, and exits 2. An unknown flag suggests the flag it most
likely means ("Did you mean --output?"). Usage text printed for a usage error
now goes to stderr instead of stdout, and citty's colors are stripped from
help whenever this CLI's own colors are off.

No update, skills or app-history notice is appended to a usage error. The
skills notice shows at most once a day per project (timestamps in
~/.hyperframes/skills-notice.json) and, in an unattended run, only when a core
skill is missing.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): reject an unknown top-level command without loading every command

citty looks for an alias on every registered command before it reports an
unknown name, which loads all of them. From a source checkout without the
studio packages built, that meant `hyperframes rendr` failed in preview's
build check with exit 1 instead of suggesting `render`. The name is now
checked against the registry first.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): help with a mistyped subcommand reports it like the command itself

`hyperframes help cloud rendr` printed cloud's help and exited 0, while
`hyperframes cloud rendr` exits 2 with a suggestion. Both now exit 2 with the
suggestion. A group that also takes positional arguments (timeline takes a
project directory) still shows its help for a word it does not know.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 09:39:06 +00:00
Miguel Ángel 3d1c0764a0 fix(cli): check prints a progress line as each pass after overlap begins (#6646)
* fix(cli): check prints a progress line as each pass after overlap begins

* fix(cli): check counts finished passes and drops the comment the ratchet flagged
2026-10-28 09:37:17 +00:00
Miguel Ángel 5ed694264e chore: release v0.8.211 (#6623) 2026-10-28 09:30:08 +00:00
Miguel Ángel 8e63faf846 perf(engine): a --gpu render probes the GPU encoders once, not twice (#6638)
* perf(engine,cli): a machine's GPU encoder probe runs once, not on every render

* refactor(engine): one shared probe per process, no answer kept on disk
2026-10-28 08:44:28 +00:00
James RussoandClaude Opus 5.5 0bdc128cb5 fix(cli): never prompt or auto-install when nobody is at the terminal (#6617)
* fix(cli): never prompt or auto-install when nobody is at the terminal

publish and upgrade called the confirmation prompt with no terminal check.
With stdin from /dev/null, publish exited 13; with an open pipe it hung.
Unattended and without --yes, both now exit 2 before doing any work, and
stderr names --yes (upgrade also names --check).

All prompts and spinners now go through ui/prompts.ts, the only module allowed
to import @clack/prompts (a lint rule and a contract test enforce it). Its
confirm, select, text and password refuse to prompt in an unattended run, and
its spinner prints only the final line, on stderr. init, feedback, catalog and
cloud delete decide whether to ask with isUnattended() instead of a TTY check
of their own.

The background auto-install no longer starts in an unattended run, and the
update notice ("Run: npm install -g ...") is not shown while an install for
that version is pending. CI now counts as set for any value but "false" or "0".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cli): neutralize agent detection in the catalog offer tests through isAgentDriven

Attendance now reads isAgentDriven(), so that is what the catalog tests stub to
stay independent of the agent running the suite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 07:56:20 +00:00
Miguel Ángel 6cf4b56697 fix(engine): a render on Windows opens no console windows (#6613)
* ci(windows): probe that a render opens no visible window

* ci(windows): the probe names what opened each window

* ci(windows): the probe spawns each binary alone, with and without detached

* ci(windows): the probe logs the processes started during the render

* ci(windows): the probe also spawns Chrome one consoleless hop down

* ci(windows): the probe logs each child started without windowsHide

* ci(windows): the probe logs every child process with its windowsHide and detached

* fix(engine): Chrome keeps the CLI's console on Windows, so a render opens no window

* fix(cli): every child the CLI starts on Windows is hidden unless it opts out

* ci(windows): the probe bisects Chrome's flags and keeps each command's output

* ci(windows): the probe names the Chrome process behind each window

* ci(windows): the probe loads a page in Chrome directly and one hidden hop down

* ci(windows): the probe prints the Chrome main command line and renders with the reporter's flags

* ci(windows): the probe narrows the CLI's Chrome arguments to the ones that open a window

* ci(windows): the probe launches the CLI's Chrome arguments one hidden hop down

* ci(windows): the probe launches Chrome through the engine's browser pool

* ci(windows): the probe varies what the engine passes to Puppeteer

* fix(engine): renders on Windows launch Chrome with a Puppeteer that keeps it attached

* refactor(cli): hideChildConsoles copies its arguments and keeps one comment line
2026-10-28 07:48:40 +00:00
Miguel Ángel b288ce0187 fix(cli): snapshot prints a progress line as each frame starts (#6618)
* fix(cli): snapshot prints a progress line as each frame starts

* test(cli): contact sheet tests ignore snapshot's progress lines
2026-10-28 06:48:26 +00:00
James RussoandClaude Opus 5.5 22e6da0233 feat(cli): define every command through one factory and test them all as an agent (#6616)
* feat(cli): define every command through one factory and test them all as an agent

Every command now comes from defineCommand in utils/defineCommand.ts. A lint
rule bans importing defineCommand from citty anywhere else, and a new contract
test walks the command registry in cli.ts, checks each command was built by the
factory, and runs it the way a coding agent does: no terminal, stdin closed,
CI=1, CLAUDECODE=1 and a home folder of its own.

Usage errors now exit 2: unknown flags, extra positionals, and citty's own
parse errors (missing argument, bad value, unknown or missing subcommand).
An unknown flag names the help command instead of printing nothing else.

isUnattended() in utils/attendedTerminal.ts is the one answer to "can a person
respond here". It is true without a TTY on stdin or stdout, under CI (any value
except "false" or "0"), when the existing agent detection finds an agent, or
with HYPERFRAMES_AGENT=1. Color is off when it is true.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* test(cli): spawn preview in the agent-mode test only when the studio packages are built

From source, preview refuses to load until the studio workspace packages are
built, and the CLI test jobs do not build them. preview is still checked for
being defined with the shared factory.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* fix(cli): count only agent-run shells as unattended, and exit 2 for missing arguments

isUnattended() used the telemetry fingerprint detectAgentRuntime(), which also
matches a person typing in Cursor's or Windsurf's terminal (TERM_PROGRAM) and
any Replit shell (REPL_ID, REPLIT_USER). It now uses isAgentDriven(), which
keeps only markers an agent sets on the shells it runs: CURSOR_AGENT counts,
TERM_PROGRAM=cursor, TERM_PROGRAM=windsurf and the Replit variables do not.

The hand-written argument checks in lambda, cloudrun, lambda policies,
capture, remove-background, init (--resolution), tts (--lang) and cloud render
(--idempotency-key, --dry-run) exited 1. They are usage errors and now exit 2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 06:28:44 +00:00
Miguel Ángel 815ff9ec8d perf(cli): check's craft pass skips clips the runtime has hidden (#6627) 2026-10-28 06:25:14 +00:00
Miguel Ángel bf7ebe961e fix(cli): local servers never bind a port Chromium refuses to load from (#6620) 2026-10-28 05:16:42 +00:00
Miguel Ángel 4f0bdfb520 fix(cli): a new blank project starts on a black frame, not grey (#6614) 2026-10-28 04:45:34 +00:00
32e171ca91 chore(codegen): resync cloud client from EF 354b4cf4 (#6608)
* codegen: resync cloud client from EF 354b4cf4

* chore(codegen): keep page_motion_blur in the generated render request

The CLI already sends page_motion_blur (#6087), and experiment-framework adds
it to the public spec in a pending change. Keep it here so the sync does not
break the CLI typecheck in the meantime.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: James <james.russo@heygen.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-28 04:43:30 +00:00