@gitruck/cli 1.1.3 → 1.1.5

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gitruck/cli",
3
- "version": "1.1.3",
3
+ "version": "1.1.5",
4
4
  "description": "同合云成片流水线 CLI —— agent 驱动云端任务、产物拉回本地、三方工程文件(客户端/剪映/PR)互通。",
5
5
  "license": "MIT",
6
6
  "author": "Gitruck (同合云)",
@@ -50,7 +50,7 @@
50
50
  "build": "bun build ./src/index.ts --target=node --minify --outfile dist/index.js",
51
51
  "typecheck": "tsc --noEmit",
52
52
  "prepublishOnly": "npm run typecheck && npm test && bun run build",
53
- "test": "esbuild src/lib/compliance-notice.ts src/lib/first-run-tutorial.ts src/lib/desktop-client-hint.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/gtrk-patch.ts src/lib/column-config.ts src/lib/matrix.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/local-index.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/material-integrity.ts src/lib/solid-png.ts src/lib/mg-lint.ts src/lib/mg-lay.ts src/lib/mg-render.ts src/lib/tool-descriptors.ts src/lib/tool-runner.ts src/lib/tool-pricing.ts src/lib/transcript.ts src/lib/runtime-assets.ts src/lib/subtitle-lay.ts src/lib/audio-align.ts src/lib/audio-tighten.ts --bundle --platform=node --format=esm --external:bun:sqlite --outdir=.test-build --out-extension:.js=.mjs && esbuild src/commands/split.ts --bundle --platform=node --format=esm --outdir=.test-build --out-extension:.js=.mjs && esbuild src/commands/patch.ts --bundle --platform=node --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 --format=esm --outfile=.test-build/mg-cmd.mjs && esbuild src/commands/tool.ts --bundle --platform=node --format=esm --outfile=.test-build/tool-cmd.mjs && esbuild src/commands/transcript.ts --bundle --platform=node --format=esm --outfile=.test-build/transcript-cmd.mjs && esbuild src/commands/project.ts --bundle --platform=node --format=esm --outfile=.test-build/project-cmd.mjs && esbuild src/commands/audio.ts --bundle --platform=node --format=esm --outfile=.test-build/audio-cmd.mjs && esbuild src/commands/subtitle.ts --bundle --platform=node --format=esm --outfile=.test-build/subtitle-cmd.mjs && esbuild src/commands/feedback.ts --bundle --platform=node --format=esm --outfile=.test-build/feedback-cmd.mjs && esbuild src/commands/music-visualizer.ts --bundle --platform=node --format=esm --external:commander --outfile=.test-build/music-visualizer-cmd.mjs && esbuild src/commands/long2short.ts --bundle --platform=node --format=esm --external:commander --outfile=.test-build/long2short-cmd.mjs && esbuild src/commands/oralcut.ts --bundle --platform=node --format=esm --external:commander --outfile=.test-build/oralcut-cmd.mjs && esbuild src/commands/skills.ts --bundle --platform=node --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/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 --format=esm --outdir=.test-build/convert --out-extension:.js=.mjs && esbuild src/lib/mad/selector.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 --format=esm --outdir=.test-build/mad --out-extension:.js=.mjs && node --import ./test/helpers/test-home.mjs --test \"test/*.test.mjs\"",
53
+ "test": "esbuild src/lib/compliance-notice.ts src/lib/first-run-tutorial.ts src/lib/desktop-client-hint.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/gtrk-patch.ts src/lib/frame-domain.ts src/lib/clock-adapter.ts src/lib/gtrk-invariants.ts src/lib/column-config.ts src/lib/matrix.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/local-index.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/material-integrity.ts src/lib/solid-png.ts src/lib/mg-lint.ts src/lib/mg-lay.ts src/lib/mg-render.ts src/lib/tool-descriptors.ts src/lib/tool-runner.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/ai-drama-lay.ts --bundle --platform=node --format=esm --external:bun:sqlite --outdir=.test-build --out-extension:.js=.mjs && esbuild src/commands/split.ts --bundle --platform=node --format=esm --outdir=.test-build --out-extension:.js=.mjs && esbuild src/commands/patch.ts --bundle --platform=node --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 --format=esm --outfile=.test-build/mg-cmd.mjs && esbuild src/commands/tool.ts --bundle --platform=node --format=esm --outfile=.test-build/tool-cmd.mjs && esbuild src/commands/transcript.ts --bundle --platform=node --format=esm --outfile=.test-build/transcript-cmd.mjs && esbuild src/commands/project.ts --bundle --platform=node --format=esm --outfile=.test-build/project-cmd.mjs && esbuild src/commands/audio.ts --bundle --platform=node --format=esm --outfile=.test-build/audio-cmd.mjs && esbuild src/commands/subtitle.ts --bundle --platform=node --format=esm --outfile=.test-build/subtitle-cmd.mjs && esbuild src/commands/ai-drama.ts --bundle --platform=node --format=esm --outfile=.test-build/ai-drama-cmd.mjs && esbuild src/commands/feedback.ts --bundle --platform=node --format=esm --outfile=.test-build/feedback-cmd.mjs && esbuild src/commands/music-visualizer.ts --bundle --platform=node --format=esm --external:commander --outfile=.test-build/music-visualizer-cmd.mjs && esbuild src/commands/long2short.ts --bundle --platform=node --format=esm --external:commander --outfile=.test-build/long2short-cmd.mjs && esbuild src/commands/oralcut.ts --bundle --platform=node --format=esm --external:commander --outfile=.test-build/oralcut-cmd.mjs && esbuild src/commands/skills.ts --bundle --platform=node --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/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 --format=esm --outdir=.test-build/convert --out-extension:.js=.mjs && esbuild src/lib/mad/selector.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 --format=esm --outdir=.test-build/mad --out-extension:.js=.mjs && node --import ./test/helpers/test-home.mjs --test \"test/*.test.mjs\"",
54
54
  "openspec:gate": "node openspec/gate.mjs"
55
55
  },
