@gitruck/cli 1.3.7 → 1.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,7 +15,7 @@ Once installed, a single sentence in any agent invokes a gtrk skill:
15
15
  | ![Calling gtrk from an agent, example 1](../assets/agent-example-1.png) | ![Calling gtrk from an agent, example 2](../assets/agent-example-2.png) |
16
16
  | ![Calling gtrk from an agent, example 3](../assets/agent-example-3.png) | ![Calling gtrk from an agent, example 4](../assets/agent-example-4.png) |
17
17
 
18
- `gtrk install` installs the 19 bundled CLI skills (`gtrk-oralcut`·`gtrk-long2short`·`gtrk-splitter`·`gtrk-matrix`·`gtrk-mg`·`gtrk-composite`·`gtrk-ai-drama`·`gtrk-style-maker`·`gtrk-transcript`·`gtrk-tools`·`gtrk-music-visualizer`·`gtrk-cover`·`gtrk-travel-recap`·`gtrk-live-slicing`·`gtrk-talking-head`·`gtrk-narration`·`gtrk-voiceover`·`gtrk-food-recap`·`gtrk-vlog-docu`) into the agents detected on this machine. The mechanism matches lark-cli: gtrk hands its local skill sources to the generic `skills` CLI, which owns agent detection, directory mapping and update rules; gtrk no longer hardcodes per-vendor paths.
18
+ `gtrk install` installs the 20 bundled CLI skills (`gtrk-oralcut`·`gtrk-long2short`·`gtrk-splitter`·`gtrk-matrix`·`gtrk-mg`·`gtrk-composite`·`gtrk-ai-drama`·`gtrk-style-maker`·`gtrk-transcript`·`gtrk-tools`·`gtrk-music-visualizer`·`gtrk-cover`·`gtrk-travel-recap`·`gtrk-live-slicing`·`gtrk-talking-head`·`gtrk-narration`·`gtrk-voiceover`·`gtrk-food-recap`·`gtrk-vlog-docu`·`gtrk-ref-reading`) into the agents detected on this machine. The mechanism matches lark-cli: gtrk hands its local skill sources to the generic `skills` CLI, which owns agent detection, directory mapping and update rules; gtrk no longer hardcodes per-vendor paths.
19
19
 
20
20
  By default `~/.agents/skills` is the single source of truth, linked into each agent's compatible directory (junctions on Windows); where linking is unavailable the adapter falls back to copying. That way updates touch one canonical copy instead of letting duplicates drift apart. Common commands:
21
21
 
@@ -66,6 +66,7 @@ Then just say "**cut a version of this talking-head**", or explicitly pick `gtrk
66
66
  | ⑤ | `/gtrk-mg` | `gtrk mg` | **MG (incl. ov) goes on last** (stacked on the settled, composition-checked base layer) |
67
67
  | ⑥ | `/gtrk-composite` | `gtrk composite` | **Composite layer** (after subtitles, before export): turn chosen time windows into composite particles — one code particle takes over the whole frame for that span (picture changes per sentence, side-by-side contrasts, list cards, subtitle layout) while the talking head and B-roll are drawn into its slots; three entry modes (cover the whole film / let the AI pick / user-given ranges), candidates show up in the client outline's CP row; purely local, zero billing. On export, `gtrk composite bake` bakes them into video locally (the first run needs `gtrk deps install --browser`) |
68
68
  | — | `/gtrk-style-maker` | (no command, builds a show) | A one-time interview that builds your show's style system (skill family + show config, see next section) |
69
+ | 🔍 | `/gtrk-ref-reading` | `gtrk transcript` + `gtrk media` | **Reference reading**: someone else's video → word-level transcript + frame grids labelled with the spoken line + key motion frame by frame → records, system by system, which line each design event answers, in `ANALYSIS.md` / `TIMELINE.md` / `PROGRESS.md`; hand the result to `/gtrk-style-maker` to build a show. The viewing commands are local and free (only the transcription step is billed); **not part of the production SOP** |
69
70
  | ③′ | "**screen-recording PiP**" | `gtrk pip lay` | Mirror the talking-head rough cut's cut points onto a screen recording / second camera recorded in sync: lays a full-frame companion track + a muted picture-in-picture copy of the talking head (ellipse / rounded rectangle / heart / diamond / star mask, optional corner radius); purely local, zero billing; when alignment confidence is low it writes an alignment project for the client to drag into place, then `--resume` |
70
71
  | — | (wrap-up) | `gtrk render` | Render a gtrk project locally → finished mp4 (overlays and MG particles included; particles that miss the cache are cloud-rendered and billed, `--no-particles` skips them) |
71
72
  | ✂️ | `/gtrk-long2short` | `gtrk long2short` | Long-to-short rough cut: semantic segment selection + jump cuts → per-clip client/Jianying/Premiere projects (the raw file is never uploaded); **not part of the production SOP**, usable standalone at any time |
@@ -82,7 +83,7 @@ Then just say "**cut a version of this talking-head**", or explicitly pick `gtrk
82
83
  | 🧭 | `/gtrk-vlog-docu` | orchestrates `gtrk transcript` → `tool audio_tts_clone` → `split` → `matrix` → `mg` → `audio lay` → `subtitle lay` | **Documentary-vlog blueprint**: a batch of on-location footage → footage understanding, register choice and script, one sign-off → live-sound skeleton + narration voice-over + B-roll + subtitle layer + BGM run unattended → a client-ready project; alternates on-location sound with post-production narration, unlike the pure talking-head or pure voice-over chains |
83
84
 
84
85
  > **Skill vs command**: `/gtrk-mg` is the **brain** — it knows it belongs at SOP step ⑤ (MG only after all three B-roll sources have landed and the composition is checked), asks for your confirmation, and resolves which particle type to produce from the show config; `gtrk mg` is the **hands** — purely deterministic lint + track laying. You trigger the skill by talking, and the skill runs the command for you.
85
- > The 19 `/gtrk-X` skills above are **framework skills bundled with the CLI** (installed by `gtrk skills install`; the list is identical to the ones in "Using it with AI agents" and "Layout") — the 7 marked 🧭 are **composite blueprints** (one sentence runs a whole chain; they only orchestrate the single-command skills above and add no commands of their own); `/gtrk-long2short` independently drives long-to-short, `/gtrk-transcript` independently drives video/audio-to-transcript, `/gtrk-tools` covers only the single-shot tool family, `/gtrk-cover` handles covers, and none of the four belong to the production SOP; `/gtrk-ai-drama`·`/gtrk-style-maker`·`/gtrk-cover` are pure authoring skills (no command). Show-specific **visual style and content** come instead from your own show's production skills (created by `/gtrk-style-maker`, bound through the show config's `style.skills`) and are never hardcoded into these framework skills.
86
+ > The 20 `/gtrk-X` skills above are **framework skills bundled with the CLI** (installed by `gtrk skills install`; the list is identical to the ones in "Using it with AI agents" and "Layout") — the 7 marked 🧭 are **composite blueprints** (one sentence runs a whole chain; they only orchestrate the single-command skills above and add no commands of their own); `/gtrk-long2short` independently drives long-to-short, `/gtrk-transcript` independently drives video/audio-to-transcript, `/gtrk-tools` covers only the single-shot tool family, `/gtrk-cover` handles covers, `/gtrk-ref-reading` handles reference reading, and none of the five belong to the production SOP; `/gtrk-ai-drama`·`/gtrk-style-maker`·`/gtrk-cover` are pure authoring skills (no command). Show-specific **visual style and content** come instead from your own show's production skills (created by `/gtrk-style-maker`, bound through the show config's `style.skills`) and are never hardcoded into these framework skills.
86
87
 
87
88
  **How each lane's actual visuals/content get produced** — what the MG looks like, what tone the AI re-enactment has — is not hardcoded into the CLI. It comes from **your own show's production skills** (created interactively with `/gtrk-style-maker`, kept locally). They are bound through the show config's **`style.skills[].produces`** (the value is the lane name), and **general-purpose drivers** such as `gtrk mg` / `gtrk matrix` consume them accordingly. **The direction is: the CLI drives the show skills** — show skills only supply style/content and carry no "which command to run" orchestration; the framework only knows lanes and pipeline interfaces, and the look always belongs to your show. Without a show, the built-in defaults are used and everything still runs end to end.
88
89
 
@@ -232,6 +233,8 @@ This entry exists in the current development workspace. Check `gtrk matrix --hel
232
233
  | `--platforms <list>` | Comma-separated `youtube,vimeo,tiktok,bilibili`; requires `--online`. Defaults to all four registered platforms. Results depend on service and source availability |
233
234
  | `--online-session <name>` | Nonempty external-search batch name; requires `--online`. Continue within a batch; changing the name starts a new search and may incur new task charges |
234
235
 
236
+ In dispatch consumption, if a round yields **no layable candidates** while the project already has a `split/broll-plan.json` with candidates (for example the previous cloud or local plan), that plan is not overwritten: this round's plan (with per-request errors and review-only candidates) is saved as `split/broll-plan.online-attempt.json`, the `--json` result carries `plan_kept`, and `planPath` still points at the existing plan. The exit code stays non-zero.
237
+
235
238
  ### `gtrk transcript <local video|voice-over audio>`
236
239
 
237
240
  Turns a local video or voice-over audio file into a multi-level Markdown transcript. It accepts local file paths only: video is extracted — and audio input transcoded — to 16 kHz mono audio on your machine, and only that derivative is uploaded. The original file is never uploaded, and URLs or platform video downloads are not supported.
@@ -251,6 +254,31 @@ Live pricing is queried from the website's price table under `asr` before the ru
251
254
 
252
255
  > With `--json` it additionally drops a sentence-timecoded `<name>-transcript.json` next to the source file (`utterances[]{id,text,st,ed}` + `material_id` + `text_hash` + `duration`, field-for-field aligned with the transcript structure `gtrk split` consumes), which the `gtrk project init --transcript` fallback path can consume directly. **Do not re-run ASR here on TTS-synthesized voice-overs** — `gtrk project init --tts-task` fetches the server-side sentence timecodes directly, zero ASR and zero extra billing.
253
256
 
257
+ ### `gtrk media <subcommand>` — local footage viewer (evidence for reference reading)
258
+
259
+ Look at any local video by timecode or by spoken line: frame grids labelled with the transcript, frame-by-frame motion, visual-change candidates. **Purely local, free, offline, no API key**; reads local files only and never downloads links. Driven by `/gtrk-ref-reading`; also handy for checking your own renders.
260
+
261
+ | Subcommand | What it does |
262
+ |---|---|
263
+ | `probe <video>` | Duration, display size (pixel aspect and rotation applied), frame rate and VFR, audio presence |
264
+ | `lines <transcript>` | One line per utterance `u<id> start–end text`; `--find <phrase>` lists every occurrence with word-level times |
265
+ | `frames <video>` | Single frames by `--at` / `--every` / `--frames` / `--every-frame`; with `--transcript` or `--label` a label band is added below each frame |
266
+ | `tile <video>` | One labelled grid (≤60 cells) |
267
+ | `tiles <video>` | Paginated grids (`--cols × --rows`, default 3×3); `--ranges <json>` visits several intervals; `--every-frame` decodes each interval once and keeps every original frame |
268
+ | `boundaries <video>` | Visual-change candidates (`--rate` default 12, `--threshold` default 0.1, `--min-gap` merges neighbours); with `--transcript` each candidate notes which line it falls on. **Mechanical evidence, not shot detection** |
269
+ | `cut <video>` | Saves an evidence clip (`.mp4/.mov/.mkv`) |
270
+
271
+ ```bash
272
+ gtrk media tiles "reference.mp4" --frames 48 --cols 6 --rows 4 --cell 320 --transcript "reading/transcript.json" -o "reading/evidence/overview"
273
+ gtrk media tiles "reference.mp4" --around "can't afford it" --occurrence 1 --every-frame --cols 6 --rows 4 --transcript "reading/transcript.json" -o "reading/evidence/motion/stamp"
274
+ gtrk media boundaries "reference.mp4" --rate 4 --threshold 0.12 --transcript "reading/transcript.json" -o "reading/evidence/boundaries.json"
275
+ ```
276
+
277
+ - **Intervals**: `--start/--end` (seconds or `[hh:]mm:ss.mmm`), or `--around <phrase> [--occurrence n] [--padding s]` to locate by the spoken line (needs `--transcript`; a repeated phrase must name its occurrence — `lines --find` lists them).
278
+ - **Transcripts**: the `gtrk transcript --json` output (v1, with word-level times) works as is; `hypit.transcript@1` is also accepted. The transcript must belong to this video — a duration or file-name mismatch prints a warning on stderr.
279
+ - **Labels** (below the picture, so the original captions and graphics stay visible): source seconds · timecode (plus frame number in frame-by-frame mode) · utterance id / the word being spoken with its times / the whole sentence with the current word highlighted. Moments with no timed word are labelled as such, never inferred as silence. Labels are drawn with libass; an ffmpeg without the `ass` filter fails with a pointer to `gtrk deps install --ffmpeg`.
280
+ - **Output**: `-o` is required — nothing lands in the current directory or next to the source; existing paths are never overwritten, and `--force` only replaces this command family's own earlier output. `frames` / `tiles` directories include an `index.json` recording each cell's file, time, word and utterance. Frame-by-frame is capped at 1800 frames per call.
281
+
254
282
  ### `gtrk oralcut <raw>`
255
283
 
256
284
  | Parameter | Purpose | Default |
@@ -398,7 +426,7 @@ Each source becomes one low-resolution full-window preview candidate. `source-im
398
426
  >
