@remixmate/cli 0.9.52 → 0.9.53

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.9.52",
4
- "generatedAt": "2026-09-27T14:43:11.552Z",
3
+ "version": "0.9.53",
4
+ "generatedAt": "2026-09-28T02:19:31.947Z",
5
5
  "skills": [
6
6
  {
7
7
  "id": "doc-parse",
@@ -944,7 +944,7 @@
944
944
  },
945
945
  "template_id": {
946
946
  "type": "string",
947
- "description": "Return this template's full authoring contract instead of the summary list: llmHint in full, customPayloadSchema (every template-specific field incl. the legal slideId values), slotMapping, compositions, variants. Multi-layout templates return slideSchemas as a MENU (per layout: description, field names, card-list key / item fields / maxItems, and whether narration must be split into items) — enough to pick layouts and draft the script. Read it once per conversation; it does not change between turns."
947
+ "description": "Return this template's full authoring contract instead of the summary list: llmHint in full, customPayloadSchema (every template-specific field incl. the legal slideId values), slotMapping, compositions, variants. Multi-layout templates return slideSchemas as a MENU (per layout: description, field names, card-list key / item fields / maxItems, and whether narration must be split into items) — enough to pick layouts and draft the script. Tool results do not carry over between chat turns: read the menu in the turn that drafts the script; the turn that writes templateData only needs slide_id."
948
948
  },
949
949
  "slide_id": {
950
950
  "type": "array",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remixmate/cli",
3
- "version": "0.9.52",
3
+ "version": "0.9.53",
4
4
  "description": "AI media generation skills for Claude Code / Codex — 12 skills covering image, video, voice, digital human, web screenshot, web recording, script, template registry, rendering, Jianying export, and video deconstruction.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,7 +52,7 @@ The intent is to avoid the failure mode where "the generic DSL looks compatible
52
52
 
53
53
  ### Mandatory steps
54
54
 
55
- 1. Read the requested template's full definition from the registry — `template_registry` with `template_id=<the id>` emits that one template's contract. This is the shipping source of truth. Read it **once per conversation**: it is still in context on the next turn, so do not re-fetch it after the user confirms the script. Do **not** ask for every template's full definition (`list_templates=true` + `full=true`): the whole registry is far more JSON than one tool result can carry, and the call fails instead of returning the contract.
55
+ 1. Read the requested template's full definition from the registry — `template_registry` with `template_id=<the id>` emits that one template's contract. This is the shipping source of truth. Never fetch it twice in one turn. Tool results do **not** carry over between chat turns (each turn only sees the chat text), so the turn that writes `dsl_json` after the user confirms needs only step 3b's `slide_id` call, not the menu again. Do **not** ask for every template's full definition (`list_templates=true` + `full=true`): the whole registry is far more JSON than one tool result can carry, and the call fails instead of returning the contract.
56
56
  2. Read its `llmHint` end to end — that is where the template states how its on-screen text and its layouts must be authored.
57
57
  3. Read its `customPayloadSchema`: every template-specific field lives there, including the enum of legal `customPayload.slideId` values for multi-layout templates.
58
58
  3b. If the template ships `slideSchemas` (multi-layout templates do), step 1 returns them as a **menu** — per layout: description, field names, card-list key / item fields / `maxItems`, and a `narration` note. Pick layouts and draft the script from the menu. Then, before writing `templateData`, call `template_registry` once with `template_id` and `slide_id=[every chosen layout]`, and **author `templateData` straight from those `slideSchemas[slideId]`** — it is the only place the per-layout field names exist. Do not infer them: the same concept is named `name` in one layout, `label` in another and `era` in a third, and a wrong name is dropped in silence (see below).
@@ -418,7 +418,7 @@ def estimate_narration_text(
418
418
  session wrote 45–58 chars into 8s scenes and shipped 117s for a 75s target.
419
419
  """
420
420
  fmt = {
421
- "label": label, "topic": topic, "duration": duration,
421
+ "label": label, "topic": _clip(topic, _PLACEHOLDER_TOPIC_CHARS), "duration": duration,
422
422
  "budget": _budget_suffix(duration, language, speed, tail_pad_sec),
423
423
  }
424
424
  if purpose == "opening":
@@ -728,6 +728,32 @@ def _placeholder_text_layers(rules: dict) -> list[dict]:
728
728
  return layers
729
729
 
730
730
 
731
+ # 视频标题 / 占位旁白里引用 topic 的长度上限。topic 是给模型写脚本用的内容提要,
732
+ # 线上常见 200+ 字(2026-09-27 一条会话 250 字),原样拿去当标题或塞进每场占位旁白
733
+ # 都不合适。
734
+ _TITLE_MAX_CHARS = 60
735
+ _PLACEHOLDER_TOPIC_CHARS = 30
736
+
737
+
738
+ def _clip(text: str, limit: int) -> str:
739
+ text = " ".join((text or "").split())
740
+ return text if len(text) <= limit else text[: limit - 1].rstrip() + "…"
741
+
742
+
743
+ def _video_title(headline: str | None, topic: str) -> str:
744
+ """meta.title:成片的标题。
745
+
746
+ 它会成为上传点播的标题(render_video 的 upload_title_hint)和脚本产物列表的
747
+ 标题(ab-api artifact_narration.dslTitle)。以前直接等于 topic —— 模型传进来的
748
+ 是一段内容提要,于是作品标题成了「讲解 AI Agent 的核心底层逻辑「Agent Loop」:
749
+ 传统大模型的单次响应 vs…」(render_video 只能截到 128 字兜底)。用户给了
750
+ headline 就用 headline(折掉换行),没给才退回截短的 topic;完整 topic 仍在
751
+ meta.topic。
752
+ """
753
+ # 画面文案里的 **强调** 标记对标题没有意义,去掉。
754
+ return _clip((headline or "").replace("**", ""), _TITLE_MAX_CHARS) or _clip(topic, _TITLE_MAX_CHARS)
755
+
756
+
731
757
  def _normalize_linebreaks(value: str | None) -> str:
732
758
  """把画面文字里的**字面量** ``\\n`` 折成真换行。
733
759
 
@@ -1168,7 +1194,7 @@ def _build_carousel_caption_dsl(
1168
1194
 
1169
1195
  # ── Narration skeleton ────────────────────────────────────────────────
1170
1196
  narration_text = _localize_label(output_language, "narration_opening",
1171
- label="", topic=topic, duration=duration)
1197
+ label="", topic=_clip(topic, _PLACEHOLDER_TOPIC_CHARS), duration=duration)
1172
1198
 
1173
1199
  # ── Effective scene duration ──────────────────────────────────────────
1174
1200
  # fit-caption 策略:时长由打字机自然节奏决定,而不是盲取调用方传入的 targetDuration。
@@ -1220,7 +1246,7 @@ def _build_carousel_caption_dsl(
1220
1246
  dsl = {
1221
1247
  "version": "v1alpha1",
1222
1248
  "meta": {
1223
- "title": topic,
1249
+ "title": _video_title(headline, topic),
1224
1250
  "topic": topic,
1225
1251
  "headline": effective_headline,
1226
1252
  "subheadline": effective_subheadline,
@@ -1879,7 +1905,7 @@ def build_dsl(
1879
1905
  dsl = {
1880
1906
  "version": "v1alpha1",
1881
1907
  "meta": {
1882
- "title": topic,
1908
+ "title": _video_title(effective_headline if headline else None, topic),
1883
1909
  "topic": topic,
1884
1910
  "headline": effective_headline,
1885
1911
  "subheadline": effective_subheadline,
@@ -61,7 +61,7 @@ the registry is loaded **only** from the ab-api HTTP endpoint — explicit `VIDE
61
61
  > The Python binding logic that maps DSL → TemplateBinding lives in `scripts/match_template.py` but is **not exposed as a CLI** — it is only consumed as a Python library by `render-video`'s `render_video.py` via `import match_template`.
62
62
 
63
63
  1. **List available templates**: run `remixmate template-registry --list-templates` to view the templates in the registry along with their supported aspect ratios / style tags, and decide which `templateId` to pick.
64
- 2. **Read that template's contract**: run `remixmate template-registry --template-id <id>` for the full definition (llmHint, `customPayloadSchema` with the legal `slideId` values, `slotMapping`, `variants`). The list view only carries a 200-char llmHint preview, which is not enough to author against. Read it **once per conversation** — it does not change between turns.
64
+ 2. **Read that template's contract**: run `remixmate template-registry --template-id <id>` for the full definition (llmHint, `customPayloadSchema` with the legal `slideId` values, `slotMapping`, `variants`). The list view only carries a 200-char llmHint preview, which is not enough to author against. Never fetch it twice in one turn. In the chat agent, tool results do **not** carry over between turns, so fetch per turn only what that turn needs: the menu while drafting the script, `--slide-id` while writing `templateData`.
65
65
  2b. **Multi-layout templates: expand only the layouts you use.** For templates that ship `slideSchemas`, step 2 returns a per-layout **menu** (description, field names, card-list key / item fields / `maxItems`, and a `narration` note on layouts that must split narration into `items`). Choose layouts and draft the script from the menu; then, right before writing `templateData`, call once more with `--slide-id <id>` for every chosen layout (repeatable) to get their exact field schemas.
66
66
  3. **Write the DSL**: when generating the Video DSL, put the chosen `templateId` into `meta.templateId` (the canonical location). Use `meta.templateVariant` / `renderHints.templateVariant` to explicitly select a variant. The legacy `renderHints.templatePreference[0]` is still tolerated by `match_template.py` and `dsl_validator._pick_template_id` during transition, but new authors should write `meta.templateId`.
67
67
  4. **Produce the TemplateBinding**: there is no standalone CLI for DSL → TemplateBinding; `prepare_video_assets` calls `match_template.build_binding(template, dsl)` inline during the asset-resolution pipeline and embeds the binding into the RenderPlan it hands to the renderer — no separate `*.binding.json` file is written.
@@ -256,7 +256,7 @@ def _apply_slide_view(tpl: dict, slide_ids: list[str] | None) -> dict:
256
256
  "MENU ONLY — per-layout summary, not the full schema. Use it to choose layouts and draft the "
257
257
  "script. Before writing templateData, call template_registry again with this template_id and "
258
258
  "slide_id=<each chosen layout> (repeatable) to get their exact field schemas. "
259
- "If you already did that earlier in this conversation, do not call it again."
259
+ "Tool results do not carry over between chat turns: fetch the menu while drafting the script, slide_id while writing templateData."
260
260
  ),
261
261
  }
262
262
  for sid in ids:
@@ -12,7 +12,7 @@
12
12
  "type": "object",
13
13
  "properties": {
14
14
  "list_templates": { "type": "boolean", "description": "List available templates as summaries — templateId / name / description / aspect ratios / language / status / styleTags / variantIds / key capabilities / a truncated llmHint (default behavior; also implied when other flags are passed)." },
15
- "template_id": { "type": "string", "description": "Return this template's full authoring contract instead of the summary list: llmHint in full, customPayloadSchema (every template-specific field incl. the legal slideId values), slotMapping, compositions, variants. Multi-layout templates return slideSchemas as a MENU (per layout: description, field names, card-list key / item fields / maxItems, and whether narration must be split into items) — enough to pick layouts and draft the script. Read it once per conversation; it does not change between turns." },
15
+ "template_id": { "type": "string", "description": "Return this template's full authoring contract instead of the summary list: llmHint in full, customPayloadSchema (every template-specific field incl. the legal slideId values), slotMapping, compositions, variants. Multi-layout templates return slideSchemas as a MENU (per layout: description, field names, card-list key / item fields / maxItems, and whether narration must be split into items) — enough to pick layouts and draft the script. Tool results do not carry over between chat turns: read the menu in the turn that drafts the script; the turn that writes templateData only needs slide_id." },
16
16
  "slide_id": { "type": "array", "items": { "type": "string" }, "description": "With template_id: return the FULL templateData schema for these layouts (the slideIds you chose from the menu). Call this once, right before writing templateData, with every layout the script uses. 'all' returns every layout (large — avoid)." },
17
17
  "full": { "type": "boolean", "description": "List mode only: emit full definitions for every listed template instead of summaries. Rejected when the result would be too large for one tool result — prefer template_id, or narrow with the filter_* parameters." },
18
18
  "filter_tag": { "type": "string", "description": "Show only templates whose styleTags contain (or are contained in) this string. Case-insensitive. E.g. 'tech' / '科普' / 'walkthrough'." },