56
56
  "devDependencies": {
@@ -1,23 +1,24 @@
1
1
  ---
2
2
  name: gtrk-ai-drama
3
- description: AI 再现分镜稿生成器——把成片 SOP 第③步(B-roll 底轨阶段)分到 AI_DRAMA 车道的 beat,逐个产**四段朴素描述(故事背景 / 角色 / 分镜 / 原文文稿)+ 独立视觉基调段 + 时长预算**,中英双版,可直接拿去任意外部 AI 漫剧管线(可灵 / 即梦 / Vidu / Veo / Runway / LTX / 本地开源,或 LibTV / OiiOii / TapNow 这类成片 agent 平台)上手用。这是**纯创作 skill、无 gtrk 命令**(产物即描述文本、出片在外部平台、用户手动拼回,同 /gtrk-style-maker)。当用户想「上 AI 再现 / 做 AI 分镜 / AI 动画描述 / 把这段做成 AI 视频 / 给这段配 AI 再现 / 弗洛伊德这段怎么做视频」时使用本 skill。通用拆镜 craft(蒙太奇段分层 / 四段描述结构 / 中英双语 / 时长预算)由本 skill 自持;栏目视觉 DNA(Style Lock)从栏目配置 style.skills 里 produces==AI_DRAMA 的条目解析注入,绝不硬编某栏目风格。凡把口播派单里 AI_DRAMA 段落转成可出片的描述稿,优先用本 skill,别手搓。
3
+ description: AI 再现分镜稿生成器——把成片 SOP 第③步(B-roll 底轨阶段)分到 AI_DRAMA 车道的 beat,逐个产**四段朴素描述(故事背景 / 角色 / 分镜 / 原文文稿)+ 独立视觉基调段 + 时长预算**,中英双版;Markdown 外壳默认严格兼容 gitruck-ai-drama-desk 解析器,可直接导入工作台,也可拿去任意外部 AI 漫剧管线(可灵 / 即梦 / Vidu / Veo / Runway / LTX / 本地开源,或 LibTV / OiiOii / TapNow 这类成片 agent 平台)上手用。创作与出片仍在 skill / 外部工作台;工作台导出的 return-v1 包用 `gtrk ai-drama lay` 确定性回填独立 AI 轨。当用户想「上 AI 再现 / 做 AI 分镜 / AI 动画描述 / 把这段做成 AI 视频 / 给这段配 AI 再现 / 弗洛伊德这段怎么做视频」时使用本 skill。通用拆镜 craft(蒙太奇段分层 / 四段描述结构 / 中英双语 / 时长预算)由本 skill 自持;栏目视觉 DNA(Style Lock)从栏目配置 style.skills 里 produces==AI_DRAMA 的条目解析注入,绝不硬编某栏目风格。凡把口播派单里 AI_DRAMA 段落转成可出片的描述稿,优先用本 skill,别手搓。
4
4
  ---
5
5
 
6
6
  # AI 再现分镜稿生成器(gtrk-ai-drama)
7
7
 
8
- 把口播派单里分到 **`AI_DRAMA` 车道**的 beat,逐个拆成分镜、为每个 beat 产 **四段朴素描述 + 独立视觉基调 + 时长预算**(中英双版),落到 `<project>/ai-drama/<beat_id>.md`,再交棒用户拿去任意外部平台出片、手动拼回时间线。
8
+ 把口播派单里分到 **`AI_DRAMA` 车道**的 beat,逐个拆成分镜、为每个 beat 产 **四段朴素描述 + 独立视觉基调 + 时长预算**(中英双版),落到 `<project>/ai-drama/<beat_id>.md`,再交棒 AI Drama Desk 或任意外部平台出片;标准工作台导出包由 CLI 自动回填时间线。
9
9
 
10
- > **这是纯创作 skill、没有 gtrk 命令**——AI_DRAMA 车道的产物就是**描述文本本身**,出片在外部生成平台、拼回时间线由用户手动做,没有确定性机械尾巴可下沉成 CLI 命令(性质同 `/gtrk-style-maker`)。所以「脑」是你(拆镜 + 注入栏目风格 + 管用户交互),「手」在外部平台和用户手里,不是 gtrk。别去找 `gtrk ai-drama` 这条命令,它不存在也不该存在。
10
+ > **脑手分工**:本 skill 负责创作(拆镜 + 注入栏目风格 + 管用户交互),外部工作台负责生成;`gtrk ai-drama lay` 只消费 AI Drama Desk 的 return-v1 导出包,把已经生成好的片段复制进工程并铺到一条独立 AI 视频轨。该命令不生成内容、不调用模型、不计费,也不改既有 A-roll / BGM / 三条 B-roll 候选轨。
11
11
 
12
12
  ## 为什么是「四段朴素描述」而不是「平台优化提示词」
13
13
 
14
- 因为**责权利要清**:无论用户拿去哪个管线,一致性的可移植载体是**角色/场景描述**,不是某平台的提示词语法("assets model-agnostic, prompts model-specific")。而且下游 agent 成片平台(LibTV / OiiOii / TapNow 等)拿到分镜稿会**自己重拆分镜、重写 prompt**,角色圣经(三视图)也在那些产品里生成——所以本 skill **不产平台优化提示词、不产角色圣经、不产参考图、不产成片**,只产四段可移植描述 + 视觉基调 + 时长,把「怎么出片、怎么保角色一致」交给下游平台,把「拼回时间线」交给用户。
14
+ 因为**责权利要清**:无论用户拿去哪个管线,一致性的可移植载体是**角色/场景描述**,不是某平台的提示词语法("assets model-agnostic, prompts model-specific")。而且下游 agent 成片平台(LibTV / OiiOii / TapNow 等)拿到分镜稿会**自己重拆分镜、重写 prompt**,角色圣经(三视图)也在那些产品里生成——所以本 skill **不产平台优化提示词、不产角色圣经、不产参考图、不产成片**,只产四段可移植描述 + 视觉基调 + 时长,把「怎么出片、怎么保角色一致」交给下游平台。**「拼回时间线」不再交给用户手工**:AI Drama Desk 的
15
+ return-v1 标准包由 `gtrk ai-drama lay` 确定性回填;其他平台没有标准包时才降级为用户手动回铺。
15
16
 
16
17
  ## SOP 定位:第 ③ 步 · B-roll 底轨阶段的 AI 情景片段腿(**不是最后**)
17
18
 
18
19
  成片是**有序 SOP + 用户检查点**,不是并行一把铺。次序:
19
20
 
20
- > ① oralcut 剪口播 → ② split 视觉拆分派单 → ③ **B-roll 底轨全铺齐**(`matrix` 铺影视/本地素材 + **本 skill 产稿→外部出片→用户手动回铺**,**两条腿同一阶段**)→ ④ **全局抽帧检查画面构图**(用户确认)→ ⑤ **mg 铺 MG(含 ov,最后叠上)** → `gtrk subtitle lay` 上字幕(快速成片模式必做)→ `gtrk render` 收口
21
+ > ① oralcut 剪口播 → ② split 视觉拆分派单 → ③ **B-roll 底轨全铺齐**(`matrix` 铺影视/本地素材 + **本 skill 产稿→外部出片→`gtrk ai-drama lay` 回填**,**两条腿同一阶段**)→ ④ **全局抽帧检查画面构图**(用户确认)→ ⑤ **mg 铺 MG(含 ov,最后叠上)** → `gtrk subtitle lay` 上字幕(快速成片模式必做)→ `gtrk render` 收口
21
22
 
22
23
  **你与 `matrix` 同属第 ③ 步,MUST 在 MG 之前落位。** 理由:**AI 情景片段属于底轨 B-roll 画面家族,不是叠加层**——整条管线里唯一的叠加层是 MG(含 ov)。MG 的排版决策是「因势象形避主体」、**依赖底轨的最终画面构图**;若你的片段在 MG 之后才回铺,底轨构图就变了,先产的 MG 是对着「还没有 AI 画面的底轨」做的避让,你的片段落位后必然被避让错位、甚至被 MG 盖住画面主体。
23
24
 
@@ -25,13 +26,14 @@ description: AI 再现分镜稿生成器——把成片 SOP 第③步(B-roll
25
26
 
26
27
  **异步等待的口子(你造成的那个)**:你产完描述后,用户要去外部平台出片、抽卡可能数天。严格串行会把 ④⑤ 无限期卡住,故约定:进 ⑤ 的硬门是「**AI 片段已回铺 ∨ 用户明示先跳过**」。走「明示跳过」时,下游 `/gtrk-mg` MUST 把与你的 beat 相邻/重叠区间的 MG 颗粒标记为「AI 回铺后待复查构图」并在收口时复述——**你在交代进度时也要把这个代价说清**,让用户知道跳过意味着将来可能返工那几颗。
27
28
 
28
- **你的下一步**:产完描述 → 用户拿去外部平台出片 → 手动回铺进工程 → 三源齐了进 **④ 全局抽帧检查构图**(对最终底轨抽帧、用户确认)→ 过了才交棒 ⑤ `/gtrk-mg`。**MUST NOT 直接从你这里跳到 `render`** ——后面还有构图检查与 MG 两步。
29
+ **你的下一步**:产完描述 → 用户拿去外部平台出片 → 若拿到 AI Drama Desk return-v1 导出目录,立即用 `gtrk ai-drama lay` 回填;其他平台没有标准包时才降级为用户手动回铺 → 三源齐了进 **④ 全局抽帧检查画面构图**(对最终底轨抽帧、用户确认)→ 过了才交棒 ⑤ `/gtrk-mg`。**MUST NOT 直接从你这里跳到 `render`** ——后面还有构图检查与 MG 两步。
29
30
 
30
- ## 三条铁律(先记死)
31
+ ## 四条铁律(先记死)
31
32
 
32
33
  1. **业务分离:通用 craft 归本 skill,栏目风格从配置解析。** 本 skill 只持**对任何栏目都通用的拆镜工艺**(蒙太奇段分层、四段描述结构、中英双语、时长预算)。**栏目专属的视觉 DNA / Style Lock(底色、单点强调色、影视参考锚点、材质、禁令)从有效栏目配置解析注入**(见下节),**绝不硬编某栏目风格**——尤其别把《实在界漫游指南》那套「黑白二元 + 单点朱红」当默认塞给所有人,那是它的栏目资产,该从配置来。未绑 AI_DRAMA 风格资产的栏目 → 用本 skill 的**中性默认**兜底。
33
34
  2. **只产描述,不产成片、不产角色圣经、不产参考图。** 本 skill 的交付物是四段可移植描述文本。出片(渲染成视频片段)、角色三视图/角色圣经、参考图,全在下游平台或用户手里。不要假装能帮他渲染或建角色资产。
34
35
  3. **中英双版地位平等,不是主辅。** ①视觉基调、②故事背景、③角色、④分镜四段每一段都给完整的中文版 + 英文版(不是把中文翻一句了事)。中文喂可灵 / 即梦 / Vidu / 通义,英文喂 Veo / Runway / LTX / Luma,用户按目标平台选。**唯独 ⑤ 原文文稿是中文口播原文、不翻译**(它是源真相 + 手动拼接的对位锚,翻译反而失真)。
36
+ 4. **内容可移植,Markdown 外壳不可自由改写。** 默认产物 MUST 通过 `gitruck-ai-drama-desk` 的 Markdown 解析契约:标题必须含 `(beat BXX)`;两大块必须叫 `## 一、中文稿…` / `## 二、English Storyboard…`;角色必须用 `#### 角色名(备注)` 标题;中文镜头必须用 `#### 分镜 01 · …`,英文镜头必须用 `#### Shot 01 · …`;元信息必须含 `区间总时长:track_st … → track_ed … ≈ … 秒`。`镜头`、粗体行、项目符号角色、`## 中文` / `## English`、`工程时间位` 都不是等价写法。**人读友好不能牺牲机读契约。**
35
37
 
36
38
  > **产物落点纪律(MUST · 全文见随包 `AGENT.md` 同名一节)**:
37
39
  > 分镜稿只落**工程目录**(`<project>/ai-drama/<beat_id>.md`)或**用户显式指定处**;
@@ -68,7 +70,8 @@ description: AI 再现分镜稿生成器——把成片 SOP 第③步(B-roll
68
70
  2. **每镜 3–8 秒**:短于 3 秒观众来不及呼吸;长于 8 秒 AI 视频模型会塌(人物表情 / 手部漂移)。
69
71
  3. **长文段拆蒙太奇段**:一个 AI_DRAMA beat 可能覆盖很长一段文稿、含多个场景/情境。此时**先拆蒙太奇段**(每段锚一个场景/情境),**段内 3–6 镜**按情感颗粒切、**段间可换场**。镜数上限按**段级**判定,**不再因整段总镜数超 6 就回退 splitter**——长区间整段做 AI 再现是合法创作,不是拆分错误。
70
72
  4. **切分触发器**(任一出现才考虑切):时间 / 空间改变、主体改变(例:理论家 → 当事人)、抽象概念登场(从「人物戏」切到「象征物戏」)、情绪转折(安静→刺点 / 爆发→凝滞)。**不要因一个句号、一个逗号就切。**
71
- 5. **控整体不控单镜**:你对**区间总时长**负责(各镜建议秒数之和量级贴合 `track_st→track_ed`),但不必把每一镜的秒数抠死——AI 平台交付本就不保证逐帧准,最终由用户手动拼时对齐区间。
73
+ 5. **控整体不控单镜**:你对**区间总时长**负责(各镜建议秒数之和量级贴合 `track_st→track_ed`),但不必把每一镜的秒数抠死——AI 平台交付本就不保证逐帧准,最终对齐由回填这一步兜:走 `gtrk ai-drama lay` 时它按窗口顺排、
74
+ 末镜尽量吸收到 `track_ed`;**素材短于窗口就留空档**(MUST NOT 拉伸/循环去填满),没有标准包才由用户手动拼。
72
75
 
73
76
  ### B. 视觉基调:独立成段 + 融入各描述(一源多处)
74
77
 
@@ -108,12 +111,33 @@ description: AI 再现分镜稿生成器——把成片 SOP 第③步(B-roll
108
111
 
109
112
  **Step 4 · 写四段描述(中英分块)**:先出**中文稿**(② 故事背景末句锁调 → ③ 角色结构化 + 禁变项 + 质感落点,复现理论家 pin 复用同段文字 → ④ 分镜每镜前缀复述基调 + 建议秒数 + 对应原文句 + 蒙太奇级意图 → ⑤ 原文文稿中文逐句不翻译),再整块出 **English Storyboard**(① Style Lock + ②③④ 英文原生化,不含 ⑤)。中英**分两大块、不逐段交错**(craft C)。
110
113
 
111
- **Step 5 · 落盘**:写到 `<project>/ai-drama/<beat_id>.md`(一 beat 一文件,靠 beat id 认得是哪段、互不覆盖)。用户直投单段时落到用户指定处或 `<cwd>/ai-drama/<自拟标题>.md`。
114
+ **Step 5 · 校验后落盘**:先把每个 beat 的候选稿写到系统 temp,运行随 skill 分发的 `scripts/validate-storyboard.mjs <候选稿.md>`;退出码为 0 才写到 `<project>/ai-drama/<beat_id>.md`。失败就按输出逐项修正,MUST NOT 把未通过稿件落进工程。校验器不可用时,至少逐项执行下面「解析器硬契约」与交付前自检。用户直投单段时同样先校验,再落到用户指定处或 `<cwd>/ai-drama/<自拟标题>.md`。临时稿无论成功失败都删掉。
112
115
 
113
116
  ## 输出契约(落盘 Markdown 结构 · 中英分块)
114
117
 
115
118
  顶部放一次责权利声明 + 元信息(不分语言);然后 **`## 一、中文稿`** 一整块、**`## 二、English Storyboard`** 一整块,用户整块复制。
116
119
 
120
+ ### 解析器硬契约(默认必须满足)
121
+
122
+ 下列是 `gitruck-ai-drama-desk` 的机器接口,不是排版建议:
123
+
124
+ - H1 标题含半角或全角括号包住的 `beat <beat_id>`,例如 `(beat B03)`。
125
+ - 元信息使用同一行:`- 区间总时长:track_st 37.685 → track_ed 65.665 ≈ 27.980 秒`;不要拆成「工程时间位」和「目标时长」。
126
+ - 中文与英文块标题分别以 `## 一、中文稿`、`## 二、English Storyboard` 开头;不要简写。否则英文 ①–④ 会被当成中文段并覆盖前面的中文段。
127
+ - ③ 内每个角色都用 `#### <角色名>(<备注>)` 开头,描述写在下一段;不要把角色写成 `- 男性:……` 项目符号。
128
+ - ④ 中文镜头头必须匹配 `#### 分镜 <数字> · <标题> |建议 ≈<秒数>s |段:… |场景:… |角色:… |对应原文:…`;英文镜头头必须匹配 `#### Shot <数字> · <title> |≈<seconds>s |segment: … |scene: … |cast: … |source lines: …`。必须用 Markdown 四级标题,中文关键词是「分镜」而不是「镜头」。
129
+ - 每镜标题后的首个非空正文必须以 `〔本镜视觉基调〕` 开头,解析器据此填 `stylePrefix`;不要写成 `- 视觉基调前缀:……` 普通项目符号。① 的负面词必须单独使用 `> 禁忌:…`,英文用 `> Avoid: …`,否则会被吞进 Style Lock 正文而不是负面字段。
130
+ - 蒙太奇分段只写进镜头头的 `|段:…` / `|segment: …` 字段;④ 内不要另插 `#### 蒙太奇段…` / `#### Montage…` 标题,解析器会把它吞进前一镜描述。
131
+ - 中英文镜头序号一一对应;至少一镜;每镜建议秒数写在头部字段,不要藏在正文项目符号里。
132
+
133
+ 校验命令(从本 `SKILL.md` 所在目录解析相对路径):
134
+
135
+ ```bash
136
+ bun scripts/validate-storyboard.mjs <候选稿.md>
137
+ ```
138
+
139
+ 通过时输出单行 JSON 并退出 0;不通过时逐条列出违约项并退出非零。它校验的是生产端硬契约;工作台仍是最终权威,仓库可用时可再用其 `server/lib/parse.ts` 做回归。
140
+
117
141
  ```
118
142
  # AI 再现分镜稿 · [自动标题](beat <beat_id>)
119
143
 
@@ -197,6 +221,8 @@ face / hair / eyes / wardrobe / signature prop + do-not-change + one line on how
197
221
 
198
222
  ## 交付前自检
199
223
 
224
+ - [ ] `scripts/validate-storyboard.mjs` 已对**每个**候选稿返回 `ok:true`,或在校验器不可用时完成了「解析器硬契约」逐项核对?
225
+ - [ ] 标题、两大块、③角色标题、④分镜标题与区间元信息使用契约原词,没有用“更好看”的同义排版替换?
200
226
  - [ ] 五区块齐备(元信息 + ①视觉基调 + ②故事背景 + ③角色 + ④分镜 + ⑤原文文稿)?
201
227
  - [ ] 每镜有**明确主语 + 明确动作**?(没有就退回)
202
228
  - [ ] 是**真的按情感颗粒切镜**,不是按句号切?长文段拆了**蒙太奇段**、段内 3–6 镜、段划分合理?
@@ -213,8 +239,10 @@ face / hair / eyes / wardrobe / signature prop + do-not-change + one line on how
213
239
  描述稿落盘后**别收工**,把接力讲清(但这一步的「手」在外部平台和用户手里、必须停下等用户):
214
240
 
215
241
  1. **交代出片**:让用户拿 `<project>/ai-drama/<beat_id>.md` 里的描述,去任意外部平台出片——中文版喂可灵 / 即梦 / Vidu;英文版喂 Veo / Runway / LTX / Luma;本地开源(Wan / Hunyuan 等)或成片 agent 平台(LibTV / OiiOii / TapNow)皆可,按下游平台的角色资产机制上传角色描述保一致。
216
- 2. **回铺时间线**:出好的 AI 片段**由用户手动**拖回 opencut 的 **AI_DRAMA 车道**,对齐该 beat 的 `track_st→track_ed`(**它属底轨 B-roll 画面家族,与影视/本地素材同层级;唯一在它之上的叠加层是 MG,而 MG 要等它落位后才产**),按区间总时长裁齐。
217
- 3. **停下等用户**:AI 出片与拼回是**外部异步、用户驱动**的——本 skill 在此停,等所有 AI_DRAMA beat 的片段都回铺齐。这是 SOP 里的用户检查点,别替他假装出片 / 回轨完成。
242
+ 2. **优先标准包回填**:AI Drama Desk 出片后,找到每个项目的 `exports/aidrama/manifest.json`,执行:
243
+ `gtrk ai-drama lay --project "<工程目录>" --package "<B03 exports/aidrama>" --package "<B05 exports/aidrama>" --json`。
244
+ 命令会先按当刻口播工程重投影 beat 窗口,再把所有镜头复制到 `<gtrk目录>/assets/ai-drama/<slug>/`,共用一条新的 `video_track`;每个 beat 从起点顺排,最后一镜吸收到 beat 终点。既有 A-roll、BGM 与 B-roll 轨零改动。若已铺 AI 轨被用户手调过,缺省拒绝覆盖;只有用户明确同意丢弃手调时才加 `--replace-all`。
245
+ 3. **非标准平台才手动回铺**:外部平台没有 return-v1 清单时,才由用户把片段拖回 opencut 的 **AI_DRAMA 车道**,对齐该 beat 的 `track_st→track_ed`。AI 出片仍是外部异步、用户驱动;未拿到完整片段前要停下等用户,别替他假装出片 / 回轨完成。
218
246
  4. **回铺齐 ≠ 收口,还有两步**:你的片段回铺齐只意味着 **③ B-roll 底轨阶段**完成。接着 MUST 走 **④ 全局抽帧检查画面构图**(对三源合并后的最终底轨抽帧,看主体位置 / 安全区 / 画面朝向 / 明暗,**停下等用户确认**)→ 过了才交棒 **⑤ `/gtrk-mg`** 把 MG(含 ov)叠上 → 最后才 `gtrk render` 收口。
219
247
  ⚠️ **MUST NOT 从你这里直接跳到 `render`**(旧文档曾这么写,那是错的——它把 AI 再现当成了最后一层)。
220
248
 
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { readFile } from "node:fs/promises";
4
+ import { basename } from "node:path";
5
+
6
+ const files = process.argv.slice(2);
7
+ if (files.length === 0) {
8
+ console.error("用法:bun scripts/validate-storyboard.mjs <分镜稿.md> [...]");
9
+ process.exit(2);
10
+ }
11
+
12
+ function firstMatchIndex(text, re) {
13
+ const m = re.exec(text);
14
+ return m ? m.index : -1;
15
+ }
16
+
17
+ function validate(md, file) {
18
+ const errors = [];
19
+ const beat = md.match(/[((]\s*beat\s+([A-Za-z0-9_-]+)\s*[))]/i)?.[1] ?? "";
20
+ if (!beat) errors.push("H1 标题缺少「(beat BXX)」");
21
+
22
+ const meta = md.match(/^[-*]\s*区间总时长\s*[::]\s*track_st\s+(-?\d+(?:\.\d+)?)\s*→\s*track_ed\s+(-?\d+(?:\.\d+)?)\s*≈\s*(\d+(?:\.\d+)?)\s*秒\s*$/m);
23
+ if (!meta) errors.push("缺少单行元信息「区间总时长:track_st … → track_ed … ≈ … 秒」");
24
+
25
+ const cnStart = firstMatchIndex(md, /^##\s*一、中文稿.*$/m);
26
+ const enStart = firstMatchIndex(md, /^##\s*二、English Storyboard.*$/mi);
27
+ if (cnStart < 0) errors.push("缺少「## 一、中文稿…」区块");
28
+ if (enStart < 0) errors.push("缺少「## 二、English Storyboard…」区块");
29
+ if (cnStart >= 0 && enStart >= 0 && cnStart >= enStart) errors.push("中文稿必须位于 English Storyboard 之前");
30
+
31
+ const cn = cnStart >= 0 ? md.slice(cnStart, enStart >= 0 ? enStart : md.length) : "";
32
+ const en = enStart >= 0 ? md.slice(enStart) : "";
33
+ for (const n of ["①", "②", "③", "④", "⑤"]) {
34
+ if (!new RegExp(`^#{1,6}\\s*${n}`, "m").test(cn)) errors.push(`中文稿缺少 ${n} 区块`);
35
+ }
36
+ for (const n of ["①", "②", "③", "④"]) {
37
+ if (!new RegExp(`^#{1,6}\\s*${n}`, "m").test(en)) errors.push(`English Storyboard 缺少 ${n} 区块`);
38
+ }
39
+ if (!/^>\s*禁忌\s*[::]/m.test(cn)) errors.push("① 视觉基调缺少独立的「> 禁忌:…」行");
40
+ if (!/^>\s*Avoid\s*[::]/mi.test(en)) errors.push("English ① Style Lock 缺少独立的「> Avoid: …」行");
41
+
42
+ const cnCharBlock = cn.match(/^#{1,6}\s*③[^\n]*\n([\s\S]*?)(?=^#{1,6}\s*④)/m)?.[1] ?? "";
43
+ const enCharBlock = en.match(/^#{1,6}\s*③[^\n]*\n([\s\S]*?)(?=^#{1,6}\s*④)/m)?.[1] ?? "";
44
+ const cnChars = [...cnCharBlock.matchAll(/^####\s+(.+?)\s*$/gm)].map((m) => m[1].trim());
45
+ const enChars = [...enCharBlock.matchAll(/^####\s+(.+?)\s*$/gm)].map((m) => m[1].trim());
46
+ if (cnChars.length === 0) errors.push("③ 角色描述中没有「#### 角色名(备注)」标题");
47
+ if (enChars.length === 0) errors.push("English ③ Characters 中没有角色四级标题");
48
+
49
+ const cnShotRe = /^####\s*分镜\s*(\d+)\s*[·•..]\s*(.*?)\s*[||]\s*建议\s*≈?\s*(\d+(?:\.\d+)?)\s*s\b.*[||]\s*角色\s*[::]\s*([^||\n]+)/gim;
50
+ const enShotRe = /^####\s*Shot\s*(\d+)\s*[·•..]\s*(.*?)\s*[||]\s*≈?\s*(\d+(?:\.\d+)?)\s*s\b.*[||]\s*cast\s*[::]\s*([^||\n]+)/gim;
51
+ const cnShots = [...cn.matchAll(cnShotRe)].map((m) => Number(m[1]));
52
+ const enShots = [...en.matchAll(enShotRe)].map((m) => Number(m[1]));
53
+ if (cnShots.length === 0) errors.push("④ 中没有可解析的「#### 分镜 01 · … |建议 ≈Xs … |角色:…」标题");
54
+ if (enShots.length === 0) errors.push("English ④ 中没有可解析的「#### Shot 01 · … |≈Xs … |cast: …」标题");
55
+ if (cnShots.length > 0 && enShots.length > 0 && JSON.stringify(cnShots) !== JSON.stringify(enShots)) {
56
+ errors.push(`中英文镜头序号不一致:CN=${cnShots.join(",")},EN=${enShots.join(",")}`);
57
+ }
58
+
59
+ const cnShotBlock = cn.match(/^#{1,6}\s*④[^\n]*\n([\s\S]*?)(?=^#{1,6}\s*⑤)/m)?.[1] ?? "";
60
+ const enShotBlock = en.match(/^#{1,6}\s*④[^\n]*\n([\s\S]*)/m)?.[1] ?? "";
61
+ const cnHeads = [...cnShotBlock.matchAll(/^####\s+(.+?)\s*$/gm)];
62
+ const enHeads = [...enShotBlock.matchAll(/^####\s+(.+?)\s*$/gm)];
63
+ const extraCnHeads = cnHeads.filter((m) => !/^分镜\s*\d+/i.test(m[1]));
64
+ const extraEnHeads = enHeads.filter((m) => !/^Shot\s*\d+/i.test(m[1]));
65
+ if (extraCnHeads.length > 0) errors.push(`④ 内存在非分镜四级标题:${extraCnHeads.map((m) => m[1]).join("、")};请把蒙太奇名放进「段」字段`);
66
+ if (extraEnHeads.length > 0) errors.push(`English ④ 内存在非 Shot 四级标题:${extraEnHeads.map((m) => m[1]).join("、")};请把 montage 名放进 segment 字段`);
67
+
68
+ function checkPrefixes(block, heads, label) {
69
+ for (let i = 0; i < heads.length; i += 1) {
70
+ const start = (heads[i].index ?? 0) + heads[i][0].length;
71
+ const end = i + 1 < heads.length ? heads[i + 1].index : block.length;
72
+ const firstBodyLine = block.slice(start, end).split(/\r?\n/).map((line) => line.trim()).find(Boolean) ?? "";
73
+ if (!firstBodyLine.startsWith("〔") || !firstBodyLine.includes("〕")) {
74
+ errors.push(`${label} ${heads[i][1].match(/\d+/)?.[0] ?? "?"} 的首个正文不是「〔视觉基调〕」前缀`);
75
+ }
76
+ }
77
+ }
78
+ checkPrefixes(cnShotBlock, cnHeads.filter((m) => /^分镜\s*\d+/i.test(m[1])), "中文分镜");
79
+ checkPrefixes(enShotBlock, enHeads.filter((m) => /^Shot\s*\d+/i.test(m[1])), "English Shot");
80
+
81
+ if (/^\s*[-*]\s*(男性|女性|男主|女主|父亲|母亲|角色)\s*[::]/m.test(cnCharBlock)) {
82
+ errors.push("③ 角色仍使用项目符号;请改成四级标题并把描述另起段落");
83
+ }
84
+ if (/^\s*\*\*(镜头|分镜|Shot)\s*\d+/mi.test(md)) {
85
+ errors.push("镜头仍使用粗体行;请改成「#### 分镜…」/「#### Shot…」四级标题");
86
+ }
87
+
88
+ if (meta) {
89
+ const st = Number(meta[1]);
90
+ const ed = Number(meta[2]);
91
+ const total = Number(meta[3]);
92
+ if (!(ed > st)) errors.push("track_ed 必须大于 track_st");
93
+ if (Math.abs((ed - st) - total) > 0.11) errors.push(`区间总时长与 track_ed-track_st 不一致:${total} vs ${(ed - st).toFixed(3)}`);
94
+ }
95
+
96
+ return {
97
+ ok: errors.length === 0,
98
+ file: basename(file),
99
+ beatId: beat || null,
100
+ characters: cnChars.length,
101
+ shots: cnShots.length,
102
+ errors,
103
+ };
104
+ }
105
+
106
+ let failed = false;
107
+ for (const file of files) {
108
+ try {
109
+ const md = await readFile(file, "utf8");
110
+ const result = validate(md, file);
111
+ console.log(JSON.stringify(result));
112
+ failed ||= !result.ok;
113
+ } catch (error) {
114
+ failed = true;
115
+ console.log(JSON.stringify({ ok: false, file: basename(file), errors: [String(error?.message ?? error)] }));
116
+ }
117
+ }
118
+
119
+ process.exit(failed ? 1 : 0);
@@ -177,7 +177,7 @@ description: B-roll 检索铺轨编排手册——成片管线里第一个铺的
177
177
  |---|---|---|---|
178
178
  | 搜整条素材(BGM 主场) | `gtrk matrix material "<词>"` | 字符串 · — | 单条检索;`--out <file>` 落盘或缺省 stdout |
179
179
  | 指定素材形态 | `--scope <s>` | `clip` \| `image` \| `audio` · `audio` | 三态通用;clip 要的是**整条**原片直链(要段落用 `matrix search`) |
180
- | 只要可商用 | `--commercial-only` | 开关 · 关(搜全库) | 成员口传 `copyright_scope=commercial`;公开口本就只含可商用,该档位会**显式提示**而非静默忽略 |
180
+ | 只要可商用 | `--commercial-only` | 开关 · 关(**成员档即全库,这是缺省而非将就**) | **按需收紧的显式开关,MUST NOT 默认带上**(见下方选材口径):成员口传 `copyright_scope=commercial`;公开口本就只含可商用,该档位会**显式提示**而非静默忽略 |
181
181
  | 按成片时长挑 | `--min-duration <秒>` / `--max-duration <秒>` | 数字 · 不过滤 | 可单独或组合给;成员口透传服务端 `filters`,公开口无该入参(CLI 按结果 `duration` 本地过滤并提示);负数 / 上界小于下界=参数错误 |
182
182
  | 多给几条候选 | `--top-k <n>` | 整数 · `5`(服务端上限 50) | BGM 推荐 3-5 首的量级 |
183
183
  | 去同质化 | `--diversity` | 开关 · 关 | 避免返回雷同素材 |
@@ -188,8 +188,8 @@ description: B-roll 检索铺轨编排手册——成片管线里第一个铺的
188
188
  > ⚠️ **`is_copyright` = 能不能商用,不是「有没有被版权保护」**
189
189
  >
190
190
  > ```
191
- > is_copyright: true → 可商用(自有 ∪ 已授权) ← 挑 BGM 就挑这种
192
- > is_copyright: false → 不可商用 ← 别碰
191
+ > is_copyright: true → 可商用(自有 ∪ 已授权)
192
+ > is_copyright: false → 不可商用(**不是**「没版权、可随便用」)
193
193
  > ```
194
194
  >
195
195
  > **`false` MUST NOT 读作「无版权、可以随便用」——它恰恰相反。** 决定性反证:概念素材(他人版权物)
@@ -197,16 +197,23 @@ description: B-roll 检索铺轨编排手册——成片管线里第一个铺的
197
197
  >
198
198
  > **真机事故(2026-09-02,这条警示的由来)**:AI 执行方连续两轮**特意去挑 `is_copyright:false`**,
199
199
  > 以为那是「没版权、随便用」的安全选项,结果把**不可商用**素材铺进了 5 个工程;唯一安全的
200
- > `true` 反倒被主动避开。
200
+ > `true` 反倒被主动避开。**病灶是把极性读反了,不是「用了 `false` 的素材」**——认准这一条,
201
+ > 下面那条缺省口径才不会被误读成「事故的教训被推翻了」。
201
202
  >
202
- > **所以选 BGM(以及任何要进成片的整条素材)MUST 按可商用口径判**:
203
+ > **选材口径按档位分流**(⟲ 2026-09-06 主理人拍板):
203
204
  >
204
- > - **MUST 只从 `is_copyright: true` 的候选里挑**;`false` 的一律不推荐给用户、不落轨——
205
- > 除非用户在对话里明确说了这条片子只自用/不发布,且你把「这条不可商用」原话告诉他;
206
- > - 嫌手动挑麻烦就让服务端筛:成员口加 **`--commercial-only`**(传 `copyright_scope=commercial`),
207
- > 出参逐条 `is_copyright:true`;
205
+ > - **internal 矩阵成员口:缺省搜全库,MUST NOT 自设一道命令面并不存在的闸。** 命令面缺省本就是
206
+ > `copyright_scope=all`,你 **MUST NOT 默认写上 `--commercial-only`**,也 MUST NOT 以「保险起见」
207
+ > 为由把 `is_copyright:false` 的候选剔除或降权——「免费搜**全库**(含非商用/概念素材)」
208
+ > 正是成员身份买到的东西,在最后一米替他扣下等于把已售出的权益又收回去;
209
+ > - **对价是如实标注**:推荐候选时 **MUST 逐条带上 `copyright_label`**(不可商用的就照直写「不可商用」)。
210
+ > 放开池子 MUST NOT 连版权状态一起放掉。但这条 **MUST NOT 升级成追问或阻塞**——
211
+ > 用户没提商用需求就不必反问,标注完照常往下走;
212
+ > - **用户特意说明才收紧**:他明说「只要可商用的」/「这条要商用发布」/「客户商单交付」时,
213
+ > MUST 加 **`--commercial-only`**(传 `copyright_scope=commercial`,出参逐条 `is_copyright:true`),
214
+ > 或只从 `copyright_label:"可商用"` 的候选里挑。**「用户没说」MUST NOT 被当成「用户要求收紧」**;
208
215
  > - **公开口(external 档)没有这个字段**——那不是「不可商用」,而是「服务端已经在源头只放可商用素材」,
209
- > 缺席即无需判;
216
+ > 缺席即无需判,本条分流对它无影响;
210
217
  > - `--json` 出参里 CLI 会**逐条派生一个人话标签 `copyright_label`**(`"可商用"` / `"不可商用"`),
211
218
  > 与 `is_copyright` 恒同向、缺席同缺席。**判读以这两个键中的任一为准,MUST NOT 靠字段名去猜。**
212
219
 
@@ -213,11 +213,14 @@ gtrk transcript "<长片>" --lang <开工⑦所答语种> --json # 影视/游
213
213
  # MUST NOT 以「粤语传错」为由重跑(la 进 ASR 缓存键,换码必 cache miss、白扣一次时长)。详见 §二 ⑦ 注
214
214
 
215
215
  # ②③ 写稿(甲档出处共生 / 乙路对照表对齐)→ 检查点①拍板(§六)
216
- gtrk matrix material "<情绪 题材 检索词>" --scope audio --top-k 5 --commercial-only --json # BGM 候选附试听
217
- # ⚠️ 版权判断不在零件手里,在你手里:零件缺省 copyright_scope=all(**不筛**)。
218
- # is_copyright 的权威语义是 1/true=可商用、0/false=**不可商用**——反直觉,MUST NOT 把 false
219
- # 读成「无版权、可随便用」。带 --commercial-only 就把不可商用的挡在候选之外;不带就 MUST
220
- # 逐条按 copyright_label(派生位,中文标签)自己判,别默认候选都能用。
216
+ gtrk matrix material "<情绪 题材 检索词>" --scope audio --top-k 5 --json # BGM 候选附试听
217
+ # ⚠️ is_copyright 的权威语义是 1/true=可商用、0/false=**不可商用**——反直觉,MUST NOT 把 false
218
+ # 读成「无版权、可随便用」(真机上正是这一步读反,把不可商用素材铺进了 5 个工程)。
219
+ # ⟲ 260906 拍板 · 选材口径:矩阵成员档**缺省搜全库**,MUST NOT 默认加 --commercial-only、
220
+ # 也 MUST NOT 以「保险起见」为由剔掉 is_copyright:false 的候选(全库正是成员身份买到的东西)。
221
+ # 对价是**如实标注**:推荐时 MUST 逐条带上 copyright_label(派生位,中文标签),不可商用的照直说,
222
+ # 但 MUST NOT 升级成追问或阻塞。用户**特意说明**只要可商用 / 要商用发布 / 客户商单时才加
223
+ # --commercial-only 收紧;「用户没说」MUST NOT 当成「用户要求收紧」。
221
224
 
222
225
  # ④ 声音基座(按开工④路由)
223
226
  gtrk tool audio_tts_clone --text-file <稿段.txt> --speaker <voice_id> --json # TTS 路
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: gtrk-oralcut
3
- description: 智能口播剪辑闭环——把一条口播原视频(毛片)通过同合云云端剪掉废话/重复/长停顿,产出客户端(gtrk)+剪映+PR 三方工程文件并自动打开产物目录。当用户想「剪口播/剪个视频/智能剪辑/去掉废话和停顿/把这条口播剪一版/出剪映草稿/生成剪辑工程/oral cut」时使用本 skill。凡涉及把口播毛片做成可二次精修的剪辑工程,优先用本 skill 驱动 gtrk CLI,不要手搓 ffmpeg。
3
+ description: 智能口播剪辑闭环——开剪前主动检查同目录独立口播音频,有则先对轨换声,再通过同合云剪掉废话/重复/长停顿,产出客户端(gtrk)+剪映+PR 三方工程。当用户想「剪口播/剪个视频/智能剪辑/去掉废话和停顿/把这条口播剪一版/出剪映草稿/生成剪辑工程/oral cut」时使用本 skill。凡涉及把口播毛片做成可二次精修的剪辑工程,优先用本 skill 驱动 gtrk CLI,不要手搓 ffmpeg。
4
4
  ---
5
5
 
6
6
  # 口播剪辑(gtrk-oralcut)
@@ -9,7 +9,7 @@ description: 智能口播剪辑闭环——把一条口播原视频(毛片)通
9
9
  > 那张图纸把本命令接进全链:外录音轨对轨换声 → 多段拼接 → 本命令粗剪 → 拆分/B-roll/字卡/BGM/字幕 → 客户端出片。
10
10
  > 本 skill 是**命令手册**(怎么跑 oralcut),那张是**场景图纸**(怎么串全链),配合用不冲突。
11
11
 
12
- 把用户的口播毛片,用 `gtrk` CLI 跑通「**本地抽音频/720p → 只传抽出物(毛片永不上传)** → 云端智能口播剪辑 → 拉回三方工程文件 →(可选)**本地 ffmpeg 渲染成片**」,再把产物目录 + 三端打开方式回给用户。**CLI 是手、你是脑。**
12
+ 把用户的口播毛片,用 `gtrk` CLI 跑通「**检查独立口播音频 →(如有)先对轨换声 → 本地抽音频/720p → 只传抽出物(毛片永不上传)** → 云端智能口播剪辑 → 拉回三方工程文件 →(可选)**本地 ffmpeg 渲染成片**」,再把产物目录 + 三端打开方式回给用户。**CLI 是手、你是脑。**
13
13
 
14
14
  > **本 skill 已含你需要的全部信息**(参数、取值、执行、读报告、排错),照它做即可,不用也无法去查外部文档。(源仓库另有一份更全的工具无关版 `AGENT.md`,那是给在仓库里开发的人看的,与你无关。)
15
15
 
@@ -29,9 +29,33 @@ oralcut 现在**本地预处理**(探原片几何 + 抽 16k 单声道 mp3 /
29
29
  - **未找到** → 你来装(CLI 不自下不自分发):**先确认确实没有**(doctor 已查过 `~/.gitruck/ffmpeg` 与系统 PATH),缺了再拉 `ffmpeg` + `ffprobe` 两个可执行文件放到 `~/.gitruck/ffmpeg/`(Windows 即 `ffmpeg.exe`/`ffprobe.exe`)。
30
30
  - **国内用户优先国内加速站点**:GitHub 官方静态构建(BtbN / gyan.dev)直连慢,走 GitHub 加速代理(pass-through)或同合云自建镜像拉,下载后**核对 sha256**。
31
31
  - 装好复跑 `gtrk doctor` 确认转绿。
32
- - 用户自己装过 ffmpeg 并配了 PATH → doctor 会从系统探到直接用;**绝不去改用户的环境变量/PATH**(CLI 一律绝对路径调用)。若装在别处,用 `--ffmpeg-path <目录>` 指给它。
33
-
34
- ## 把用户的话变成一次调用
32
+ - 用户自己装过 ffmpeg 并配了 PATH → doctor 会从系统探到直接用;**绝不去改用户的环境变量/PATH**(CLI 一律绝对路径调用)。若装在别处,用 `--ffmpeg-path <目录>` 指给它。
33
+
34
+ ## 开剪前硬闸:主动发现独立口播音频,先对轨再 oralcut
35
+
36
+ **无论用户有没有主动提到外录音频,执行 agent 都必须在第一次 `gtrk oralcut` 之前检查毛片所在目录。** 用户给的是目录时,先列出其中的视频与音频;用户给的是单个视频时,也要查看它的同目录。识别 `wav / mp3 / m4a / flac` 等独立音频,并按文件名、同一拍摄批次与数量关系判断是否是该视频的口播外录:
37
+
38
+ - 唯一视频 + 唯一口播音频,或名称可明显配对(如 `03思考_视频_V01.mp4` ↔ `03思考_音频_V01.flac`)→ **直接视为一组,不用反问用户**。
39
+ - 多个视频/音频且配对有歧义 → 在任何上传或 oralcut 计费前,让用户确认映射关系。
40
+ - 文件名/上下文明确是 BGM、配乐、伴奏或环境声 → 不是口播外录,不走对轨。
41
+
42
+ 发现可配对的独立口播音频后,固定顺序:
43
+
44
+ ```bash
45
+ gtrk audio align "<毛片>" "<外录>" --json
46
+ gtrk oralcut "<align 结果 JSON 的 output,即 *_extaudio.mp4>" [原有参数…] --json
47
+ ```
48
+
49
+ - **MUST 先 align、再 oralcut。MUST NOT 先剪机内声,事后再把外录硬塞进工程或另剪一版补救。** align 产物已经把外录换进视频,后续 oralcut 的每个画面切点会天然同步作用到外录声。
50
+ - align 高置信自动完成时,后续输入必须切换为结果 JSON 的 `output`,不要继续拿原毛片跑 oralcut。
51
+ - align 低置信时会产 `*_align.gtrk`:停下让用户在客户端拖齐并保存,再跑 `gtrk audio align --resume "<对齐工程>" --json`;拿到换轨产物后才允许继续 oralcut。
52
+ - align 失败时如实停下排错;除非用户明确同意,**不得静默回退到机内声开剪**。
53
+ - 换轨产物是后续工程依赖素材,必须提醒用户留存;原毛片保留不动,作为内录保底。
54
+ - **文稿路径要在换轨前记住**:若原毛片靠同名 `.txt` 自动探稿,换轨后的 `_extaudio.mp4` 通常匹配不到原同名文稿,后续必须显式传 `--script "<原文稿路径>"`。
55
+
56
+ 没有发现独立口播音频,才直接对原毛片执行 oralcut。
57
+
58
+ ## 把用户的话变成一次调用
35
59
 
36
60
  > **每次调用都必须带 `--json`**(成功时 stdout 只出一行结果 JSON、人读日志转 stderr),你才能解析结果 + 读报告。不是可选项。
37
61
 
@@ -188,7 +188,7 @@ description: 旅拍解说一站式成片图纸——输入一个旅拍素材文
188
188
  - 用户有本地音乐 → 询问是否 `gtrk tool audio_separation` 伴奏分离后取伴奏(缺省建议分离——TTS 配音不与歌曲人声打架);
189
189
  - 用户未提供 → **搜同和素材矩阵**:`gtrk matrix material "<按稿件情绪+地理文化写的检索词>" --scope audio --top-k 5 --diversity --json`
190
190
  (检索词口径见公约 §三′:**内容驱动、禁通用词、至少含一个只有这一片才成立的词**;候选
191
- MUST 给用户挑不许默认 top1;跨片避让由 `audio lay` 记账 + 检索缺省 `--exclude-recent` 自动完成)(按成片时长挑可加 `--min-duration <秒>`)。身份路由(矩阵成员搜全库 / 非成员搜公开库)与计费口径由零件持有;**版权判断不在零件手里,在你手里**——零件缺省 `copyright_scope=all`,**不筛**。挑 BGM MUST 按**可商用**口径判:出参 `is_copyright` 的权威语义是 `1/true` = **可商用**、`0/false` = **不可商用**(反直觉:`is_copyright:false` MUST NOT 读成「无版权、可随便用」,它恰恰是不可用的那一档;概念素材固定 `is_copyright=0` 即此意)。派生位 `copyright_label` 直接给了中文标签,照它转述即可。省事路是检索时就带 `--commercial-only`,把不可商用的挡在候选之外;出参 `results[]` 直接用,`upsell` 是独立顶层字段(非成员且没搜够时才有,原样转述给用户即可,**不要**把它当候选)。
191
+ MUST 给用户挑不许默认 top1;跨片避让由 `audio lay` 记账 + 检索缺省 `--exclude-recent` 自动完成)(按成片时长挑可加 `--min-duration <秒>`)。身份路由(矩阵成员搜全库 / 非成员搜公开库)与计费口径由零件持有;**版权状态要你如实转述**——出参 `is_copyright` 的权威语义是 `1/true` = **可商用**、`0/false` = **不可商用**(反直觉:`is_copyright:false` MUST NOT 读成「无版权、可随便用」,它恰恰是不可用的那一档;概念素材固定 `is_copyright=0` 即此意)。**⟲ 260906 拍板 · 选材口径**:零件缺省 `copyright_scope=all`,矩阵成员档就是**搜全库**——你 MUST NOT 默认加 `--commercial-only`,也 MUST NOT 以「保险起见」为由剔掉 `is_copyright:false` 的候选(全库正是成员身份买到的东西)。对价是**如实标注**:推荐时 MUST 逐条带上派生位 `copyright_label` 给的中文标签,不可商用的照直说,但 MUST NOT 升级成追问或阻塞。用户**特意说明**只要可商用 / 要商用发布 / 客户商单时,才加 `--commercial-only` 把不可商用的挡在候选之外;「用户没说」MUST NOT 当成「用户要求收紧」。出参 `results[]` 直接用,`upsell` 是独立顶层字段(非成员且没搜够时才有,原样转述给用户即可,**不要**把它当候选)。
192
192
  推荐 3-5 首、**每首附试听链接**(结果的 `download_url`);`audio_type:"pure"` 纯音乐直接用,`"song"` 直接取 `accompaniment_url` 现成伴奏(零处理成本,两档都有);注意曲长 vs 成片时长(`duration` 字段):`audio lay` **缺省循环叠满至工程末尾、尾段裁齐**(多 clip 首尾相接,命令会打 INFO 报段数),短曲只是**接缝多一些**、不会铺一半就静音(想少几道接缝,挑曲时加 `--min-duration <秒>`);要单次铺请显式传 `--no-loop`;
193
193
  - **试听链接义务(MUST)**:音色与 BGM 的推荐没有试听链接=未完成推荐。
194
194
  - **BGM 落地一步(MUST,别留悬空 · ⟲ 2026-09-02 补)**:检索出参只给**签名 URL**,而 §五 ⑩ 的 `gtrk audio lay --file` **只收本地路径**(传 URL 必炸)——拍板之后、上轨之前,agent MUST 把选中曲**落成本地文件**。口径照解说链正本 `gtrk-narration` §五 原文对齐:`audio_type:"song"` **MUST 取 `accompaniment_url` 伴奏版上轨**、**MUST NOT 用 `download_url` 原曲**(人声与配音打架,260827 美食批踩坑);`"pure"` 取 `download_url`。文件名按 **`bgm-<曲名>[-伴奏].<扩展名>`** 约定——跨片避让记账按**标题归一化键**匹配(剥 `bgm-` 前缀与 伴奏 / instrumental / off-vocal 后缀),命名不合规会让下一轮 `--exclude-recent` 避让失准。这一步不违反 §一 的下载禁令:那条禁令的射程只到二创源片 / 主体素材,平台授权素材的签名直链不在其内。
@@ -294,7 +294,7 @@ gtrk mg --project "<工程目录>" --json
294
294
  # 文件名 bgm-<曲名>[-伴奏].<扩展名>:跨片避让按**标题归一化键**记账(剥 bgm- 前缀与伴奏/instrumental/off-vocal 后缀),乱命名=下一轮 --exclude-recent 失准
295
295
  gtrk audio lay --project "<工程目录>" --file "<bgm>" --volume 0.1 --beat-align --json
296
296
 
297
- # ⑪ 字幕(样式/颜色 = 检查点①拍板值;用户没选用 default/雅黑;纯本地零计费、重跑幂等替换)
297
+ # ⑪ 字幕(样式/颜色 = 检查点①拍板值;用户没选用 default/雅黑;零计费——拆行走云端 subtitle_line_split 0 积分留痕,离线可加 --offline 用本地拆窗、重跑幂等替换)
298
298
  gtrk subtitle lay --project "<工程目录>" --style default --color 雅黑 --json
299
299
  ```
300
300
 
@@ -188,7 +188,7 @@ HKRR 四维(缺哪补哪):
188
188
  3. **旁白稿 + 标题**:正文全文贴给用户(不是给路径),标注每段旁白挂在哪个位置;标题候选列出;
189
189
  4. **画幅**:横屏 B 站/西瓜=1920x1080,竖屏抖音=1080x1920。**素材几何与画幅不一致时明确提示**;
190
190
  5. **TTS 音色**:拉 catalog(`GET {apiBase}/task/tts/voices`,免鉴权)挑 2–4 个候选,**每个附试听链接**(`audition_url`)。⚠️ 纪实类的旁白与现场同期声会交替出现,**音色应贴近用户本人音色或明显区分**(避免观众困惑「这是谁在说」)——如用户本人出镜说话,优先建议同性别接近音色;
191
- 6. **BGM 处置(双来源)**:用户自带 → 询问是否 `audio_separation` 取伴奏;未提供 → `gtrk matrix material "<按内容情绪写的检索词>" --scope audio --top-k 5 --diversity --json`,推荐 3–5 首**每首附试听链接**(`download_url`),`audio_type:"song"` 取 `accompaniment_url`。⚠️ 纪实类 BGM 要给同期声让路,音量口径同旅拍(-20dB);
191
+ 6. **BGM 处置(双来源)**:用户自带 → 询问是否 `audio_separation` 取伴奏;未提供 → `gtrk matrix material "<按内容情绪写的检索词>" --scope audio --top-k 5 --diversity --json`,推荐 3–5 首**每首附试听链接**(`download_url`),`audio_type:"song"` 取 `accompaniment_url`。⚠️ **版权状态如实转述**:`is_copyright` 的语义是 `1/true`=**可商用**、`0/false`=**不可商用**(反直觉,MUST NOT 把 `false` 读成「无版权、可随便用」);矩阵成员档**缺省搜全库**,MUST NOT 默认加 `--commercial-only`、也 MUST NOT 擅自剔掉 `false` 的候选,但推荐时 MUST 逐条带上派生位 `copyright_label` 的中文标签(不追问不阻塞)——用户特意说明只要可商用 / 要商用发布时才加 `--commercial-only` 收紧。⚠️ 纪实类 BGM 要给同期声让路,音量口径同旅拍(-20dB);
192
192
  7. **字幕样式**:7 样式 × 11 色;用户没选用 default+雅黑(不追问不阻塞)。
193
193
 
194
194
  **试听链接义务(MUST)**:音色与 BGM 的推荐没有试听链接=未完成推荐。
@@ -33,6 +33,10 @@ description: 通用配音视频创作图纸(配音链快速成片预设)—
33
33
  ```bash
34
34
  # ①② 写稿(三段式通用框架层 + 文风铁律自校验)→ 检查点①拍板
35
35
  gtrk matrix material "<情绪 调性 检索词>" --scope audio --top-k 5 --json # BGM 候选附试听
36
+ # ⚠️ is_copyright:1/true=可商用、0/false=**不可商用**(反直觉,MUST NOT 读成「无版权、可随便用」)。
37
+ # ⟲ 260906 拍板:矩阵成员档**缺省搜全库**,MUST NOT 默认加 --commercial-only、也 MUST NOT 擅自剔掉
38
+ # false 的候选;对价是推荐时 MUST 逐条带上派生位 copyright_label 的中文标签(不追问不阻塞)。
39
+ # 用户**特意说明**只要可商用 / 要商用发布 / 客户商单时,才加 --commercial-only 收紧。
36
40
 
37
41
  # ③ 配音 → 建工程
38
42
  gtrk tool audio_tts_clone --text-file <稿.txt> --speaker <voice_id> --json
@@ -72,5 +76,5 @@ gtrk subtitle lay --project "<工程>" --style <样式> --color <色> --json
72
76
  ## 六、计费与排错
73
77
 
74
78
  - 计费大头 = TTS 按字数(跑前实时价格原样转述);平台库检索/理解按矩阵成员口径,运行时打印为准。
75
- - 素材夹索引、拆分、铺排、字幕纯本地零计费。
79
+ - 素材夹索引、拆分、铺排纯本地零计费;字幕上轨零计费(拆行走云端 subtitle_line_split,0 积分留痕;离线可加 `--offline`)。
76
80
  - 排错回各零件 skill(`gtrk-matrix` / `gtrk-mg` / `gtrk-transcript` / `gtrk project --help`),本图纸不搬;文风与写稿症状按 `gtrk-travel-recap` §三 通用框架层自校验清单处理。