399
427
  > **Material-on-disk self-check**: after writing back the project, the CLI read-only checks whether every `materials[].path` exists. Relative paths resolve against the directory containing the `.gtrk` file (`<output>/gtrk/`). `--json` emits `integrity:{ checked, counts, dangling:[…], danglingReferenced, danglingOrphan, external:[…], noPathIds:[…] }`; `dangling` lists registered materials missing on disk and marks whether the timeline references each one, while missing absolute paths are counted as `external`; http(s) materials are counted without network requests. This check only reports: it does not change `ok`, the exit code, material entries or files. Repair a dangling reference by re-confirming the source clip in the client or deleting the corresponding clip. Runs that never write back (`--lay 0` / refused / missing project) emit no `integrity` field.
400
428
  >
401
- > **Clip background fill (default)**: every candidate clip carries a solid black fill (gtrk contract `background_fill`, so narrow or shrunken clips never reveal the talking-head underneath), and each candidate track patches its own empty spans inside every landed beat envelope with solid black clips — with `--lay 2`, leaving only one track visible still hides the talking-head; audio-driven projects (no talking-head) are not patched. `--json` emits `lay.bed` (`fill`) and `lay.bed_fill` (patches and seconds). Spans with no B-roll at all count as holes: `lay.blackBedHoleSec` / `lay.blackBedHoles` are always emitted, with a non-fatal warning at ≥3 s for one span or ≥15% of one beat; adjust `--score-floor` or patch in the client. The patch material is stored at `assets/builtin/solid-000000-<W>x<H>.png` and reused idempotently.
429
+ > **Clip background fill (default)**: every candidate clip carries a solid black fill (gtrk contract `background_fill`, so narrow or shrunken clips never reveal the talking-head underneath), and each candidate track patches its own empty spans inside every landed beat envelope with solid black clips — with `--lay 2`, leaving only one track visible still hides the talking-head; audio-driven projects (no talking-head) are not patched. `--json` emits `lay.bed` (`fill`) and `lay.bed_fill` (patches and seconds). Spans with no B-roll at all count as holes: `lay.blackBedHoleSec` / `lay.blackBedHoles` are always emitted, with a non-fatal warning at ≥3 s for one span or ≥15% of one beat; adjust `--score-floor` or patch in the client. The patch material is stored at `assets/builtin/solid-000000-<W>x<H>.png` and reused idempotently. Gaps shorter than 1.2 s (1–2-frame seams between clips, the span before an anchored shot) are first absorbed by the neighbouring clip within its source-window headroom (never crossing the search segment, in-segment cuts or black spans); only gaps that cannot be absorbed are patched black, and each is listed in `lay.flash_gaps.kept` and a warning.
402
430
  >
403
431
  > **Solid black bed track (`--black-bed`, legacy)**: one solid black track covers the landed beat envelope beneath candidate tracks and above the talking-head track, and candidate clips carry no fill; holes are counted the same way with the same numbers. A black bed track left in an older project is replaced by clip fills on the next default re-lay.
404
432
 
@@ -450,19 +478,19 @@ Consumes the `dispatch.mg` dispatch landed by `gtrk split`, laying html-particle
450
478
  | `--yes` | Render mode: skip the billing-estimate confirmation | off |
451
479
  | `--json` | Machine-readable: human logs go to stderr, stdout carries only the result JSON | off |
452
480
 
453
- - **Laying** (`gtrk mg --project <dir>`): reads `dispatch.mg` → for each beat takes the source particle from `<project>/mg/<composition_id>.html` → lints → lays it into `beat_track` and atomically writes `struct_meta.mg` back into `.gtrk` (self-produced tracks are registered idempotently in `lay_tracks`; a re-lay strips the previous self-produced items before appending, and user-added tracks are never touched). Whether a particle is a "transparent overlay" or a "full-screen bed" is decided by `opaque`, inferred from the particle HTML's root `background`. Beats with a missing HTML file or a failed lint are counted in `skipped` and do not block the rest.
481
+ - **Laying** (`gtrk mg --project <dir>`): reads `dispatch.mg` → for each beat takes the source particle from `<project>/mg/<composition_id>.html` → lints → lays it into `beat_track` and atomically writes `struct_meta.mg` back into `.gtrk` (self-produced tracks are registered idempotently in `lay_tracks`; a re-lay strips the previous self-produced items before appending, and user-added tracks are never touched). Whether a particle is a "transparent overlay" or a "full-screen bed" is decided by `opaque`, inferred from the particle HTML's root `background`. The lint compares each particle's aspect ratio with the project's `video_size`; a particle with a different ratio (say, one declared 1920×1080 in a portrait project) fails `1-canvas-match` and is not laid. Beats with a missing HTML file or a failed lint are counted in `skipped` and do not block the rest.
454
482
  - **The stripping surface ≠ "what gets laid this run", and ≠ "every registered track"**: `--only <beat>` **strips only the matched particles** (true incremental merge) — every other already-laid particle's clip / material / registration entry is **preserved as is**, together with any manual tweaks you made in opencut (what is preserved is the existing clip itself, not a rebuild from the registration, so transparency `opaque` is not lost); those preserved entries are **not re-linted and their source HTML is not re-copied** (the project is self-contained, so deleting files under `<project>/mg/` does not matter). A full re-lay is still "strip clean, then rebuild the whole track", with **one exception**: particles that are in this dispatch but failed to land (missing HTML / failed lint / zero surviving span after re-projection) keep their previous round's clip on the track (a broken new one must not destroy the working old one); conversely, already-laid entries **no longer present in the dispatch** are still stripped (a plan change ≠ something broke). To strip every other already-laid particle too, authorize it explicitly with `--replace-all`.
455
483
  - **The material table does not accumulate**: strip materials by “self-produced identity × zero references” (self-produced = an `mg-`/`rrv-` prefix, or a registered filename under the CLI-exclusive `assets/mg/`), without trusting the client-rewritable `html_material` prefix. The `mg-` count stays equal to the particles on the track; duplicate and orphan entries are removed. Non-self-produced materials, self-produced materials still referenced by a surviving clip, and HTML copies under `assets/mg/` are preserved.
456
484
  - **"Nothing matched" is not a clear-everything instruction**: when `--only` matches nothing, `dispatch.mg` is empty/missing, or every entry in this run was skipped, **while the track already has laid particles**, the write-back is refused (that is a signal that the dispatch or the selector is broken). Add `--replace-all` if you really mean to clear it. A first-time lay (no existing laid entries on the track) is exempt and completes normally, reporting `laid=0`.
457
485
  - **Slot windows are re-projected on the spot**: before laying and linting, each queue entry's `[track_st, track_ed]` is recomputed from "`transcript` × the current `.gtrk`", and thereafter the lint slot envelope (house rule ⑦) and the laid clip duration both follow the recomputed values (`--only` obeys the same rule; aux particles re-project against **their own** span and are not conflated with the main beat window). The timecodes in `dispatch.mg` are only a **projection-time snapshot**, used as a fallback solely when re-projection is impossible — **after editing the talking-head track you can lay directly without re-running `gtrk split`**. `--json` always emits `reprojection:{mode,degraded,reason?,drifted,max_offset,shrunk,dropped}` (including under `--lint-only`). Entries with **zero surviving span** after re-projection are skipped and counted in `skipped` (no HTML is copied and nothing is laid back from the snapshot); if re-projection is impossible (missing transcript / project not found / no talking-head material on the main track) → it degrades to the snapshot with a warning and a `--json` marker, and the exit code is unchanged; behaviour for **non-v1** projects is unchanged (the laying path's version gate exits non-zero, while `--lint-only` still produces its report). A successful lay appends this run's timecode provenance (`timecode_source` / `reprojected_at`) into `struct_meta.mg`.
458
- - **lint** (`gtrk mg lint <particle.html> [--dispatch <path>]`): a purely local static check of the machine-decidable subset of the house rules for a particle HTML (wrapped in `<template>`, `data-composition-id` + 1920×1080, `gsap.timeline({ paused: true })`, registration in `window.__timelines`, no `Math.random` / `Date.now`, self-contained with no relative external links, root `background` consistent with `opaque`, …); when `--dispatch` is given it also checks that the `composition_id` matches the dispatch. Any fatal item exits non-zero and reports every reason.
486
+ - **lint** (`gtrk mg lint <particle.html> [--dispatch <path>]`): a purely local static check of the machine-decidable subset of the house rules for a particle HTML (wrapped in `<template>`, `data-composition-id` + root `data-width` / `data-height` (positive integers), `gsap.timeline({ paused: true })`, registration in `window.__timelines`, no `Math.random` / `Date.now`, self-contained with no relative external links, root `background` consistent with `opaque`, …); when `--dispatch` is given it also checks that the `composition_id` matches the dispatch, and compares the particle's declared aspect ratio with the project's `.gtrk` `video_size` (`1-canvas-match`: a different aspect ratio is fatal, the same ratio at another size passes, and ratios within 7% count as the same — a 16:9 particle in a 17:9 DCI 4K project passes, accepting a 6.7% horizontal stretch; `--project` does the same). A bare lint has no target project and only checks for positive integers. Any fatal item exits non-zero and reports every reason.
459
487
  - **Expected-id consistency** (`1-cid-expect`, **fatal**): the `data-composition-id` inside the HTML must equal the expected id (when laying, the dispatch entry's `composition_id`; for `mg lint`, the filename — but only when it matches the dispatch or looks like `…-B<digits>[-aux<n>]`, so a renamed copy such as `./tmp.html` is not compared). This guards against "copying `<id>.html`, renaming the file and forgetting the id inside" — laying would write a clip/material named after the file while the file registers a different `__timelines` key and fights the same-named particle over the same style scope.
460
488
  - **House rule ⑦, timeline length estimation** (`7-fill-slot` / `7-no-estimate` / `7-infinite-repeat`, **always non-fatal, never blocks laying**): when the slot envelope is known (per particle when laying; for `mg lint --dispatch` when it matches a dispatch entry) it computes a **static lower bound** on the GSAP timeline — degrading call by call, counting whatever it can parse (`duration×(repeat+1) + repeatDelay×repeat`, with `yoyo` adding no time), while calls with expression positions or non-literal durations are **skipped and not counted** (ignoring some calls still yields a valid lower bound). Estimate < envelope → warning; nothing computable at all → an explicit notice that "length could not be estimated statically, house rule ⑦ was not verified, and it must be accepted against the real engine's seek" (**never silent**: "could not compute" and "computed and passed" are distinguishable in the output); containing `repeat:-1` → a warning that "an infinite loop makes the total length Infinity, so house rule ⑦ cannot be verified statically; please use a finite repeat computed from the slot". The real criterion is always the render engine frame by frame; this item is only a reminder layer.
461
489
  - **House rule ⑧, repeated-primitive merging** (`8-primitive-merge`, **always non-fatal, never blocks laying**): identifies mergeable batches of `line` / `rect` / `path` / `polyline` / `polygon` that are "created inside a loop, or by a named factory called in a loop; landing under the same parent node; and not driven by per-element animation". Only batches whose purely numeric loop trip counts sum to **≥ 8** under the same parent are reported; when bounds involve `.length` or named constants and cannot be computed it still reports "count unknown" without constant folding; elements with per-element `gsap.set` / tweens, or used as a tween's first argument, are excluded. This item only points out "there is a batch of repeated primitives here that can be merged **losslessly**, pixel-identical after merging" — it is **not a risk verdict**: a hit does not mean the particle will reproduce a defect, and a miss does not mean it is safe. The real criterion remains sampling frames from a real render.
462
490
  - **Callback and seek semantics** (`x-callback-driven` / `x-engine-api-override` / `x-raf-interval`, **always non-fatal, never blocks laying**): aligned with the same-named contract section. The engine MUST use `seek(t,false)` / `time(t)` / `progress(p)` when fixing a frame; particles may drive their picture from timeline callbacks but MUST NOT override `tl.seek` or replace `window.__timelines[…]`. These lint items are reminders only and do not require seek shims.
463
491
  - **status** (`gtrk mg status --project <dir>`): summarizes the MG pipeline — total beats in `dispatch.mg` / how many source HTML files exist / how many are laid into `.gtrk`, annotating each beat (missing HTML / produced but not laid / laid).
464
- - **render** (`gtrk mg render <particle.html> --duration <sec> [--out <dir>] [--yes]`): renders a single particle in the cloud, **independent of any project**, into a Jianying-readable qtrle transparent alpha MOV (the fine-cut supplement channel — when the rough cut is already exported to Jianying and one more motion graphic is needed, you can add it without going back to the client app). The chain = lint upfront (envelope = `--duration`; any fatal item is blocked locally, zero submission, zero billing) → **billing-estimate confirmation** (live price lookup; the CLI has no local HTML render engine, so the cloud billed task is the only path for standalone particles; `--yes` skips the prompt) → inline submission to the cloud → `<composition_id>.mov` landed with `task.json`/`result.json` breadcrumbs (recoverable by task_id after a crash). **Scope**: qtrle only for now (`--format webm` is explicitly refused — Jianying cannot read VP8-alpha), 1920×1080 particles only (the contract has no portrait/odd-canvas opening), and `--duration` is required. The output never enters `.gtrk` and never lands in the Jianying draft folder; qtrle is lossless and relatively large, suited to seconds-long particles. Note it shares a word with `gtrk render` (full-video rendering) but is a different thing.
465
- - **fetch** (`gtrk mg fetch <query|block> [--top 3] [--all]` / `gtrk mg fetch --pick <block> --slot <beat> --project <dir>` / `--pick <block> --as <composition_id> --duration <sec> [--out <dir>]`): the **neutral particle source** from the HyperFrames registry — MG without a show skill. Candidate mode is **offline** (the snapshot ships with the package, pinned to a source commit) and lists 2–3 blocks per query with compat flags and poster links; pick mode fetches the block through three sources in order (our mirror first, then jsdelivr, then GitHub raw; 5 s each, **sha256-verified per source**, nothing is written if all three fail) → eight mechanical rewrites (`<template>` wrap / id rename / drop data-start and fit the slot / sink the solid backdrop into a child / swap GSAP to the contract CDN / swap fonts to a CJK font provable in our runtime mirror / fit duration to the slot / letterbox scaling, never stretch) → `gtrk mg lint` (fatal items block the write) → lands at `<project>/mg/<composition_id>.html`. **A skeleton is not a deliverable**: the returned `editable` (copy / number arrays / colors) MUST be rewritten for the beat and the show before laying. The contract currently accepts 1920×1080 particles only; `excluded` blocks are refused, `review` blocks (canvas / house-rule 8 shapes) can be fetched but need a real render check. `GITRUCK_MG_REGISTRY_BASE` overrides the fetch prefix.
492
+ - **render** (`gtrk mg render <particle.html> --duration <sec> [--out <dir>] [--yes]`): renders a single particle in the cloud, **independent of any project**, into a Jianying-readable qtrle transparent alpha MOV (the fine-cut supplement channel — when the rough cut is already exported to Jianying and one more motion graphic is needed, you can add it without going back to the client app). The chain = lint upfront (envelope = `--duration`; any fatal item is blocked locally, zero submission, zero billing) → **billing-estimate confirmation** (live price lookup; the CLI has no local HTML render engine, so the cloud billed task is the only path for standalone particles; `--yes` skips the prompt) → inline submission to the cloud → `<composition_id>.mov` landed with `task.json`/`result.json` breadcrumbs (recoverable by task_id after a crash). **Scope**: qtrle only for now (`--format webm` is explicitly refused — Jianying cannot read VP8-alpha), particle geometry must be positive integers, and `--duration` is required; the MOV comes out at the particle's declared `data-width` / `data-height` (a 1080×1920 portrait particle yields a 1080×1920 MOV). The output never enters `.gtrk` and never lands in the Jianying draft folder; qtrle is lossless and relatively large, suited to seconds-long particles. Note it shares a word with `gtrk render` (full-video rendering) but is a different thing.
493
+ - **fetch** (`gtrk mg fetch <query|block> [--top 3] [--all]` / `gtrk mg fetch --pick <block> --slot <beat> --project <dir>` / `--pick <block> --as <composition_id> --duration <sec> [--out <dir>]`): the **neutral particle source** from the HyperFrames registry — MG without a show skill. Candidate mode is **offline** (the snapshot ships with the package, pinned to a source commit) and lists 2–3 blocks per query with compat flags and poster links; pick mode fetches the block through three sources in order (our mirror first, then jsdelivr, then GitHub raw; 5 s each, **sha256-verified per source**, nothing is written if all three fail) → eight mechanical rewrites (`<template>` wrap / id rename / drop data-start and fit the slot / sink the solid backdrop into a child / swap GSAP to the contract CDN / swap fonts to a CJK font provable in our runtime mirror / fit duration to the slot / letterbox scaling, never stretch) → `gtrk mg lint` (fatal items block the write) → lands at `<project>/mg/<composition_id>.html`. **A skeleton is not a deliverable**: the returned `editable` (copy / number arrays / colors) MUST be rewritten for the beat and the show before laying. Registry neutral blocks come in one geometry only, 1920×1080 (the size is baked into each block), and `--canvas` with any other size is refused; in slot mode, a project whose `.gtrk` `video_size` is not 16:9 (7% tolerance, so 4K landscape and 17:9 DCI still fetch) is refused before anything is downloaded or written — portrait / square projects use `--source text` (no `.gtrk` or an invalid `video_size` skips the check with a notice); `excluded` blocks are refused, `review` blocks (canvas / house-rule 8 shapes) can be fetched but need a real render check. `GITRUCK_MG_REGISTRY_BASE` overrides the fetch prefix.
466
494
 
467
495
  `--json` output: `{ ok, mode:"lay"|"lint"|"status", … }` (each mode carries its own fields, e.g. `laid` / `skipped` when laying, per-beat status for `status`). Laying mode additionally carries **`track_total`** (how many laid particles currently exist on the track), **`kept`** / **`kept_ids`** (how many are **left over from the previous round** and not re-laid this time, plus their `composition_id` list) and **`removed`** (how many old self-produced particles were stripped this run) — `laid` (this run), `track_total` (total on the track) and `kept` (left over) **must be read together**, and `track_total = laid + kept` always holds. Reading `laid` alone makes "lay 1, strip 20" look identical to "patch in 1", and reading only the first two hides the cost that "a few particles on the track are not from this round" (`kept_ids` and `skipped` overlap: something that failed to land this round is still there from the previous one). When not fully green it also carries a machine-readable **`reason`**: `skipped` (some were not laid) / `empty_queue` (**write-back refused**, the project was not modified, with `refused:true` and `blocked[]`) / `no_project` (project missing, nothing laid). Runs that actually wrote back also carry **`integrity`** (the material-on-disk self-check, identical in name and shape to `gtrk matrix`; see the previous section).
468
496
 
@@ -488,12 +516,12 @@ gtrk composite bake --input <snapshot.gtrk> --base <project gtrk dir> [--progres
488
516
  gtrk composite bake --abi
489
517
  ```
490
518
 
491
- - `plan`: writes candidate windows to `struct_meta.composite` in the `.gtrk` (that key only, atomic write-back; the same pattern as `gtrk split` writing `struct_meta.split`); the client outline's CP row draws them as proposal blocks. Also writes a review sheet `composite/plan.md`. Re-running keeps built windows and does not re-propose rejected ones.
519
+ - `plan`: writes candidate windows to `struct_meta.composite` in the `.gtrk` (that key only, atomic write-back; the same pattern as `gtrk split` writing `struct_meta.split`); the client outline's CP row draws them as proposal blocks. Also writes a review sheet `composite/plan.md`. Re-running keeps built windows and does not re-propose rejected ones. The text signals behind the suggested layout (lists / contrasts / numbers / steps) are recognised in Chinese and English; other languages rely on B-roll density only, and the review sheet says so.
492
520
  - `review`: approve / reject / adjust / add windows, written back to the same key; later steps only act on approved windows.
493
521
  - `scaffold`: writes a scene skeleton `composite/scenes/cp-wNN.html` and a context sheet `cp-wNN.context.md` for each approved window; existing scenes are not overwritten.
494
522
  - `shots`: for picking footage — a contact sheet stamped with source time codes, shot cut points, and measured black bars (values for `data-crop`); it does not pick for you and does not touch the project.
495
- - `build`: scene → self-contained particle `gtrk/assets/composite/<id>.html`. Resolution matches the infra `composite-particle-profile` rules byte for byte (frame-grid window, track identity, anchors, subtitle input, `inputs_hash`); style comes from `composite/theme.css`.
496
- - `lint`: static checks plus a per-frame layout check in the local Chrome / Edge (a lower picture's foreground border over an upper picture, a foreground line crossing a picture, a picture leaving the stage) and a **frame-purity** check (each sampled frame is rendered twice — once right after the previous frame, once after a distant frame — and element and slot geometry must match; impure scenes draw the wrong picture at bake chunk starts and after preview seeks), optionally rendering stills. Exit code 0 pass / 1 problems / 3 layout not checked (no browser found; set `GTRK_CHROME`).
523
+ - `build`: scene → self-contained particle `gtrk/assets/composite/<id>.html`. Resolution matches the infra `composite-particle-profile` rules byte for byte (frame-grid window, track identity, anchors, subtitle input, `inputs_hash`); style comes from `composite/theme.css`. Write anchor keywords as they appear in the sentence: matching ignores spaces, punctuation and case on both sides, and English matches whole words (`"cold brew"` works as written; `tea` does not match `teas`).
524
+ - `lint`: static checks plus a per-frame layout check in the local Chrome / Edge (a lower picture's foreground border over an upper picture, a foreground line crossing a picture, a picture leaving the stage, an element inside a container that sets `left` / `top` offsets without being positioned — `unpositioned-offset`; give it `position:absolute` or the `.abs` class) and a **frame-purity** check (each sampled frame is rendered twice — once right after the previous frame, once after a distant frame — and element and slot geometry must match; impure scenes draw the wrong picture at bake chunk starts and after preview seeks), optionally rendering stills. Exit code 0 pass / 1 problems / 3 layout not checked (no browser found; set `GTRK_CHROME`).
497
525
  - `lay`: idempotently lays onto the topmost beat track by producer identity, registers materials, marks windows built; refuses stale particles; never overwrites a same-id particle the user edited in the client.
498
526
  - `bake`: bakes the laid composite particles **into opaque video locally**, written to `gtrk/assets/composite-bake/<key>.mp4`; inputs (slot pictures, subtitles, anchors) are computed from the **current timeline**. Purely local, zero billing.
499
527
  - **Browser**: prefers the pinned chrome-headless-shell (deterministic frames, fastest); otherwise falls back to the local Chrome / Edge (screenshot mode, slower, one fewer worker by default). **Never downloads silently**: install only with `gtrk deps install --browser` or this command's explicit `--install-browser` (about 110 MB). `GTRK_CHROME` selects a browser.
@@ -501,7 +529,7 @@ gtrk composite bake --abi
501
529
  - **Cache**: the key covers the particle, the computed inputs, material file fingerprints (size + mtime), fps, browser build and encode settings; hits are reused. Changing concurrency does not change the key.
502
530
  - **Frame rate**: defaults to the project rate; `--fps` overrides (e.g. a 30fps export). Frame count = `ceil(window × fps)`, covering the whole window.
503
531
  - **Exit codes**: 0 all succeeded (cache hits included) / 1 some composite failed (the rest still bake; reasons per composite) / 2 argument or environment error / 130 cancelled.
504
- - **Machine-readable**: `--progress jsonl` prints one event per line with `type` in `start` / `browser` / `prep` / `progress` / `composite` / `cancelled` / `done`, at most 1 second apart; in this mode **closing stdin cancels** (other modes ignore stdin). `--abi` prints only `{abi, version, browser, overrides}` without reading the project: `browser.pinned` says whether the pinned render component is installed; `overrides` lists the slot user overrides this version can bake (`fit` / `framing` / `crop` / `box` for the picture-in-picture window size and position / `slip_ms` for shifting a referenced clip's frames), so the client can tell whether the adjustments in a project can be baked. `--input <snapshot> --base <dir>`: use the snapshot as the timeline (for the client's unsaved state); relative material paths and the output location resolve against `--base`; the snapshot is never written back.
532
+ - **Machine-readable**: `--progress jsonl` prints one event per line with `type` in `start` / `browser` / `prep` / `progress` / `composite` / `cancelled` / `done`, at most 1 second apart; in this mode **closing stdin cancels** (other modes ignore stdin). `--abi` prints only `{abi, version, browser, overrides, stage}` without reading the project: `browser.pinned` says whether the pinned render component is installed; `overrides` lists the slot user overrides this version can bake (`fit` / `framing` / `crop` / `box` for the picture-in-picture window size and position / `slip_ms` for shifting a referenced clip's frames; `box_px` means the window's pixel offsets `dx_px` / `dy_px` can be baked), so the client can tell whether the adjustments in a project can be baked; `stage: "canvas"` means any stage size can be baked (the composite stage follows the project canvas, so portrait and square work the same way; older versions without this field only handle 1920×1080). `--input <snapshot> --base <dir>`: use the snapshot as the timeline (for the client's unsaved state); relative material paths and the output location resolve against `--base`; the snapshot is never written back.
505
533
  - `gtrk render` / `gtrk project export` bake the project's composites first: a successfully baked span shows the baked video (in Jianying / Premiere, a video clip on that layer), and **MG / OV particles lying entirely inside a composite window are no longer pre-rendered or billed**; a composite that fails to bake (missing footage, no browser, …) falls back to the rough cut with its reason reported, and never blocks export.
506
534
 
507
535
  ### `gtrk project init` / `gtrk audio lay` / `gtrk audio tighten` — audio-first project atoms (voice-over first)
@@ -584,7 +612,7 @@ Standalone single-request capabilities, kept separate from the pipeline's lane c
584
612
  | Tool | Input | Output | Billing | Status |
585
613
  |---|---|---|---|---|
586
614
  | `image_move` | One image; optional `--motion` picks one of 26 camera moves | Camera-move video (geometry derived from the source orientation: landscape 1920×1080 / portrait 1080×1920) | Queried live before the run | Live |
587
- | `image_matting` | One image | Transparent-background png (`--param` can request a backing plate) | Queried live before the run | Live |
615
+ | `image_matting` | One image (phone photos with an EXIF orientation are uprighted to their display orientation before upload; the original is untouched) | Transparent-background png (`--param` can request a backing plate) | Queried live before the run | Live |
588
616
  | `image_blackborder_remove` | One local image | Image with black borders removed | Queried live before the run | Live |
589
617
  | `image_canvas_adapt` | One local image; optional target width/height and `normal` / `rectangle` / `square` | Aspect-adapted image | Queried live before the run | Live |
590
618
  | `image_purify` | One local image; optional `full_screen` / `region` and region boxes (only material you have the rights to process) | Image cleaned of watermarks, logos or overlays | Queried live before the run | Live |
package/docs/reference.md CHANGED
@@ -23,7 +23,7 @@
23
23
  | ![在 Agent 中调用 gtrk 示例 1](../assets/agent-example-1.png) | ![在 Agent 中调用 gtrk 示例 2](../assets/agent-example-2.png) |
24
24
  | ![在 Agent 中调用 gtrk 示例 3](../assets/agent-example-3.png) | ![在 Agent 中调用 gtrk 示例 4](../assets/agent-example-4.png) |
25
25
 
26
- `gtrk install` 会把 19 个 CLI 自带 skill(`gtrk-oralcut`·`gtrk-long2short`·`gtrk-splitter`·`gtrk-matrix`·`gtrk-mg`·`gtrk-composite`·`gtrk-ai-drama`·`gtrk-style-maker`·`gtrk-transcript`·`gtrk-tools`·`gtrk-music-visualizer`·`gtrk-cover`·`gtrk-travel-recap`·`gtrk-live-slicing`·`gtrk-talking-head`·`gtrk-narration`·`gtrk-voiceover`·`gtrk-food-recap`·`gtrk-vlog-docu`)装进本机检测到的 Agent。实现方式与 lark-cli 一致:gtrk 把本地 skill 源交给通用 `skills` CLI,由它维护 Agent 探测、目录映射及更新规则;gtrk 不再硬编码各家路径。
26
+ `gtrk install` 会把 20 个 CLI 自带 skill(`gtrk-oralcut`·`gtrk-long2short`·`gtrk-splitter`·`gtrk-matrix`·`gtrk-mg`·`gtrk-composite`·`gtrk-ai-drama`·`gtrk-style-maker`·`gtrk-transcript`·`gtrk-tools`·`gtrk-music-visualizer`·`gtrk-cover`·`gtrk-travel-recap`·`gtrk-live-slicing`·`gtrk-talking-head`·`gtrk-narration`·`gtrk-voiceover`·`gtrk-food-recap`·`gtrk-vlog-docu`·`gtrk-ref-reading`)装进本机检测到的 Agent。实现方式与 lark-cli 一致:gtrk 把本地 skill 源交给通用 `skills` CLI,由它维护 Agent 探测、目录映射及更新规则;gtrk 不再硬编码各家路径。
27
27
 
28
28
  默认使用 `~/.agents/skills` 作为统一正本,再链接到各 Agent 的兼容目录(Windows 使用 junction);链接不可用时适配器会回退复制。这样更新只有一份正本,不会让多份副本逐渐漂移。常用命令:
29
29
 
@@ -72,6 +72,7 @@ gtrk skills install --copy
72
72
  | ⑤ | `/gtrk-mg` | `gtrk mg` | **MG(含 ov)最后叠上**(叠在已定稿、构图已核的底轨之上) |
73
73
  | ⑥ | `/gtrk-composite` | `gtrk composite` | **合成层**(字幕之后、出片之前):把选定时间窗做成合成颗粒——一段时间内的整个画面由一颗代码颗粒接管(逐句换画面、对照并排、并列卡片、字幕排版),讲者与 B-roll 作为槽位画进去;入口三选一(整片铺满 / AI 判定 / 指定区间),候选显示在客户端大纲 CP 行;纯本地零计费。出片时由 `gtrk composite bake` 在本机烘焙成视频(首次需 `gtrk deps install --browser`) |
74
74
  | — | `/gtrk-style-maker` | (无命令,建栏目) | 一次性访谈式建你栏目的风格体系(skill 家族 + 栏目配置,见下节) |
75
+ | 🔍 | `/gtrk-ref-reading` | `gtrk transcript` + `gtrk media` | **参考片精读**:别人的一条视频 → 字级转写 + 带台词标签的网格 + 关键动效逐帧 → 按「系统」记每个设计事件对着哪句话,落成 `ANALYSIS.md` / `TIMELINE.md` / `PROGRESS.md`;读完可交 `/gtrk-style-maker` 建栏目。看片命令纯本地零计费(只有转写一步计费),**不在成片 SOP 序列内** |
75
76
  | ③′ | 「**屏录画中画**」 | `gtrk pip lay` | 把口播粗剪的切点镜像到同步录的屏录 / 第二机位,铺屏录满幅轨 + 人像画中画副本轨(圆 / 圆角方 / 爱心 / 菱形 / 星形蒙版,可圆角);纯本地零计费;低置信时产对齐工程让客户端拖齐后 `--resume` |
76
77
  | — | (收口) | `gtrk render` | 本地渲染 gtrk 工程 → 成片 mp4(含 overlay 与 MG 颗粒;颗粒未命中缓存时云渲计费,`--no-particles` 可跳过) |
77
78
  | ✂️ | `/gtrk-long2short` | `gtrk long2short` | 长剪短·粗剪:长视频语义选段+跳剪 → 逐 clip 出客户端/剪映/PR 三方工程(毛片不上传),**不在成片 SOP 序列内**、随时可独立用 |
@@ -88,7 +89,7 @@ gtrk skills install --copy
88
89
  | 🧭 | `/gtrk-vlog-docu` | 编排 `gtrk transcript` → `tool audio_tts_clone` → `split` → `matrix` → `mg` → `audio lay` → `subtitle lay` | **Vlog 纪实图纸**:一批现场素材 → 素材理解、选档写稿、一次拍板 → 同期声骨架 + 旁白配音 + B-roll + 字幕层 + BGM 全自动 → 客户端可出片工程;「现场同期声 × 后期旁白」双声道交替,区别于纯口播链与纯配音链 |
89
90
 
90
91
  > **skill 与命令的区别**:`/gtrk-mg` 是**脑**——懂它在 SOP 第 ⑤ 步(B-roll 三源全齐、构图核过才铺 MG)、带用户确认、按栏目配置解析该产哪种颗粒;`gtrk mg` 是**手**——纯确定性 lint + 铺轨。你对话触发 skill,skill 替你跑命令。
91
- > 上面 19 个 `/gtrk-X` 都是 **CLI 自带框架 skill**(`gtrk skills install` 装;名单与「给 AI Agent 用」一节、「结构」一节完全一致)——其中 🧭 标记的 7 张是**组合图纸**(一句话跑全链,只编排上面的单命令 skill、本身不新增命令);`/gtrk-long2short` 独立驱动长剪短,`/gtrk-transcript` 独立驱动视频/音频转文字稿,`/gtrk-tools` 只负责单点工具族,`/gtrk-cover` 管封面,四者都不属成片 SOP 序列;`/gtrk-ai-drama`·`/gtrk-style-maker`·`/gtrk-cover` 是纯创作 skill(无命令)。栏目专属的**视觉风格/生产内容**另由你栏目的生产 skill(`/gtrk-style-maker` 产、经栏目配置 `style.skills` 绑定)供,不写死在这些框架 skill 里。
92
+ > 上面 20 个 `/gtrk-X` 都是 **CLI 自带框架 skill**(`gtrk skills install` 装;名单与「给 AI Agent 用」一节、「结构」一节完全一致)——其中 🧭 标记的 7 张是**组合图纸**(一句话跑全链,只编排上面的单命令 skill、本身不新增命令);`/gtrk-long2short` 独立驱动长剪短,`/gtrk-transcript` 独立驱动视频/音频转文字稿,`/gtrk-tools` 只负责单点工具族,`/gtrk-cover` 管封面,`/gtrk-ref-reading` 管参考片精读,五者都不属成片 SOP 序列;`/gtrk-ai-drama`·`/gtrk-style-maker`·`/gtrk-cover` 是纯创作 skill(无命令)。栏目专属的**视觉风格/生产内容**另由你栏目的生产 skill(`/gtrk-style-maker` 产、经栏目配置 `style.skills` 绑定)供,不写死在这些框架 skill 里。
92
93
 
93
94
  **各车道的具体视觉/内容怎么产**——MG 动态图长什么样、AI 情景动画什么调性——不写死在 CLI 里,而由**你自己栏目的生产 skill** 提供(用 `/gtrk-style-maker` 访谈式产出、留本地)。它们经**栏目配置 `style.skills[].produces`**(值 = 车道名)绑定,`gtrk mg` / `gtrk matrix` 等**通用驱动器**据此消费。**驱动方向 = CLI 驱动栏目 skill**:栏目 skill 只供风格/内容、不含任何「跑哪条命令」的编排职责;框架只认车道与管线接口,画面风格永远归你的栏目。不建栏目就用内置默认,端到端照常跑。
94
95
 
@@ -235,6 +236,8 @@ DO_NOT_TRACK=1 gtrk … # 业界通用的「不追踪」约定,同样生效
235
236
  | `--platforms <list>` | 逗号分隔 `youtube,vimeo,tiktok,bilibili`,仅配合 `--online`;不指定时查询已登记的四个平台。实际结果仍取决于服务与来源可访问性 |
236
237
  | `--online-session <name>` | 外部检索批次名,仅配合 `--online` 且不可为空;同批次续跑,换名称发起新搜索,可能产生新任务与费用 |
237
238
 
239
+ 派单消费时若这一轮**零可铺候选**而工程里已有带候选的 `split/broll-plan.json`(例如上一轮云端或本地 plan),既有 plan 不会被覆盖:本轮 plan(含逐条报错与待核候选)另存为 `split/broll-plan.online-attempt.json`,`--json` 结果带 `plan_kept`,`planPath` 仍指既有 plan。退出码照旧非 0。
240
+
238
241
  ### `gtrk transcript <本地视频|配音音频>`
239
242
 
240
243
  把本地视频或配音音频转为一个多层级的 Markdown 文字稿。只接受本地文件路径:视频在本机抽取、音频在本机转码为 16 kHz 单声道音频,只上传音频衍生物,原文件不会上传,也不支持 URL 或平台视频下载。
@@ -254,6 +257,31 @@ gtrk transcript "D:/素材/采访视频.mp4" --lang zh-CN --out "D:/文字稿/
254
257
 
255
258
  > `--json` 时另在源文件旁产一份句级时码 `<名>-transcript.json`(`utterances[]{id,text,st,ed}` + `material_id` + `text_hash` + `duration`,与 `gtrk split` 的 transcript 结构逐字段对齐),可直接被 `gtrk project init --transcript` 兜底路消费。**TTS 合成的配音勿走本零件重跑 ASR**——`gtrk project init --tts-task` 直取服务端句级时码,零 ASR 零额外计费。
256
259
 
260
+ ### `gtrk media <子命令>` — 本地看片(参考片精读取证)
261
+
262
+ 按时码或台词看任意本地视频:带台词标签的抽帧网格、逐帧看动效、视觉变化候选。**纯本地、零计费、不联网、不要 API Key**;只读本地文件,不下载链接。由 `/gtrk-ref-reading` 驱动,也可用来复核自家成片。
263
+
264
+ | 子命令 | 做什么 |
265
+ |---|---|
266
+ | `probe <视频>` | 时长、显示尺寸(计入像素宽高比与旋转)、帧率与 VFR、有无音轨 |
267
+ | `lines <转写稿>` | 逐句 `u<id> 起–止 文本`;`--find <台词>` 列出每处出现与字级起止 |
268
+ | `frames <视频>` | 按 `--at` / `--every` / `--frames` / `--every-frame` 取单帧;带 `--transcript` 或 `--label` 时帧下方加标签 |
269
+ | `tile <视频>` | 一张带标签的网格(≤60 格) |
270
+ | `tiles <视频>` | 分页网格(`--cols × --rows`,缺省 3×3);`--ranges <json>` 一次看多段;`--every-frame` 每段连续解码、保留每一原始帧 |
271
+ | `boundaries <视频>` | 视觉变化候选(`--rate` 缺省 12、`--threshold` 缺省 0.1、`--min-gap` 合并近邻);带 `--transcript` 时标注对着哪句话。**是机械证据,不是镜头判定** |
272
+ | `cut <视频>` | 截一段证据片(`.mp4/.mov/.mkv`) |
273
+
274
+ ```bash
275
+ gtrk media tiles "参考片.mp4" --frames 48 --cols 6 --rows 4 --cell 320 --transcript "精读/transcript.json" -o "精读/evidence/overview"
276
+ gtrk media tiles "参考片.mp4" --around "买不动" --occurrence 1 --every-frame --cols 6 --rows 4 --transcript "精读/transcript.json" -o "精读/evidence/motion/stamp"
277
+ gtrk media boundaries "参考片.mp4" --rate 4 --threshold 0.12 --transcript "精读/transcript.json" -o "精读/evidence/boundaries.json"
278
+ ```
279
+
280
+ - **区间**:`--start/--end`(秒或 `[hh:]mm:ss.mmm`),或 `--around <台词> [--occurrence n] [--padding s]` 按台词定位(须配 `--transcript`;台词多处出现时必须指定第几处,`lines --find` 可先列出)。
281
+ - **转写稿**:`gtrk transcript --json` 的产物(v1,含字级时码)原生可用,也认 `hypit.transcript@1`。转写稿须是这条视频的——时长 / 文件名对不上会在 stderr 告警。
282
+ - **标签**(画面下方,不遮原片字幕与图形):源片秒 · 时码(逐帧加帧号)· 句号 / 正在读的字与字级起止 / 所在整句(当前字高亮)。无字时写「此刻无字在读」「句间空隙」,不推断静音。标签用 libass 画,ffmpeg 缺 `ass` 滤镜时报错并指向 `gtrk deps install --ffmpeg`。
283
+ - **落点**:`-o` 必填,不落当前目录或源片旁;已存在不覆盖,`--force` 只覆盖本命令族的旧产物。`frames` / `tiles` 的产物目录含 `index.json`,逐格记录文件、时间、字与句号。逐帧单次上限 1800 帧。
284
+
257
285
  ### `gtrk oralcut <毛片>`
258
286
 
259
287
  | 参数 | 作用 | 缺省 |
@@ -426,7 +454,7 @@ gtrk matrix source-import --project <工程目录> --beat B07 --urls links.txt -
426
454
  >
427
455
  > **素材落盘自检**:写回工程之后只读检查 `materials[].path` 是否落盘。相对路径以 `.gtrk` 文件所在目录(`<产物目录>/gtrk/`)为基准解析。`--json` 出 `integrity:{ checked, counts, dangling:[…], danglingReferenced, danglingOrphan, external:[…], noPathIds:[…] }`;`dangling` 是登记存在但文件缺失的素材,并标出是否被时间线引用,绝对路径缺失另计 `external`,http(s) 素材只计数且不发网络请求。检查只告知,不改 `ok`、退出码、素材条目或文件;修复悬空引用请在客户端重新确认原片或删除对应 clip。未写回的运行(`--lay 0` / 拒铺 / 工程缺失)不出 `integrity` 字段。
428
456
  >
429
- > **片段底板(缺省)**:每个候选片段自带纯黑底(gtrk 契约 `background_fill`,窄画幅 / 缩小时不露下层口播),每条候选轨在已落成的 beat 包络内把自己的空档补成纯黑片——`--lay 2` 时只留任一条可见也不露口播;音频驱动工程(无口播)不补。`--json` 出 `lay.bed`(`fill`)与 `lay.bed_fill`(补了几段、几秒)。没有任何 B-roll 的纯黑时段记作空洞:`lay.blackBedHoleSec` / `lay.blackBedHoles` 恒出,单段 ≥ 3s 或单 beat 占比 ≥ 15% 时给非致命告警,可调 `--score-floor` 或在客户端补片。补片素材落在 `assets/builtin/solid-000000-<W>x<H>.png` 并幂等复用。
457
+ > **片段底板(缺省)**:每个候选片段自带纯黑底(gtrk 契约 `background_fill`,窄画幅 / 缩小时不露下层口播),每条候选轨在已落成的 beat 包络内把自己的空档补成纯黑片——`--lay 2` 时只留任一条可见也不露口播;音频驱动工程(无口播)不补。`--json` 出 `lay.bed`(`fill`)与 `lay.bed_fill`(补了几段、几秒)。没有任何 B-roll 的纯黑时段记作空洞:`lay.blackBedHoleSec` / `lay.blackBedHoles` 恒出,单段 ≥ 3s 或单 beat 占比 ≥ 15% 时给非致命告警,可调 `--score-floor` 或在客户端补片。补片素材落在 `assets/builtin/solid-000000-<W>x<H>.png` 并幂等复用。短于 1.2s 的空档(片段之间 1–2 帧的缝、锚定镜头前的空档)先在源窗余量内并进相邻片段(不越检索段界、段内切点与黑段),并不掉的才补黑,并在 `lay.flash_gaps.kept` 与告警里逐段点名。
430
458
  >
431
459
  > **纯黑底垫轨(`--black-bed`,旧形态)**:在候选轨之下、口播主轨之上铺满已落成的 beat 包络,候选片段不带底板;空洞口径与数值同上。老工程里已有的黑底轨在下次缺省重铺时被换成片段底板。
432
460
 
@@ -478,22 +506,23 @@ gtrk matrix lay --project <目录> [--plan <path>] # ③ 消费(
478
506
  | `--yes` | render 模式:跳过计费预估确认 | 关 |
479
507
  | `--json` | 机读:人读日志转 stderr,stdout 只输出结果 JSON | 关 |
480
508
 
481
- - **铺轨**(`gtrk mg --project <dir>`):读 `dispatch.mg` → 逐 beat 从 `<project>/mg/<composition_id>.html` 取源颗粒 → lint → 铺进 `beat_track`,把 `struct_meta.mg` 原子写回 `.gtrk`(幂等登记自产轨 `lay_tracks`,重铺先剥旧自产物再 append、用户手加轨零连带)。「透明叠加 / 满屏底层」由颗粒 HTML 根 `background` 反推的 `opaque` 决定。缺 HTML / lint 失败的 beat 计入 `skipped`、不拦其余。
509
+ - **铺轨**(`gtrk mg --project <dir>`):读 `dispatch.mg` → 逐 beat 从 `<project>/mg/<composition_id>.html` 取源颗粒 → lint → 铺进 `beat_track`,把 `struct_meta.mg` 原子写回 `.gtrk`(幂等登记自产轨 `lay_tracks`,重铺先剥旧自产物再 append、用户手加轨零连带)。「透明叠加 / 满屏底层」由颗粒 HTML 根 `background` 反推的 `opaque` 决定。lint 按工程 `video_size` 比对颗粒画幅比例,跨比例的颗粒(如竖屏工程里声明 1920×1080 的颗粒)判 `1-canvas-match` 致命、不铺。缺 HTML / lint 失败的 beat 计入 `skipped`、不拦其余。
482
510
  - **剥离面 ≠ 「本次铺什么」,也 ≠ 「登记轨全集」**:`--only <beat>` **只剥命中的那几颗**(真增量合并)——轨上其余已铺颗粒的 clip / 素材 / 登记条目**原样保留**,连同用户在 opencut 对它们的手调(保留的是既有 clip 原件,非照登记重建,故透明度 `opaque` 不会丢);这些保留条目**不重新 lint、不重新复制源 HTML**(工程自包含,`<project>/mg/` 下源文件删了也不影响)。全量重铺仍是「剥净再整轨重建」,**唯一例外**是本次派单里有、却因缺 HTML / lint 未过 / 重投影后零存活而**没铺成**的那几颗——它们上一轮的 clip 保留在轨上(不因为新的做坏了就把旧的也毁掉);反之**派单里已不存在**的已铺条目仍照剥(计划变更 ≠ 做坏了)。要连其余已铺颗粒一起剥掉重来:`--replace-all` 显式授权。
483
511
  - **素材表不囤积**:素材按「自产身份 × 零引用」剥离(自产 = `mg-`/`rrv-` 前缀,或位于 CLI 独占的 `assets/mg/` 且文件名在自产登记里),不依赖客户端可改写的 `html_material` 前缀。`mg-` 素材数与轨上颗粒数保持一致;重复或孤儿条目会清掉。非自产素材、仍被存活 clip 引用的自产素材,以及盘上 `assets/mg/` 的 HTML 副本都不动。
484
512
  - **「一条都没定位到」不是清空指令**:`--only` 打空、`dispatch.mg` 为空/缺失、或本次条目全被 skip,**而轨上已有已铺颗粒**时,同样拒绝写回(那是派单或选择器出问题的信号)。确要清空加 `--replace-all`。首次铺轨(轨上本就没有已铺条目)不受此限,照常走完报 `laid=0`。
485
513
  - **槽位窗口现场重投影**:铺轨与 lint 之前先用「`transcript` × 当刻 `.gtrk`」重算每条队列条目的 `[track_st, track_ed]`,之后 lint 的坑位包络(铁律⑦)与落轨 clip 时长一律以重算值为准(`--only` 同守;aux 派生颗粒按**自己的** span 重投影,不与主 beat 窗口混同)。`dispatch.mg` 里的时码只是**投影时刻快照**,仅在重投影不可行时兜底——**改完口播轨直接铺即可,不必先重跑 `gtrk split`**。`--json` 恒出 `reprojection:{mode,degraded,reason?,drifted,max_offset,shrunk,dropped}`(`--lint-only` 也有)。重投影后**零存活**的条目 skip 并计入 `skipped`(不复制 HTML、不按快照铺回去);重投影不可行(transcript 缺失 / 工程定位不到 / 主轨查不到口播素材)→ 降级用快照 + 告警 + `--json` 标注,退出码不变;工程**非 v1** 的既有行为不变(铺轨路径版本门非 0 退出、`--lint-only` 照旧出报告)。铺轨成功会把本次时码来源(`timecode_source` / `reprojected_at`)纯追加登记进 `struct_meta.mg`。
486
- - **lint**(`gtrk mg lint <颗粒.html> [--dispatch <path>]`):纯本地静态校验颗粒 HTML 的铁律机器可判定子集(`<template>` 包裹、`data-composition-id` + 1920×1080、`gsap.timeline({ paused: true })`、`window.__timelines` 注册、无 `Math.random` / `Date.now`、自包含无相对外链、根 `background` 与 `opaque` 自洽…);给 `--dispatch` 时校验 `composition_id` 命中派单。任一致命项非 0 退出并逐条报因。
514
+ - **lint**(`gtrk mg lint <颗粒.html> [--dispatch <path>]`):纯本地静态校验颗粒 HTML 的铁律机器可判定子集(`<template>` 包裹、`data-composition-id` + 根 `data-width` / `data-height`(正整数)、`gsap.timeline({ paused: true })`、`window.__timelines` 注册、无 `Math.random` / `Date.now`、自包含无相对外链、根 `background` 与 `opaque` 自洽…);给 `--dispatch` 时校验 `composition_id` 命中派单,并按工程 `.gtrk` 的 `video_size` 比对颗粒声明的画幅比例(`1-canvas-match`:跨比例致命,同比例不同尺寸放行,比例差 7% 以内视为同比例——DCI 4K 这类 17:9 工程里的 16:9 颗粒放行、接受 6.7% 横向拉伸;`--project` 同样);裸 lint 不知道目标工程,只核正整数。任一致命项非 0 退出并逐条报因。
487
515
  - **期望 id 一致性**(`1-cid-expect`,**致命**):HTML 内 `data-composition-id` 必须等于期望 id(铺轨=该条派单的 `composition_id`;`mg lint`=文件名,仅当它命中派单或形如 `…-B<数字>[-aux<n>]` 时比对,`./tmp.html` 这类改过名的副本不比对)。防的是「复制 `<id>.html` 改名时漏改内部 id」——落轨会写出以文件名命名的 clip/material,而文件注册的是另一个 `__timelines` 键、还与同名颗粒抢同一个样式作用域。
488
516
  - **铁律⑦ tl 总长估长**(`7-fill-slot` / `7-no-estimate` / `7-infinite-repeat`,**恒非致命、不拦铺轨**):已知坑位包络时(铺轨逐颗;`mg lint --dispatch` 命中派单条目)对 GSAP 时间线做**静态下界估算**——逐调用降级,能解析的计入(`duration×(repeat+1) + repeatDelay×repeat`,`yoyo` 不加时长),表达式 position / 非字面量 duration 那条**跳过不计**(忽略若干调用仍是合法下界)。估长 < 包络 → 告警;一条都算不出 → 显式提示「无法静态估长,铁律⑦未校验,须真引擎 seek 验收」(**不静默**,「算不出」与「算过且通过」在输出上可区分);含 `repeat:-1` → 告警「无限循环令总长 Infinity、铁律⑦不可静态验证,请改按坑位算死的有限 repeat」。真判据永远是渲染引擎逐帧,本项只做提醒层。
489
517
  - **铁律⑧重复图元合并**(`8-primitive-merge`,**恒非致命、不拦铺轨**):识别「循环体内创建,或由循环调用具名工厂创建;落到同一父节点;且没有逐元素动画驱动」的可合并 `line` / `rect` / `path` / `polyline` / `polygon` 批次。同一父节点的纯数字循环 trip count 累加后 **≥ 8** 才报数;边界含 `.length` / 具名常量而算不出时仍报「条数未知」,不做常量折叠;逐元素 `gsap.set` / tween 或被 tween 首实参使用的元素数组会被排除。本项只提示「这里有一批可**无损**合并的重复图元,合并后画面逐像素不变」,**不是风险判定**:命中不代表该颗粒会复现缺陷,未命中也不代表安全,真判据仍是真渲染出片抽帧。
490
518
  - **回调与 seek 语义**(`x-callback-driven` / `x-engine-api-override` / `x-raf-interval`,**恒非致命、不拦铺轨**):对齐契约同名一节。引擎定帧 MUST 使用 `seek(t,false)` / `time(t)` / `progress(p)`;颗粒可用时间线回调驱动画面,但不得覆写 `tl.seek` 或替换 `window.__timelines[…]`。三项 lint 只作提醒,不要求作者添加垫片。
491
519
  - **status**(`gtrk mg status --project <dir>`):汇总 MG 流水线——`dispatch.mg` beat 总数 / 已产源 HTML 数 / 已铺进 `.gtrk` 数,并逐 beat 标注(缺 HTML / 已产未铺 / 已铺)。
492
- - **render**(`gtrk mg render <颗粒.html> --duration <sec> [--out <dir>] [--yes]`):**脱离工程**把单颗颗粒云渲成剪映可读的 qtrle 透明 alpha MOV(精剪补给口——粗剪导剪映后缺一颗动态图,不回客户端就能补)。链路 = lint 前置(包络 = `--duration`,任一致命项本地拦截、零提交零计费)→ **计费预估确认**(实时查价;CLI 无本地 HTML 渲染引擎,独立颗粒唯一路 = 云渲计费任务,`--yes` 跳过确认)→ 内联提交云端 → `<composition_id>.mov` 落盘 + `task.json`/`result.json` 面包屑(崩溃可凭 task_id 恢复)。**射程**:首发只出 qtrle(`--format webm` 明确拒绝,剪映不吃 VP8-alpha)、只收 1920×1080 颗粒(契约未开竖屏/异形口)、`--duration` 必填。产物不进 `.gtrk`、不写剪映草稿目录;qtrle 无损体积偏大,适合秒级颗粒。注意与 `gtrk render`(整片成片渲染)同词不同物。
493
- - **fetch**(`gtrk mg fetch <检索词|块名> [--top 3] [--all]` / `gtrk mg fetch --pick <块名> --slot <beat> --project <dir>` / `--pick <块名> --as <composition_id> --duration <sec> [--out <dir>]`):**registry 中性颗粒源**——没建栏目也能出 MG。候选态**离线**(快照随包、钉死来源 commit)按检索词列 2–3 件(含 compat 标记与海报链接);取块态按三源顺序取块(我方镜像优先、jsdelivr、GitHub raw,每源 5 s,**逐源 sha256 强校验**,三源皆败零落盘)→ 机械改写八条(`<template>` 包裹 / 改 id / 去 data-start 贴坑位 / 实心底下沉子层 / GSAP 源换契约 CDN / 字体换运行时镜像可证的 CJK 字体 / 时长贴坑位 / 信箱缩放不拉伸)→ `gtrk mg lint` 致命项不过不落盘 → 落 `<project>/mg/<composition_id>.html`。**骨架不是成品**:返回的 `editable`(文案 / 数值数组 / 色值)MUST 按 beat 与栏目改写后再铺。契约当前只收 1920×1080 颗粒,其它画布拒绝;`excluded` 件拒取,`review` 件(canvas / 铁律 8 高危形态)可取但须真渲验收。`GITRUCK_MG_REGISTRY_BASE` 可整体覆盖取块前缀。
520
+ - **render**(`gtrk mg render <颗粒.html> --duration <sec> [--out <dir>] [--yes]`):**脱离工程**把单颗颗粒云渲成剪映可读的 qtrle 透明 alpha MOV(精剪补给口——粗剪导剪映后缺一颗动态图,不回客户端就能补)。链路 = lint 前置(包络 = `--duration`,任一致命项本地拦截、零提交零计费)→ **计费预估确认**(实时查价;CLI 无本地 HTML 渲染引擎,独立颗粒唯一路 = 云渲计费任务,`--yes` 跳过确认)→ 内联提交云端 → `<composition_id>.mov` 落盘 + `task.json`/`result.json` 面包屑(崩溃可凭 task_id 恢复)。**射程**:首发只出 qtrle(`--format webm` 明确拒绝,剪映不吃 VP8-alpha)、颗粒几何须为正整数、`--duration` 必填;MOV 按颗粒声明的 `data-width` / `data-height` 原尺寸出(竖屏 1080×1920 颗粒出 1080×1920 MOV)。产物不进 `.gtrk`、不写剪映草稿目录;qtrle 无损体积偏大,适合秒级颗粒。注意与 `gtrk render`(整片成片渲染)同词不同物。
521
+ - **fetch**(`gtrk mg fetch <检索词|块名> [--top 3] [--all]` / `gtrk mg fetch --pick <块名> --slot <beat> --project <dir>` / `--pick <块名> --as <composition_id> --duration <sec> [--out <dir>]`):**registry 中性颗粒源**——没建栏目也能出 MG。候选态**离线**(快照随包、钉死来源 commit)按检索词列 2–3 件(含 compat 标记与海报链接);取块态按三源顺序取块(我方镜像优先、jsdelivr、GitHub raw,每源 5 s,**逐源 sha256 强校验**,三源皆败零落盘)→ 机械改写八条(`<template>` 包裹 / 改 id / 去 data-start 贴坑位 / 实心底下沉子层 / GSAP 源换契约 CDN / 字体换运行时镜像可证的 CJK 字体 / 时长贴坑位 / 信箱缩放不拉伸)→ `gtrk mg lint` 致命项不过不落盘 → 落 `<project>/mg/<composition_id>.html`。**骨架不是成品**:返回的 `editable`(文案 / 数值数组 / 色值)MUST 按 beat 与栏目改写后再铺。registry 中性块只有 1920×1080 一种几何(尺寸写死在块里),`--canvas` 给其它画幅拒绝;派单模式下工程 `.gtrk` 的 `video_size` 比例不是 16:9(容差 7%:4K 横屏与 DCI 17:9 照取)在下载前拒、不落盘,竖屏 / 方形工程走 `--source text`(找不到 `.gtrk` 或 `video_size` 不合法时不比对、照取并提示);`excluded` 件拒取,`review` 件(canvas / 铁律 8 高危形态)可取但须真渲验收。`GITRUCK_MG_REGISTRY_BASE` 可整体覆盖取块前缀。
494
522
  - **fetch --source text**(`gtrk mg fetch --source text <检索词> [--top 3] [--offline]` / `--pick <模板 id> --slot <beat> --project <dir>` / `--pick <模板 id> --as <composition_id> [--out <dir>]`):**同合云自家的文字特效模板库**——与中性块是两条路。模板是我方 clean-room 重写的**成品**(配色与排版已做过),**不做机械改写、MUST NOT 直接改 HTML**。每颗内嵌产生它的 IR 与双哈希,颗粒因此自带出身证明(三态 `ir` / `detached` / `html`);手改一个字节就掉成 `detached`,云端改写会拒绝它(`gtrk mg lint` 报非致命的 `x-ir-detached`)。目录走**远端择新、随包兜底**:优先取镜像上的 `catalog.json` 按 `version` 择新并缓存 24h,三源皆不可达时回落随包那份并**明示**(候选态因此永远离线可用)。取块仍逐源 sha256 强校验,三源皆败零落盘。`GITRUCK_TEXT_TEMPLATE_BASE` 可整体覆盖取块与目录前缀。
495
- **落点与时长由命令钉定**:派单模式的 `composition_id` 取自 `dispatch.mg` 那条(`<工程slug>-<beatId>`,不是 `--slot` 收的 beat id),颗粒内嵌 IR 的 `id` 与 `canvas.duration` 被钉到「期望 id」与「坑位包络 + 0.3s 余量」(铁律⑦,贴模板末尾的层跟着钉),再走 `mg compile` 同一条本地编译链重编——产物仍是 `ir` 态,`1-cid-expect` 天然对得上。独立模式 `--as` 同样改写颗粒内部 id,`--duration` 钉时长;`--slot` 与 `--duration` 互斥。两者都没改时原字节落盘。
496
- - **compile**(`gtrk mg compile <ir.json> [--out <dir>]`):把 IR 交给云端**确定性编译**成颗粒(**0 积分**)。改字、换色、改字号描边阴影、改时长都走它——改 IR 再编,比手改 HTML 干净,且颗粒保持 `ir` 态(云端还能继续调)。**本地没有编译器**是有意的:第二份编译器意味着第二套产物字节,而三态身份判的就是字节。
523
+ **落点与时长由命令钉定**:派单模式的 `composition_id` 取自 `dispatch.mg` 那条(`<工程slug>-<beatId>`,不是 `--slot` 收的 beat id),颗粒内嵌 IR 的 `id` 与 `canvas.duration` 被钉到「期望 id」与「坑位包络 + 0.3s 余量」(铁律⑦,贴模板末尾的层跟着钉),再走 `mg compile` 同一条本地编译链重编——产物仍是 `ir` 态,`1-cid-expect` 天然对得上。独立模式 `--as` 同样改写颗粒内部 id,`--duration` 钉时长;`--slot` 与 `--duration` 互斥。两者都没改、且目标画幅就是模板的 1920×1080 时原字节落盘。
524
+ **画幅**:按目标画幅编(IR 住在 1920 参考系,画幅是编译入参)。派单模式缺省取工程 `.gtrk` 的 `video_size`(显式 `--canvas` 与它不符当场拒;找不到 `.gtrk` 时按 1920×1080 编并提示补 `--canvas`),独立模式取 `--canvas`、不给即 1920×1080;`--json` 带 `canvas` 与 `canvas_from`(`flag` / `project` / `reference`)。
525
+ - **compile**(`gtrk mg compile <ir.json | 颗粒.html> [--canvas <WxH>] [--out <dir>] [--remote]`):把 IR **确定性编译**成颗粒(**0 积分**)。改字、换色、改字号描边阴影、改时长都走它——改 IR 再编,比手改 HTML 干净,且颗粒保持 `ir` 态(云端还能继续调)。缺省在本地编(零请求、断网可用,与服务端编译器逐字节等价);`--remote` 改走服务端,留给排查与对拍。给颗粒 HTML = 按它内嵌的 IR 重置回模板。`--canvas` 是目标画幅:不给时重置沿用那颗自己的画幅、`.ir.json` 按 1920×1080;`--remote` 把同一画幅交给服务端,产物画幅与请求不符(旧服务端不认画幅)时不落盘。
497
526
  - **edit**(`gtrk mg edit <颗粒.html> --say "<一句话>" [--n 1|3] [--pick <k>] [--yes]`):自然语言改写文字模板颗粒,经云端模型链出候选。`--n` 只收 1 或 3,**它就是计费单位数**(2 积分/候选,首次与改写同价、无免费次数)。`detached` / 无内嵌 IR 的颗粒**本地就拒**,不提交云任务、不花钱。候选带 `scope`:`L1` = 在模板可调范围内、`L2` = 越界即新生成(**不是失败**);服务端说做不到时给一句 `refusal`,命令如实转述并以非 0 退出码区分于网络错误。不给 `--pick` 就只落候选不动原文件;给了才替换并留 `<cid>.bak.html`。
498
527
 
499
528
  `--json` 输出:`{ ok, mode:"lay"|"lint"|"status", … }`(各模式带对应字段,如铺轨的 `laid` / `skipped`、status 的逐 beat 状态)。铺轨模式另带 **`track_total`**(轨上现存已铺数)、**`kept`** / **`kept_ids`**(其中**上轮遗留**、本次没重铺的颗数与 `composition_id` 清单)与 **`removed`**(本次被剥的旧自产颗粒数)——`laid`(本次)、`track_total`(轨上共)与 `kept`(上轮遗留)**要一起读**,恒有 `track_total = laid + kept`;只看 `laid` 会让「铺 1 颗、剥 20 颗」跟「补铺 1 颗」长得一模一样,只看前两个又会漏掉「轨上那几颗不是这一轮的版本」这条代价(`kept_ids` 与 `skipped` 会重叠:这轮没铺成、上一轮那条还在轨上)。非全绿时另带机读 **`reason`**:`skipped`(部分没铺上)/ `empty_queue`(**拒写回**,工程未被改动,另带 `refused:true` 与 `blocked[]`)/ `no_project`(工程缺失未铺轨)。真写回过的运行另带 **`integrity`**(素材落盘自检,与 `gtrk matrix` 同名同形,口径见上节)。
@@ -520,12 +549,12 @@ gtrk composite bake --input <快照.gtrk> --base <工程 gtrk 目录> [--progres
520
549
  gtrk composite bake --abi
521
550
  ```
522
551
 
523
- - `plan`:候选窗口写进 `.gtrk` 的 `struct_meta.composite`(只动这一个键、原子写回;与 `gtrk split` 写 `struct_meta.split` 同一套做法),客户端大纲 CP 行据此画提案块;另出审稿稿 `composite/plan.md`。重跑保留已做的、不再提用户不要的。
552
+ - `plan`:候选窗口写进 `.gtrk` 的 `struct_meta.composite`(只动这一个键、原子写回;与 `gtrk split` 写 `struct_meta.split` 同一套做法),客户端大纲 CP 行据此画提案块;另出审稿稿 `composite/plan.md`。重跑保留已做的、不再提用户不要的。「建议画面」用到的文字信号(列举 / 对照 / 数字 / 分点)识别中文与英文;其他语种只看 B-roll 密度,审稿稿里写明未识别。
524
553
  - `review`:采纳 / 不要 / 调起止 / 补窗口,写回同一个键;后续步骤只处理已采纳的窗口。
525
554
  - `scaffold`:为已采纳窗口落场景骨架 `composite/scenes/cp-wNN.html` 与上下文 `cp-wNN.context.md`;已有场景不覆盖。
526
555
  - `shots`:给选片用——带源时间码的联系表、镜头切点、黑边实测(`data-crop` 取值);不替你选、不改工程。
527
- - `build`:场景 → 自包含颗粒 `gtrk/assets/composite/<id>.html`。解析规则与 infra `composite-particle-profile` 逐字节一致(帧格窗口、轨身份、锚点、字幕输入、`inputs_hash`);风格取 `composite/theme.css`。
528
- - `lint`:静态校验 + 本机 Chrome / Edge 逐帧排版机检(下层画面的前景描边压上层画面、前景线条穿过画面、画面越出舞台)与**帧纯度**机检(同一帧号按「先渲前一帧」「先渲远处一帧」两种顺序各渲一次,元素与槽位几何必须一致——不一致的场景在烘焙段首、预览跳转后会画错),可出静帧。退出码 0 通过 / 1 有问题 / 3 排版未机检(找不到浏览器,可设 `GTRK_CHROME`)。
556
+ - `build`:场景 → 自包含颗粒 `gtrk/assets/composite/<id>.html`。解析规则与 infra `composite-particle-profile` 逐字节一致(帧格窗口、轨身份、锚点、字幕输入、`inputs_hash`);风格取 `composite/theme.css`。锚点关键词照句子原文写:匹配时关键词与句子都忽略空格、标点与大小写,英文按整词(`"cold brew"` 直接写,`tea` 不命中 `teas`)。
557
+ - `lint`:静态校验 + 本机 Chrome / Edge 逐帧排版机检(下层画面的前景描边压上层画面、前景线条穿过画面、画面越出舞台、容器里的元素写了 `left` / `top` 偏移却没定位——`unpositioned-offset`,容器内要写 `position:absolute` 或加 `.abs`)与**帧纯度**机检(同一帧号按「先渲前一帧」「先渲远处一帧」两种顺序各渲一次,元素与槽位几何必须一致——不一致的场景在烘焙段首、预览跳转后会画错),可出静帧。退出码 0 通过 / 1 有问题 / 3 排版未机检(找不到浏览器,可设 `GTRK_CHROME`)。
529
558
  - `lay`:按自产身份幂等铺进 beat 轨最上层、补登记素材、窗口记为已做;过期颗粒拒铺;用户在客户端改过的同 id 颗粒不覆盖。
530
559
  - `bake`:把已铺的合成颗粒在本机**烘焙成不透明视频**,产物落 `gtrk/assets/composite-bake/<键>.mp4`;输入(槽位画面、字幕、锚点)按**当前时间线**现算。纯本地、零计费。
531
560
  - **浏览器**:优先钉定版本的 chrome-headless-shell(确定性出帧、最快);没有时退回本机 Chrome / Edge(截图模式,更慢、并发降一档)。**不会静默下载**:`gtrk deps install --browser` 或本命令 `--install-browser` 显式授权才装(约 110 MB)。`GTRK_CHROME` 可指定浏览器。
@@ -533,7 +562,7 @@ gtrk composite bake --abi
533
562
  - **缓存**:键含颗粒、现算输入、素材文件指纹(大小 + 修改时间)、帧率、浏览器版本与编码参数;命中直接复用。改并发不换键。
534
563
  - **帧率**:缺省 = 工程帧率;`--fps` 指定(例如导出 30fps)。帧数 = `ceil(窗口 × fps)`,盖满整个窗口。
535
564
  - **退出码**:0 全部成功(含命中缓存)/ 1 有颗失败(其余照常,逐颗给原因)/ 2 参数或环境错误 / 130 被取消。
536
- - **机读**:`--progress jsonl` 逐行输出 `type` 为 `start` / `browser` / `prep` / `progress` / `composite` / `cancelled` / `done` 的事件,相邻两行不超过 1 秒;此模式下**关闭 stdin 即取消**(其它模式不认 stdin)。`--abi` 只输出 `{abi, version, browser, overrides}`,不读工程:`browser.pinned` 是钉定的渲染组件装没装;`overrides` 是本版能烤出的槽位用户覆盖(`fit` / `framing` / `crop` 裁切 / `box` 画中画窗口大小与位置 / `slip_ms` 引用片段前后挪取帧),客户端据此判断工程里的调整能否烤出。`--input <快照> --base <目录>`:以快照为时间线来源(客户端交未保存状态用),相对素材路径与产物落点按 `--base` 解析,快照不回写。
565
+ - **机读**:`--progress jsonl` 逐行输出 `type` 为 `start` / `browser` / `prep` / `progress` / `composite` / `cancelled` / `done` 的事件,相邻两行不超过 1 秒;此模式下**关闭 stdin 即取消**(其它模式不认 stdin)。`--abi` 只输出 `{abi, version, browser, overrides, stage}`,不读工程:`browser.pinned` 是钉定的渲染组件装没装;`overrides` 是本版能烤出的槽位用户覆盖(`fit` / `framing` / `crop` 裁切 / `box` 画中画窗口大小与位置 / `slip_ms` 引用片段前后挪取帧;`box_px` 表示能烤窗口的像素位移 `dx_px` / `dy_px`),客户端据此判断工程里的调整能否烤出;`stage: "canvas"` 表示能烤任意舞台尺寸(合成舞台随工程画布,竖屏、方形同口径;缺这个字段的老版本只认 1920×1080)。`--input <快照> --base <目录>`:以快照为时间线来源(客户端交未保存状态用),相对素材路径与产物落点按 `--base` 解析,快照不回写。
537
566
  - `gtrk render` / `gtrk project export` 会先烘焙工程里的合成颗粒:烘焙成功的那段以烘焙视频出画(剪映 / Pr 里是该层位上的一段视频),**时段整段落在合成窗口内的 MG / OV 不再预渲、不计费**;烘焙失败(素材丢失、找不到浏览器等)的那颗按粗剪出画并点名原因,不拦导出。
538
567
 
539
568
  ### `gtrk project init` / `gtrk audio lay` / `gtrk audio tighten` — 音频驱动工程零件(配音先行)
@@ -616,7 +645,7 @@ gtrk project background --gtrk <工程文件> --clear
616
645
  | 工具 | 输入 | 产物 | 计费 | 状态 |
617
646
  |---|---|---|---|---|
618
647
  | `image_move` | 单张图片;可选 `--motion` 指定 26 种运镜之一 | 运镜视频(几何按原图朝向推导:横 1920×1080 / 竖 1080×1920) | 运行前实时查询 | 已上线 |
619
- | `image_matting` | 单张图片 | 透明背景 png(可 `--param` 请求背景底板) | 运行前实时查询 | 已上线 |
648
+ | `image_matting` | 单张图片(带 EXIF 方向的手机照片先按显示方向摆正再上传,原图不动) | 透明背景 png(可 `--param` 请求背景底板) | 运行前实时查询 | 已上线 |
620
649
  | `image_blackborder_remove` | 单张本地图片 | 去黑边图片 | 运行前实时查询 | 已上线 |
621
650
  | `image_canvas_adapt` | 单张本地图片;可选目标宽高与 `normal` / `rectangle` / `square` | 比例适配图片 | 运行前实时查询 | 已上线 |
622
651
  | `image_purify` | 单张本地图片;可选 `full_screen` / `region` 与区域框(仅处理你有权处理的素材) | 清理水印、Logo 或叠加元素后的净化图片 | 运行前实时查询 | 已上线 |
package/package.json CHANGED
@@ -1,79 +1,79 @@
1
- {
2
- "name": "@gitruck/cli",
3
- "version": "1.3.7",
4
- "description": "同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通。",
5
- "license": "MIT",
6
- "author": "Gitruck (同合云)",
7
- "repository": {
8
- "type": "git",
9
- "url": "git+https://github.com/Gitruck/cli.git"
10
- },
11
- "homepage": "https://github.com/Gitruck/cli#readme",
12
- "bugs": {
13
- "url": "https://github.com/Gitruck/cli/issues"
14
- },
15
- "type": "module",
16
- "bin": {
17
- "gtrk": "dist/index.js"
18
- },
19
- "files": [
20
- "dist",
21
- "assets",
22
- "skills",
23
- "contracts",
24
- "AGENT.md",
25
- "README.md",
26
- "README.en.md",
27
- "docs/reference.md",
28
- "docs/reference.en.md",
29
- "docs/workflow.md",
30
- "docs/workflow.en.md",
31
- "docs/development.md",
32
- "docs/development.en.md",
33
- "LICENSE",
34
- "CHANGELOG.md"
35
- ],
36
- "engines": {
37
- "node": ">=20.6"
38
- },
39
- "publishConfig": {
40
- "access": "public",
41
- "registry": "https://registry.npmjs.org/"
42
- },
43
- "keywords": [
44
- "gitruck",
45
- "同合云",
46
- "video",
47
- "oral-cut",
48
- "口播剪辑",
49
- "cli",
50
- "jianying",
51
- "剪映",
52
- "agent"
53
- ],
54
- "scripts": {
55
- "gtrk": "bun run src/index.ts",
56
- "test:purify": "esbuild src/commands/purify.ts --bundle --platform=node --format=esm --external:bun:sqlite --outfile=.test-build/purify-cmd.mjs && node --test test/purify-cmd.test.mjs",
57
- "build": "bun build ./src/index.ts --target=node --minify --outfile dist/index.js",
58
- "typecheck": "tsc --noEmit",
59
- "prepublishOnly": "npm run typecheck && npm test && bun run build",
60
- "test": "node scripts/workspace-pollution-gate.mjs --snapshot && esbuild src/lib/compliance-notice.ts src/lib/config.ts src/lib/crash-report.ts src/lib/usage-analytics.ts src/lib/log.ts src/lib/read-json.ts src/lib/sidecar-write.ts src/lib/user-config.ts src/lib/enum-catalog.ts src/lib/first-run-tutorial.ts src/lib/desktop-client-hint.ts src/lib/command-path.ts src/lib/user-path.ts src/lib/runtime-channel.ts src/lib/self-install.ts src/lib/chunk-upload.ts src/lib/upload-cache.ts src/lib/upload-submit.ts src/lib/render.ts src/lib/cloud.ts src/lib/materialize.ts src/lib/jianying.ts src/lib/clip-brief.ts src/lib/media.ts src/lib/projection.ts src/lib/reproject.ts src/lib/splitdoc.ts src/lib/gtrk-writeback.ts src/lib/project-background.ts src/lib/client-only-state.ts src/lib/gtrk-patch.ts src/lib/frame-domain.ts src/lib/clock-adapter.ts src/lib/gtrk-invariants.ts src/lib/clip-speed.ts src/lib/clip-keyframes.ts src/lib/column-config.ts src/lib/third-party-catalog.ts src/lib/mg-adopt.ts src/lib/mg-registry.ts src/lib/matrix.ts src/lib/online-broll-client.ts src/lib/online-search-journal.ts src/lib/matrix-material.ts src/lib/matrix-fetch.ts src/lib/matrix-lay.ts src/lib/arrange-metering.ts src/lib/arrange-wire.ts src/lib/arrange-apply.ts src/lib/arrange-client.ts src/lib/arrange-gate.ts src/lib/arrange-decision-pin.ts src/lib/arrange-qc.ts src/lib/arrange-qc-bind.ts src/lib/rate-limit-wait.ts src/lib/embed-client.ts src/lib/describe.ts src/lib/highlight-rubric.ts src/lib/highlight-input.ts src/lib/local-index.ts src/lib/character-input.ts src/lib/index-decode.ts src/lib/local-search.ts src/lib/qc.ts src/lib/alignment-qc.ts src/lib/bgm-history.ts src/lib/image-move.ts src/lib/cloud-render-guard.ts src/lib/outdir-guard.ts src/lib/landing-wait.ts src/lib/material-integrity.ts src/lib/project-health.ts src/lib/contact-sheet.ts src/lib/solid-png.ts src/lib/mask-raster.ts src/lib/mg-lint.ts src/lib/mg-visual-job.ts src/lib/mg-track-identity.ts src/lib/particle-identity.ts src/lib/text-ir/compile.ts src/lib/text-ir/validate.ts src/lib/text-ir/compile-local.ts src/lib/text-templates.ts src/lib/text-ir-client.ts src/lib/mg-lay.ts src/lib/mg-render.ts src/lib/particle-qtrle.ts src/lib/composite-particle.ts src/lib/composite-lay.ts src/lib/composite-lint.ts src/lib/composite-browser.ts src/lib/composite-plan.ts src/lib/composite-shots.ts src/lib/composite-resolve.ts src/lib/composite-profile.ts src/lib/composite-scene.ts src/lib/composite-bake-plan.ts src/lib/composite-bake.ts src/lib/composite-export.ts src/lib/tool-descriptors.ts src/lib/tool-runner.ts src/lib/tool-batch.ts src/lib/tool-pricing.ts src/lib/transcript.ts src/lib/runtime-assets.ts src/lib/subtitle-lay.ts src/lib/caption-align.ts src/lib/subtitle-line-split-client.ts src/lib/audio-align.ts src/lib/audio-tighten.ts src/lib/cut-snap.ts src/lib/mg-coverage.ts src/lib/ai-drama-lay.ts src/lib/ai-drama-pack.ts src/lib/desk-locate.ts src/lib/pip-lay.ts --bundle --platform=node --format=esm --external:bun:sqlite --outdir=.test-build --out-extension:.js=.mjs && esbuild src/lib/text-ir/compile.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/text-ir-compile.mjs && esbuild src/commands/split.ts --bundle --platform=node --external:bun:sqlite --format=esm --outdir=.test-build --out-extension:.js=.mjs && esbuild src/commands/patch.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/patch-cmd.mjs && esbuild src/commands/matrix.ts --bundle --platform=node --format=esm --external:bun:sqlite --outfile=.test-build/matrix-cmd.mjs && esbuild src/commands/mg.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/mg-cmd.mjs && esbuild src/commands/mg-text.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/mg-text-cmd.mjs && esbuild src/commands/tool.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/tool-cmd.mjs && esbuild src/commands/transcript.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/transcript-cmd.mjs && esbuild src/commands/project.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/project-cmd.mjs && esbuild src/commands/audio.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/audio-cmd.mjs && esbuild src/commands/subtitle.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/subtitle-cmd.mjs && esbuild src/commands/ai-drama.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/ai-drama-cmd.mjs && esbuild src/commands/feedback.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/feedback-cmd.mjs && esbuild src/commands/music-visualizer.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/music-visualizer-cmd.mjs && esbuild src/commands/long2short.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/long2short-cmd.mjs && esbuild src/commands/oralcut.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/oralcut-cmd.mjs && esbuild src/commands/oralcut-result.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/oralcut-result-cmd.mjs && esbuild src/commands/skills.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/skills-cmd.mjs && esbuild src/commands/qc.ts --bundle --platform=node --format=esm --external:commander --external:bun:sqlite --outfile=.test-build/qc-cmd.mjs && esbuild src/commands/doctor.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/doctor-cmd.mjs && esbuild src/lib/convert/ir_to_jsx.ts src/lib/convert/ir_to_am.ts src/lib/convert/ir_to_nv.ts src/lib/convert/am_parse.ts src/lib/convert/nv_parse.ts src/lib/convert/parse.ts src/lib/convert/download.ts src/lib/convert/bake_ops.ts --bundle --platform=node --external:bun:sqlite --format=esm --outdir=.test-build/convert --out-extension:.js=.mjs && esbuild src/lib/mad/selector.ts src/lib/mad/techniques.ts src/lib/mad/beat.ts src/lib/mad/scan.ts src/lib/mad/data.ts src/lib/mad/pool.ts src/lib/mad/cloud-beat.ts src/lib/mad/mad.ts --bundle --platform=node --external:bun:sqlite --format=esm --outdir=.test-build/mad --out-extension:.js=.mjs && esbuild src/commands/composite.ts --bundle --platform=node --format=esm --external:bun:sqlite --external:commander --outfile=.test-build/composite-cmd.mjs && esbuild src/commands/purify.ts --bundle --platform=node --format=esm --external:bun:sqlite --outfile=.test-build/purify-cmd.mjs && node --import ./test/helpers/test-home.mjs --test \"test/*.test.mjs\" && node scripts/workspace-pollution-gate.mjs --verify",
61
- "test:linux": "node scripts/test-linux.mjs",
62
- "openspec:gate": "node openspec/gate.mjs",
63
- "mg-registry:snapshot": "node scripts/mg-registry-snapshot.mjs",
64
- "text-template:snapshot": "node scripts/text-template-snapshot.mjs",
65
- "text-ir:parity": "node scripts/text-ir-parity.mjs",
66
- "text-ir:sync": "node scripts/text-ir-sync.mjs"
67
- },
68
- "devDependencies": {
69
- "@xmldom/xmldom": "^0.9.6",
70
- "bun-types": "^1.1.0",
71
- "commander": "^12.1.0",
72
- "esbuild": "^0.28.1",
73
- "typescript": "^5.6.0"
74
- },
75
- "dependencies": {
76
- "hash-wasm": "^4.12.0",
77
- "jszip": "^3.10.1"
78
- }
79
- }
1
+ {
2
+ "name": "@gitruck/cli",
3
+ "version": "1.3.9",
4
+ "description": "同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通。",
5
+ "license": "MIT",
6
+ "author": "Gitruck (同合云)",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Gitruck/cli.git"
10
+ },
11
+ "homepage": "https://github.com/Gitruck/cli#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/Gitruck/cli/issues"
14
+ },
15
+ "type": "module",
16
+ "bin": {
17
+ "gtrk": "dist/index.js"
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "assets",
22
+ "skills",
23
+ "contracts",
24
+ "AGENT.md",
25
+ "README.md",
26
+ "README.en.md",
27
+ "docs/reference.md",
28
+ "docs/reference.en.md",
29
+ "docs/workflow.md",
30
+ "docs/workflow.en.md",
31
+ "docs/development.md",
32
+ "docs/development.en.md",
33
+ "LICENSE",
34
+ "CHANGELOG.md"
35
+ ],
36
+ "engines": {
37
+ "node": ">=20.6"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public",
41
+ "registry": "https://registry.npmjs.org/"
42
+ },
43
+ "keywords": [
44
+ "gitruck",
45
+ "同合云",
46
+ "video",
47
+ "oral-cut",
48
+ "口播剪辑",
49
+ "cli",
50
+ "jianying",
51
+ "剪映",
52
+ "agent"
53
+ ],
54
+ "scripts": {
55
+ "gtrk": "bun run src/index.ts",
56
+ "test:purify": "esbuild src/commands/purify.ts --bundle --platform=node --format=esm --external:bun:sqlite --outfile=.test-build/purify-cmd.mjs && node --test test/purify-cmd.test.mjs",
57
+ "build": "bun build ./src/index.ts --target=node --minify --outfile dist/index.js",
58
+ "typecheck": "tsc --noEmit",
59
+ "prepublishOnly": "npm run typecheck && npm test && bun run build",
60
+ "test": "node scripts/workspace-pollution-gate.mjs --snapshot && esbuild src/lib/compliance-notice.ts src/lib/config.ts src/lib/crash-report.ts src/lib/usage-analytics.ts src/lib/log.ts src/lib/read-json.ts src/lib/sidecar-write.ts src/lib/user-config.ts src/lib/enum-catalog.ts src/lib/first-run-tutorial.ts src/lib/desktop-client-hint.ts src/lib/command-path.ts src/lib/user-path.ts src/lib/runtime-channel.ts src/lib/self-install.ts src/lib/chunk-upload.ts src/lib/upload-cache.ts src/lib/upload-submit.ts src/lib/render.ts src/lib/cloud.ts src/lib/materialize.ts src/lib/jianying.ts src/lib/clip-brief.ts src/lib/media.ts src/lib/projection.ts src/lib/reproject.ts src/lib/splitdoc.ts src/lib/gtrk-writeback.ts src/lib/project-background.ts src/lib/client-only-state.ts src/lib/gtrk-patch.ts src/lib/frame-domain.ts src/lib/clock-adapter.ts src/lib/gtrk-invariants.ts src/lib/clip-speed.ts src/lib/clip-keyframes.ts src/lib/column-config.ts src/lib/third-party-catalog.ts src/lib/mg-adopt.ts src/lib/mg-registry.ts src/lib/matrix.ts src/lib/online-broll-client.ts src/lib/online-search-journal.ts src/lib/matrix-material.ts src/lib/matrix-fetch.ts src/lib/matrix-lay.ts src/lib/arrange-metering.ts src/lib/arrange-wire.ts src/lib/arrange-apply.ts src/lib/arrange-client.ts src/lib/arrange-gate.ts src/lib/arrange-decision-pin.ts src/lib/arrange-qc.ts src/lib/arrange-qc-bind.ts src/lib/rate-limit-wait.ts src/lib/embed-client.ts src/lib/describe.ts src/lib/highlight-rubric.ts src/lib/highlight-input.ts src/lib/local-index.ts src/lib/character-input.ts src/lib/index-decode.ts src/lib/local-search.ts src/lib/qc.ts src/lib/alignment-qc.ts src/lib/bgm-history.ts src/lib/image-move.ts src/lib/cloud-render-guard.ts src/lib/outdir-guard.ts src/lib/landing-wait.ts src/lib/material-integrity.ts src/lib/project-health.ts src/lib/contact-sheet.ts src/lib/media-transcript.ts src/lib/media-grid.ts src/lib/media-ffmpeg.ts src/lib/solid-png.ts src/lib/mask-raster.ts src/lib/mg-lint.ts src/lib/mg-visual-job.ts src/lib/mg-track-identity.ts src/lib/particle-identity.ts src/lib/text-ir/compile.ts src/lib/text-ir/validate.ts src/lib/text-ir/compile-local.ts src/lib/text-templates.ts src/lib/text-ir-client.ts src/lib/mg-lay.ts src/lib/mg-render.ts src/lib/particle-qtrle.ts src/lib/composite-particle.ts src/lib/composite-lay.ts src/lib/composite-lint.ts src/lib/composite-browser.ts src/lib/composite-plan.ts src/lib/composite-shots.ts src/lib/composite-resolve.ts src/lib/composite-profile.ts src/lib/composite-scene.ts src/lib/composite-bake-plan.ts src/lib/composite-bake.ts src/lib/composite-export.ts src/lib/tool-descriptors.ts src/lib/tool-runner.ts src/lib/tool-batch.ts src/lib/tool-pricing.ts src/lib/transcript.ts src/lib/runtime-assets.ts src/lib/subtitle-lay.ts src/lib/caption-align.ts src/lib/subtitle-line-split-client.ts src/lib/audio-align.ts src/lib/audio-tighten.ts src/lib/cut-snap.ts src/lib/mg-coverage.ts src/lib/ai-drama-lay.ts src/lib/ai-drama-pack.ts src/lib/desk-locate.ts src/lib/pip-lay.ts --bundle --platform=node --format=esm --external:bun:sqlite --outdir=.test-build --out-extension:.js=.mjs && esbuild src/lib/text-ir/compile.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/text-ir-compile.mjs && esbuild src/commands/split.ts --bundle --platform=node --external:bun:sqlite --format=esm --outdir=.test-build --out-extension:.js=.mjs && esbuild src/commands/patch.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/patch-cmd.mjs && esbuild src/commands/matrix.ts --bundle --platform=node --format=esm --external:bun:sqlite --outfile=.test-build/matrix-cmd.mjs && esbuild src/commands/mg.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/mg-cmd.mjs && esbuild src/commands/mg-text.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/mg-text-cmd.mjs && esbuild src/commands/tool.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/tool-cmd.mjs && esbuild src/commands/transcript.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/transcript-cmd.mjs && esbuild src/commands/project.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/project-cmd.mjs && esbuild src/commands/audio.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/audio-cmd.mjs && esbuild src/commands/subtitle.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/subtitle-cmd.mjs && esbuild src/commands/ai-drama.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/ai-drama-cmd.mjs && esbuild src/commands/feedback.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/feedback-cmd.mjs && esbuild src/commands/music-visualizer.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/music-visualizer-cmd.mjs && esbuild src/commands/long2short.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/long2short-cmd.mjs && esbuild src/commands/oralcut.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/oralcut-cmd.mjs && esbuild src/commands/oralcut-result.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/oralcut-result-cmd.mjs && esbuild src/commands/skills.ts --bundle --platform=node --external:bun:sqlite --format=esm --outfile=.test-build/skills-cmd.mjs && esbuild src/commands/qc.ts --bundle --platform=node --format=esm --external:commander --external:bun:sqlite --outfile=.test-build/qc-cmd.mjs && esbuild src/commands/media.ts --bundle --platform=node --format=esm --external:commander --external:bun:sqlite --outfile=.test-build/media-cmd.mjs && esbuild src/commands/doctor.ts --bundle --platform=node --external:bun:sqlite --format=esm --external:commander --outfile=.test-build/doctor-cmd.mjs && esbuild src/lib/convert/ir_to_jsx.ts src/lib/convert/ir_to_am.ts src/lib/convert/ir_to_nv.ts src/lib/convert/am_parse.ts src/lib/convert/nv_parse.ts src/lib/convert/parse.ts src/lib/convert/download.ts src/lib/convert/bake_ops.ts --bundle --platform=node --external:bun:sqlite --format=esm --outdir=.test-build/convert --out-extension:.js=.mjs && esbuild src/lib/mad/selector.ts src/lib/mad/techniques.ts src/lib/mad/beat.ts src/lib/mad/scan.ts src/lib/mad/data.ts src/lib/mad/pool.ts src/lib/mad/cloud-beat.ts src/lib/mad/mad.ts --bundle --platform=node --external:bun:sqlite --format=esm --outdir=.test-build/mad --out-extension:.js=.mjs && esbuild src/commands/composite.ts --bundle --platform=node --format=esm --external:bun:sqlite --external:commander --outfile=.test-build/composite-cmd.mjs && esbuild src/commands/purify.ts --bundle --platform=node --format=esm --external:bun:sqlite --outfile=.test-build/purify-cmd.mjs && node --import ./test/helpers/test-home.mjs --test \"test/*.test.mjs\" && node scripts/workspace-pollution-gate.mjs --verify",
61
+ "test:linux": "node scripts/test-linux.mjs",
62
+ "openspec:gate": "node openspec/gate.mjs",
63
+ "mg-registry:snapshot": "node scripts/mg-registry-snapshot.mjs",
64
+ "text-template:snapshot": "node scripts/text-template-snapshot.mjs",
65
+ "text-ir:parity": "node scripts/text-ir-parity.mjs",
66
+ "text-ir:sync": "node scripts/text-ir-sync.mjs"
67
+ },
68
+ "devDependencies": {
69
+ "@xmldom/xmldom": "^0.9.6",
70
+ "bun-types": "^1.1.0",
71
+ "commander": "^12.1.0",
72
+ "esbuild": "^0.28.1",
73
+ "typescript": "^5.6.0"
74
+ },
75
+ "dependencies": {
76
+ "hash-wasm": "^4.12.0",
77
+ "jszip": "^3.10.1"
78
+ }
79
+ }