@remixmate/cli 0.1.0 → 0.1.2

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.
Files changed (126) hide show
  1. package/README.md +100 -83
  2. package/README.zh-CN.md +183 -0
  3. package/dist/handlers/gen-digital-human.d.ts +12 -0
  4. package/dist/handlers/gen-digital-human.js +157 -0
  5. package/dist/handlers/gen-image.js +5 -41
  6. package/dist/handlers/gen-video.d.ts +18 -0
  7. package/dist/handlers/gen-video.js +153 -0
  8. package/dist/handlers/gen-voice.js +2 -12
  9. package/dist/handlers/index.js +4 -2
  10. package/dist/handlers/shared.d.ts +26 -0
  11. package/dist/handlers/shared.js +66 -0
  12. package/dist/http.d.ts +4 -2
  13. package/dist/http.js +5 -3
  14. package/dist/manifest.json +38 -24
  15. package/dist/registry.d.ts +5 -16
  16. package/dist/registry.js +11 -17
  17. package/dist/skill-schema.d.ts +65 -0
  18. package/dist/skill-schema.js +53 -0
  19. package/package.json +5 -4
  20. package/skills/export-jianying/scripts/gen_jianying_draft.py +4 -4
  21. package/skills/export-jianying/skill.json +1 -0
  22. package/skills/gen-digital-human/SKILL.md +23 -46
  23. package/skills/gen-digital-human/skill.json +4 -2
  24. package/skills/gen-digital-human/version.json +1 -1
  25. package/skills/gen-image/SKILL.md +2 -2
  26. package/skills/gen-image/skill.json +3 -1
  27. package/skills/gen-image/version.json +1 -1
  28. package/skills/gen-script/SKILL.md +13 -13
  29. package/skills/gen-script/scripts/gen_script.py +50 -12
  30. package/skills/gen-script/skill.json +3 -1
  31. package/skills/gen-video/SKILL.md +22 -33
  32. package/skills/gen-video/skill.json +4 -2
  33. package/skills/gen-video/version.json +1 -1
  34. package/skills/gen-voice/SKILL.md +1 -1
  35. package/skills/gen-voice/skill.json +1 -0
  36. package/skills/gen-voice/version.json +1 -1
  37. package/skills/prepare-video-assets/SKILL.md +2 -2
  38. package/skills/prepare-video-assets/skill.json +2 -1
  39. package/skills/render-video/scripts/render_video.py +8 -8
  40. package/skills/render-video/skill.json +1 -0
  41. package/skills/{template-bind → template-registry}/README.md +4 -4
  42. package/skills/{template-bind → template-registry}/SKILL.md +14 -12
  43. package/skills/template-registry/scripts/list_templates.py +214 -0
  44. package/skills/{template-bind → template-registry}/scripts/match_template.py +4 -3
  45. package/skills/{template-bind → template-registry}/scripts/registry_loader.py +1 -1
  46. package/skills/{template-bind → template-registry}/scripts/template_paths.py +7 -7
  47. package/skills/{template-bind → template-registry}/skill.json +8 -5
  48. package/skills/template-registry/version.json +7 -0
  49. package/skills/{template-bind → template-registry}/video_dsl/README.md +2 -2
  50. package/skills/{template-bind → template-registry}/video_dsl/runtime/__init__.py +1 -1
  51. package/skills/video-parser/SKILL.md +47 -160
  52. package/skills/video-parser/skill.json +4 -4
  53. package/skills/video-parser/version.json +1 -1
  54. package/skills/web-capture/SKILL.md +407 -116
  55. package/skills/web-capture/scripts/_media_screenshot/__init__.py +21 -0
  56. package/skills/web-capture/scripts/_media_screenshot/bootstrap.py +34 -0
  57. package/skills/web-capture/scripts/_media_screenshot/browser.py +86 -0
  58. package/skills/web-capture/scripts/_media_screenshot/cli_args.py +111 -0
  59. package/skills/web-capture/scripts/_media_screenshot/js/arrow.js +86 -0
  60. package/skills/web-capture/scripts/_media_screenshot/js/caption.js +27 -0
  61. package/skills/web-capture/scripts/_media_screenshot/js/clear_overlay.js +5 -0
  62. package/skills/web-capture/scripts/_media_screenshot/js/cursor_bootstrap.js +26 -0
  63. package/skills/web-capture/scripts/_media_screenshot/js/cursor_hide.js +6 -0
  64. package/skills/web-capture/scripts/_media_screenshot/js/cursor_move.js +12 -0
  65. package/skills/web-capture/scripts/_media_screenshot/js/cursor_ripple.js +25 -0
  66. package/skills/web-capture/scripts/_media_screenshot/js/element_zoom.js +72 -0
  67. package/skills/web-capture/scripts/_media_screenshot/js/element_zoom_restore.js +21 -0
  68. package/skills/web-capture/scripts/_media_screenshot/js/fade_out_overlays.js +10 -0
  69. package/skills/web-capture/scripts/_media_screenshot/js/focus.js +38 -0
  70. package/skills/web-capture/scripts/_media_screenshot/js/highlight.js +97 -0
  71. package/skills/web-capture/scripts/_media_screenshot/js/overlay_bootstrap.js +19 -0
  72. package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_in.js +54 -0
  73. package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_out.js +11 -0
  74. package/skills/web-capture/scripts/_media_screenshot/js/page_zoom_restore.js +13 -0
  75. package/skills/web-capture/scripts/_media_screenshot/js/redact.js +33 -0
  76. package/skills/web-capture/scripts/_media_screenshot/js/scroll.js +26 -0
  77. package/skills/web-capture/scripts/_media_screenshot/js/scroll_through.js +18 -0
  78. package/skills/web-capture/scripts/_media_screenshot/js/sequence.js +76 -0
  79. package/skills/web-capture/scripts/_media_screenshot/js/settled_check.js +15 -0
  80. package/skills/web-capture/scripts/_media_screenshot/js/title_card.js +71 -0
  81. package/skills/web-capture/scripts/_media_screenshot/js_loader.py +14 -0
  82. package/skills/web-capture/scripts/_media_screenshot/overlay.py +17 -0
  83. package/skills/web-capture/scripts/_media_screenshot/recording.py +100 -0
  84. package/skills/web-capture/scripts/_media_screenshot/scenes/__init__.py +39 -0
  85. package/skills/web-capture/scripts/_media_screenshot/scenes/arrow.py +50 -0
  86. package/skills/web-capture/scripts/_media_screenshot/scenes/caption.py +20 -0
  87. package/skills/web-capture/scripts/_media_screenshot/scenes/click.py +35 -0
  88. package/skills/web-capture/scripts/_media_screenshot/scenes/code_lines.py +54 -0
  89. package/skills/web-capture/scripts/_media_screenshot/scenes/focus.py +24 -0
  90. package/skills/web-capture/scripts/_media_screenshot/scenes/highlight.py +54 -0
  91. package/skills/web-capture/scripts/_media_screenshot/scenes/hover.py +28 -0
  92. package/skills/web-capture/scripts/_media_screenshot/scenes/redact.py +32 -0
  93. package/skills/web-capture/scripts/_media_screenshot/scenes/scroll.py +32 -0
  94. package/skills/web-capture/scripts/_media_screenshot/scenes/sequence.py +43 -0
  95. package/skills/web-capture/scripts/_media_screenshot/scenes/title_card.py +20 -0
  96. package/skills/web-capture/scripts/_media_screenshot/scenes/type.py +42 -0
  97. package/skills/web-capture/scripts/_media_screenshot/scenes/wait.py +9 -0
  98. package/skills/web-capture/scripts/_media_screenshot/scenes/zoom.py +72 -0
  99. package/skills/web-capture/scripts/_media_screenshot/screenshot.py +166 -0
  100. package/skills/web-capture/scripts/_media_screenshot/storyboard.py +88 -0
  101. package/skills/web-capture/scripts/_media_screenshot/template.py +119 -0
  102. package/skills/web-capture/scripts/_media_screenshot/trim.py +82 -0
  103. package/skills/web-capture/scripts/record.py +186 -0
  104. package/skills/web-capture/scripts/screenshot.py +94 -0
  105. package/skills/web-capture/skill.json +15 -14
  106. package/skills/web-capture/templates/github-code-tour.json +59 -0
  107. package/skills/web-capture/templates/github-readme-tour.json +77 -0
  108. package/skills/web-capture/templates/github-repo-intro.json +74 -0
  109. package/skills/web-capture/version.json +2 -2
  110. package/dist/handlers/template-bind.d.ts +0 -15
  111. package/dist/handlers/template-bind.js +0 -149
  112. package/skills/gen-digital-human/scripts/gen_digital_human_video.py +0 -574
  113. package/skills/gen-video/scripts/gen_video.py +0 -591
  114. package/skills/template-bind/version.json +0 -7
  115. package/skills/web-capture/scripts/web_capture.py +0 -461
  116. /package/skills/{template-bind → template-registry}/scripts/check_contracts.py +0 -0
  117. /package/skills/{template-bind → template-registry}/scripts/render_job_client.py +0 -0
  118. /package/skills/{template-bind → template-registry}/scripts/sync_registry.py +0 -0
  119. /package/skills/{template-bind → template-registry}/video_dsl/runtime/dsl_validator.py +0 -0
  120. /package/skills/{template-bind → template-registry}/video_dsl/runtime/prompt_enhancer.py +0 -0
  121. /package/skills/{template-bind → template-registry}/video_dsl/runtime/template_binder.py +0 -0
  122. /package/skills/{template-bind → template-registry}/video_dsl/runtime/timeline_compiler.py +0 -0
  123. /package/skills/{template-bind → template-registry}/video_dsl/schema/render-plan-v1alpha1.json +0 -0
  124. /package/skills/{template-bind → template-registry}/video_dsl/schema/template-binding-v1alpha1.json +0 -0
  125. /package/skills/{template-bind → template-registry}/video_dsl/schema/template-definition-v1alpha1.json +0 -0
  126. /package/skills/{template-bind → template-registry}/video_dsl/schema/video-dsl-v1alpha1.json +0 -0
@@ -0,0 +1,214 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ template-registry CLI — list available video templates.
4
+
5
+ Single source of truth: registry loading, caching and stable/beta status
6
+ gating all live in ``registry_loader.py`` (the same module ``render-video``
7
+ and ``gen-script`` import in-process). This entry point only adds the CLI
8
+ surface — flag parsing, tag/aspect/language filtering, and table/JSON
9
+ rendering — that used to be re-implemented in TypeScript
10
+ (``src/handlers/template-bind.ts``). Deleting that handler removes the
11
+ TS↔Python duplication; this is now the only place the list verb is realized.
12
+
13
+ The DSL → TemplateBinding logic stays in ``match_template.py`` and is invoked
14
+ in-process by ``render-video``; it is intentionally NOT exposed as a CLI verb.
15
+
16
+ Flags:
17
+ --list-templates (accepted; listing is the only verb)
18
+ --list-examples list *.dsl.json / *.binding.json reference
19
+ examples grouped by templateId (consumed
20
+ by downstream agents that want to read a
21
+ template's reference shape before producing
22
+ new DSL)
23
+ --filter-tag <substr> keep templates whose styleTags match
24
+ --filter-aspect <ratio> keep templates supporting this aspect ratio
25
+ --filter-language <zh|en|...> keep templates tagged with this content lang
26
+ (templates with no contentLanguage are kept)
27
+ --include-beta also show beta templates (else stable-only)
28
+ --json-output emit {"templates": [...]} or {"examples": [...]}
29
+ instead of a table
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import argparse
35
+ import json
36
+ import os
37
+ import sys
38
+ from pathlib import Path
39
+
40
+ # When run as `python3 <skillDir>/scripts/list_templates.py`, this script's own
41
+ # directory is sys.path[0], so the sibling shared modules import directly.
42
+ from registry_loader import list_templates as load_visible_templates # noqa: E402
43
+
44
+
45
+ def _matches(tpl: dict, tag: str | None, aspect: str | None, language: str | None) -> bool:
46
+ if tag:
47
+ needle = tag.lower()
48
+ tags = [str(t).lower() for t in (tpl.get("styleTags") or [])]
49
+ if not any(needle in t or t in needle for t in tags):
50
+ return False
51
+ if aspect:
52
+ want = aspect.lower()
53
+ ratios = [str(r).lower() for r in (tpl.get("supportedAspectRatios") or [])]
54
+ if want not in ratios:
55
+ return False
56
+ if language:
57
+ # Unknown language (absent/empty) is never excluded by a language filter.
58
+ langs = [str(l).lower() for l in (tpl.get("contentLanguage") or [])]
59
+ if langs and language.lower() not in langs:
60
+ return False
61
+ return True
62
+
63
+
64
+ def _status_of(tpl: dict) -> str:
65
+ raw = tpl.get("status")
66
+ if isinstance(raw, str) and raw.strip():
67
+ return raw.strip().lower()
68
+ return "stable"
69
+
70
+
71
+ # ----- Examples discovery (--list-examples) ---------------------------------
72
+ #
73
+ # Examples live under <SkillDir>/video_dsl/schema/examples/ as `*.dsl.json` and
74
+ # `*.binding.json` files. Naming convention (per gen-script SKILL.md) is
75
+ # `<templateId>.dsl.json` / `<templateId>.binding.json`, occasionally with
76
+ # variant suffixes (`<templateId>.<variant>.dsl.json`).
77
+ #
78
+ # We do not parse the JSON content here (downstream agents read the raw files
79
+ # they care about); we just enumerate paths so the agent doesn't have to guess
80
+ # the directory layout. When the directory does not exist (e.g. inside a slim
81
+ # package distribution), we return an empty list — non-fatal.
82
+
83
+ # Resolve the examples directory relative to this script.
84
+ # <skill>/scripts/list_templates.py → <skill>/video_dsl/schema/examples/
85
+ _EXAMPLES_DIR = Path(__file__).resolve().parent.parent / "video_dsl" / "schema" / "examples"
86
+
87
+
88
+ def _collect_examples() -> list[dict]:
89
+ if not _EXAMPLES_DIR.is_dir():
90
+ return []
91
+ by_template: dict[str, dict] = {}
92
+ for entry in sorted(_EXAMPLES_DIR.iterdir()):
93
+ if not entry.is_file():
94
+ continue
95
+ name = entry.name
96
+ # Recognise *.dsl.json / *.binding.json
97
+ if name.endswith(".dsl.json"):
98
+ kind = "dsl"
99
+ base = name[: -len(".dsl.json")]
100
+ elif name.endswith(".binding.json"):
101
+ kind = "binding"
102
+ base = name[: -len(".binding.json")]
103
+ else:
104
+ continue
105
+ # Variant: 'foo.bar.dsl.json' → templateId='foo', variant='bar'
106
+ if "." in base:
107
+ template_id, variant = base.split(".", 1)
108
+ else:
109
+ template_id, variant = base, None
110
+ bucket = by_template.setdefault(template_id, {"templateId": template_id, "files": []})
111
+ bucket["files"].append({
112
+ "kind": kind,
113
+ "variant": variant,
114
+ "path": str(entry.relative_to(_EXAMPLES_DIR.parent.parent.parent)), # relative to skill dir
115
+ "absolutePath": str(entry),
116
+ "sizeBytes": entry.stat().st_size,
117
+ })
118
+ return list(by_template.values())
119
+
120
+
121
+ def main() -> None:
122
+ ap = argparse.ArgumentParser(
123
+ description="List available video templates from the registry.",
124
+ )
125
+ ap.add_argument("--list-templates", action="store_true",
126
+ help="List every available template (the only CLI verb).")
127
+ ap.add_argument("--list-examples", action="store_true",
128
+ help="List reference examples (*.dsl.json / *.binding.json) grouped by templateId.")
129
+ ap.add_argument("--filter-tag", help="Keep templates whose styleTags match this substring.")
130
+ ap.add_argument("--filter-aspect", help="Keep templates supporting this aspect ratio (e.g. 9:16).")
131
+ ap.add_argument("--filter-language", help="Keep templates tagged with this content language (zh|en|...).")
132
+ ap.add_argument("--include-beta", action="store_true", help="Also show beta templates (default: stable only).")
133
+ ap.add_argument("--json-output", action="store_true", help='Emit JSON ({"templates":[...]} or {"examples":[...]}) instead of a table.')
134
+ args = ap.parse_args()
135
+
136
+ # --include-beta flips registry_loader's documented status gate via env, so
137
+ # the CLI and in-process callers share one gating implementation.
138
+ if args.include_beta:
139
+ os.environ["ENABLE_BETA_TEMPLATES"] = "1"
140
+
141
+ # ----- examples mode -----------------------------------------------------
142
+ if args.list_examples:
143
+ examples = _collect_examples()
144
+ if args.json_output:
145
+ print(json.dumps({"examples": examples}, ensure_ascii=False))
146
+ return
147
+ if not examples:
148
+ print(
149
+ f"⚠️ No examples found under {_EXAMPLES_DIR}.\n"
150
+ " This package may have shipped without reference examples; "
151
+ "check the cli source repo or upgrade @remixmate/cli.",
152
+ )
153
+ return
154
+ print(f"\n{'Template ID':<28} Files")
155
+ print(f"{'-' * 28} {'-' * 60}")
156
+ for bucket in examples:
157
+ tid = str(bucket["templateId"])[:28].ljust(28)
158
+ files = bucket["files"]
159
+ for i, f in enumerate(files):
160
+ marker = tid if i == 0 else " " * 28
161
+ kind = f["kind"]
162
+ variant = f"@{f['variant']}" if f.get("variant") else ""
163
+ print(f"{marker} [{kind}{variant}] {f['path']} ({f['sizeBytes']}B)")
164
+ print(f"\n{sum(len(b['files']) for b in examples)} example file(s) across {len(examples)} template(s)")
165
+ return
166
+
167
+ try:
168
+ templates = load_visible_templates()
169
+ except RuntimeError as exc:
170
+ print(f"❌ {exc}", file=sys.stderr)
171
+ sys.exit(1)
172
+
173
+ visible = [
174
+ t for t in templates
175
+ if _matches(t, args.filter_tag, args.filter_aspect, args.filter_language)
176
+ ]
177
+
178
+ if args.json_output:
179
+ print(json.dumps({"templates": visible}, ensure_ascii=False))
180
+ return
181
+
182
+ if not visible:
183
+ print(
184
+ "⚠️ No templates match the current filters "
185
+ "(check --include-beta or relax --filter-*).",
186
+ )
187
+ return
188
+
189
+ header = (
190
+ f"\n{'Template ID':<22} {'Name':<18} {'Aspect':<12} "
191
+ f"{'Lang':<8} {'Status':<8} Tags / LLM hint"
192
+ )
193
+ print(header)
194
+ print(
195
+ f"{'-' * 22} {'-' * 18} {'-' * 12} {'-' * 8} {'-' * 8} {'-' * 60}"
196
+ )
197
+ for tpl in visible:
198
+ tid = str(tpl.get("templateId", ""))[:22].ljust(22)
199
+ name = str(tpl.get("name", ""))[:18].ljust(18)
200
+ ratios = ",".join(tpl.get("supportedAspectRatios") or [])[:12].ljust(12)
201
+ lang = (",".join(tpl.get("contentLanguage") or []) or "-")[:8].ljust(8)
202
+ status = _status_of(tpl).ljust(8)
203
+ tags = ", ".join((tpl.get("styleTags") or [])[:5])
204
+ print(f"{tid} {name} {ratios} {lang} {status} {tags}")
205
+ hint = tpl.get("llmHint")
206
+ if hint:
207
+ hint = hint if len(hint) <= 200 else hint[:197] + "..."
208
+ print(f"{' ' * 22} ↳ {hint}")
209
+
210
+ print(f"\n{len(visible)} template(s) shown")
211
+
212
+
213
+ if __name__ == "__main__":
214
+ main()
@@ -1,10 +1,11 @@
1
1
  """
2
- template-bind 库模块 — DSL → TemplateBinding 的纯函数实现。
2
+ template-registry 库模块 — DSL → TemplateBinding 的纯函数实现。
3
3
 
4
4
  仅作为 Python library 被 render-video 的 render_video.py / gen-script 的
5
5
  gen_script.py 通过 `sys.path.insert` 后 `import match_template` 引入;
6
- 对外不再暴露 CLItemplate-bind skill 已迁移到 ab-skill CLI 的 builtin
7
- handler,由 src/handlers/template-bind.ts 处理 --list-templates)。
6
+ 对外不暴露 binding CLItemplate-registry skill `--list-templates` 入口由
7
+ sibling 的 `list_templates.py` 实现(它复用 registry_loader 的统一加载 +
8
+ 状态门控),两者共享同一份 registry 读取逻辑,无 TS 侧重复实现。
8
9
 
9
10
  提供的公共接口:
10
11
  load_registry() → list[dict] 模板列表
@@ -3,7 +3,7 @@
3
3
  模板 registry 加载器 - 统一抽象本地文件 / HTTP API 两种数据源。
4
4
 
5
5
  为什么独立成一个模块:
6
- template-bind、render-video 等多个 skill 都要读 registry,集中在一处便于:
6
+ template-registry、render-video 等多个 skill 都要读 registry,集中在一处便于:
7
7
  - 缓存策略一致(HTTP 拉取 5 分钟 TTL + ETag 复用)
8
8
  - 解析顺序一致(环境变量 → 本地文件 → monorepo 回退)
9
9
  - 错误降级一致(HTTP 故障时复用上一次缓存)
@@ -6,9 +6,9 @@ Before this file landed, every caller that needed to reach into the
6
6
  monorepo's ``template-library/`` from inside ``apps/ab-skill/`` had to count
7
7
  ``..`` segments by hand. The depth differs per call site:
8
8
 
9
- skills/template-bind/scripts/registry_loader.py → 5 × ".."
10
- skills/template-bind/scripts/check_contracts.py → 3 × ".."
11
- skills/template-bind/video_dsl/runtime/template_binder → 6 × ".."
9
+ skills/template-registry/scripts/registry_loader.py → 5 × ".."
10
+ skills/template-registry/scripts/check_contracts.py → 3 × ".."
11
+ skills/template-registry/video_dsl/runtime/template_binder → 6 × ".."
12
12
  skills/gen-script/scripts/gen_script.py → 5 × ".."
13
13
  skills/render-video/scripts/render_video.py → 4 × ".."
14
14
 
@@ -17,9 +17,9 @@ move silently breaks one site without touching the others. This module
17
17
  computes the monorepo root **once** from its own location and exposes
18
18
  helpers so callers no longer count.
19
19
 
20
- All sibling skills now go through these helpers; the TS handler in
21
- ``src/handlers/template-bind.ts`` keeps its own one-off computation
22
- because it cannot import this Python module.
20
+ All sibling skills (including the ``list_templates.py`` CLI entry, via
21
+ ``registry_loader``) go through these helpers, so the monorepo-root
22
+ computation lives in exactly one place.
23
23
  """
24
24
  from __future__ import annotations
25
25
 
@@ -29,7 +29,7 @@ from typing import Optional
29
29
  __all__ = ["monorepo_root", "monorepo_registry_path", "monorepo_template_src_dir"]
30
30
 
31
31
  # This file lives at:
32
- # <repo>/apps/ab-skill/skills/template-bind/scripts/template_paths.py
32
+ # <repo>/apps/ab-skill/skills/template-registry/scripts/template_paths.py
33
33
  # So the monorepo root is 5 levels up.
34
34
  _HERE = os.path.dirname(os.path.abspath(__file__))
35
35
  _REPO_ROOT = os.path.normpath(os.path.join(_HERE, "..", "..", "..", "..", ".."))
@@ -1,19 +1,22 @@
1
1
  {
2
- "name": "template-bind",
3
- "toolName": "template_bind",
2
+ "name": "template-registry",
3
+ "toolName": "template_registry",
4
4
  "tier": "orchestration",
5
- "title": "Template Registry (List-only)",
5
+ "category": "authoring",
6
+ "title": "Video Template Registry",
6
7
  "description": "List all available video templates (templateId / name / aspect ratio / style tags). Template-to-DSL binding is no longer exposed as a separate step — once prepare_video_assets receives a template_id it runs the full DSL→RenderPlan pipeline internally.",
7
8
  "envVars": ["PRIV_TOKEN", "VIDEO_TEMPLATE_REGISTRY", "VIDEO_TEMPLATE_REGISTRY_URL", "VIDEO_TEMPLATE_REGISTRY_HTTP_METHOD"],
8
- "entry": { "type": "builtin", "handler": "template-bind" },
9
+ "entry": { "type": "python", "scriptPath": "scripts/list_templates.py" },
9
10
  "parameters": {
10
11
  "type": "object",
11
12
  "properties": {
12
13
  "list_templates": { "type": "boolean", "description": "List available templates (default behavior; also implied when other flags are passed)." },
14
+ "list_examples": { "type": "boolean", "description": "List the *.dsl.json / *.binding.json reference examples shipped under template-registry/video_dsl/schema/examples/, grouped by templateId. Useful for downstream agents (e.g. a creation agent) that want to read a template's reference shape before producing new DSL." },
13
15
  "filter_tag": { "type": "string", "description": "Show only templates whose styleTags contain (or are contained in) this string. Case-insensitive. E.g. 'tech' / '科普' / 'walkthrough'." },
14
16
  "filter_aspect": { "type": "string", "description": "Show only templates that declare this aspect ratio. E.g. '9:16' / '16:9' / '1:1'." },
15
17
  "filter_language": { "type": "string", "description": "Show only templates whose contentLanguage includes this code ('zh' or 'en'). Templates with no declared language are always shown (treated as language-agnostic)." },
16
- "include_beta": { "type": "boolean", "description": "Also list templates with status='beta'. Default off (only 'stable' shows). The ENABLE_BETA_TEMPLATES env var has the same effect process-wide." }
18
+ "include_beta": { "type": "boolean", "description": "Also list templates with status='beta'. Default off (only 'stable' shows). The ENABLE_BETA_TEMPLATES env var has the same effect process-wide." },
19
+ "json_output": { "type": "boolean", "description": "Emit a JSON result ({ templates: [...] } or { examples: [...] }) instead of the human-readable table." }
17
20
  },
18
21
  "required": []
19
22
  }
@@ -0,0 +1,7 @@
1
+ {
2
+ "skillName": "template-registry",
3
+ "repoName": "agent-skill-media-maker",
4
+ "skillId": "475",
5
+ "version": "V12",
6
+ "skillDescription": "视频模板仓库(列表查询)。存储所有视频模板定义,对外只暴露「列出可用模板」一个能力;DSL→TemplateBinding 的绑定逻辑已内嵌进 prepare-video-assets,不再作为独立步骤暴露。同时是跨 skill 共享 Python 库(registry_loader / match_template / template_paths / video_dsl 等)的存放位置。\n\n当用户提到以下任何需求时,立即使用本 skill:\n- 查看可用模板、列出所有模板"
7
+ }
@@ -47,7 +47,7 @@ video_dsl/
47
47
  ```
48
48
  主题 → [gen-script] → Video DSL
49
49
 
50
- [template-bind] → TemplateBinding
50
+ [template-registry] → TemplateBinding
51
51
 
52
52
  [render-video] → RenderPlan → MP4 / 剪映草稿
53
53
  ```
@@ -67,7 +67,7 @@ video_dsl/
67
67
  | 模块 | 职责 | 被谁调用 |
68
68
  |------|------|----------|
69
69
  | `dsl_validator.py` | DSL JSON Schema 校验与参数标准化 | gen-script、render-video |
70
- | `template_binder.py` | 将 DSL scene 映射到模板 slot,生成 TemplateBinding(从 @ab-templates/metadata 读取模板定义) | template-bind |
70
+ | `template_binder.py` | 将 DSL scene 映射到模板 slot,生成 TemplateBinding(从 @ab-templates/metadata 读取模板定义) | template-registry |
71
71
  | `timeline_compiler.py` | 帧号计算、字幕切片(`split_subtitle`)、旁白分段(`segment_narration`) | render-video(跨 skill import) |
72
72
  | `asset_adapter.py` | 素材格式适配 | render-video |
73
73
  | `prompt_enhancer.py` | 图片 prompt 增强 | render-video |
@@ -1,7 +1,7 @@
1
1
  """Package marker for ``video_dsl.runtime`` — kept intentionally empty.
2
2
 
3
3
  This file exists so ``from video_dsl.runtime.<mod> import …`` resolves when
4
- ``skills/template-bind/`` is on sys.path (the pattern used by render-video
4
+ ``skills/template-registry/`` is on sys.path (the pattern used by render-video
5
5
  and gen-script). Public modules:
6
6
 
7
7
  - timeline_compiler : DSL → Remotion frame-axis timeline + subtitle splits
@@ -17,222 +17,109 @@ triggers:
17
17
 
18
18
  # Video Deconstruction Skill
19
19
 
20
- Splits a video into reusable content assets — audio, ASR transcript (with per-word timestamps), scene cuts, keyframe images. Emits a structured asset-manifest JSON.
20
+ Splits a video into reusable content assets — audio, ASR transcript (with per-word timestamps), scene cuts, keyframe images and returns a structured asset-manifest JSON.
21
21
 
22
- ## Dependencies
23
-
24
- - **ffmpeg**: must be installed locally (`brew install ffmpeg`) — used for audio extraction, scene-change detection, and keyframe capture.
25
- - **Python 3**: standard library only, no third-party dependencies.
22
+ The skill entry (`scripts/parse_via_render.py`) runs the deconstruction **through the ab-render HTTP service** (`POST /parse` → poll `POST /parseStatus`). It needs **no local ffmpeg** — the heavy lifting happens server-side and the manifest (with cloud-hosted asset URLs) comes back as JSON on stdout.
26
23
 
27
24
  ## Authentication & environment
28
25
 
29
- There is no dedicated env file; the **process environment** variables of the caller apply. Examples use `python`; on macOS without that alias use `python3`.
26
+ No skill-local env file the executing process inherits the system environment.
30
27
 
31
- - **Enterprise OpenClaw**: auth is injected, **no** `PRIV_TOKEN` / `--priv-token` needed.
32
- - **Other environments**: configure a token — obtain a `PRIV_TOKEN` from your ab-api service. Non-interactive invocations may fail when the token is missing; interactive runs prompt for one.
28
+ - **Enterprise OpenClaw**: auth + service address are injected; nothing to configure.
29
+ - **Other environments**: set `RENDER_API_URL` (the ab-render service) and `PRIV_TOKEN`. Without them the run fails fast with an actionable message.
33
30
 
34
31
  | Env var | Description | Default |
35
32
  |---------|-------------|---------|
36
- | `PRIV_TOKEN` | Tianyan token; overridable with `--priv-token`. | (none) |
37
- | `MM_API_BASE_URL` | API root URL. | `http://localhost:3001/api` |
38
- | `AGENT_NAME` | Optional, sent as the `x-invoke-agent` request header. | (none) |
33
+ | `RENDER_API_URL` | ab-render service base URL (e.g. `http://localhost:3000`). Required. | (none) |
34
+ | `PRIV_TOKEN` | Tianyan token, sent as `X-Priv-Token`. | (none) |
35
+ | `CONVERSATION_ID` | Optional, sent as `x-conversation-id` for file association. | (none) |
39
36
 
40
37
  ## Steps
41
38
 
42
- 1. **Script path**: read the system-injected `Base directory for this skill: <path>` as `<SkillDir>`. Every command below uses `<SkillDir>/scripts/deconstruct_video.py`; never hard-code paths.
43
- 2. **Confirm ffmpeg**: run `ffmpeg -version` to confirm it is installed locally.
44
- 3. **Pick the input**: either a direct video URL or a local file path (one of the two).
45
- 4. **Run the deconstruction**: invoke the script and wait for it to finish.
46
- 5. **Inspect the result**: check `deconstruction.json`, `audio.mp3`, and the `keyframes/` directory in the output folder.
39
+ 1. **Script path**: read the system-injected `Base directory for this skill: <path>` as `<SkillDir>`. The entry is always `<SkillDir>/scripts/parse_via_render.py`; never hard-code paths.
40
+ 2. **Provide a direct video URL** (`--url`). Page links (Douyin / Xiaohongshu share pages) are not supported — pass a direct video file URL.
41
+ 3. **Run and wait**: the script submits the task and polls until it finishes (up to ~10 minutes), printing progress lines.
42
+ 4. **Consume the result**: a JSON asset-manifest is printed to stdout (audio / ASR / scenes / keyframes, with cloud URLs). Save or pipe it as needed.
47
43
 
48
- ### Deconstruct from a URL
44
+ ### Deconstruct a video
49
45
 
50
46
  ```bash
51
- python3 <SkillDir>/scripts/deconstruct_video.py \
52
- --url "https://example.com/video.mp4" \
53
- --output-dir ./output_deconstructed
54
- ```
55
-
56
- ### Deconstruct from a local file
57
-
58
- ```bash
59
- python3 <SkillDir>/scripts/deconstruct_video.py \
60
- --local /path/to/video.mp4 \
61
- --output-dir ./output_deconstructed
47
+ python3 <SkillDir>/scripts/parse_via_render.py \
48
+ --url "https://example.com/video.mp4"
62
49
  ```
63
50
 
64
51
  ### Custom scene-change threshold
65
52
 
66
53
  ```bash
67
- python3 <SkillDir>/scripts/deconstruct_video.py \
54
+ python3 <SkillDir>/scripts/parse_via_render.py \
68
55
  --url "https://example.com/video.mp4" \
69
56
  --scene-threshold 0.4
70
57
  ```
71
58
 
72
- Lower values are more sensitive (more cuts), higher values are looser (fewer cuts). Default is 0.3.
59
+ Lower values are more sensitive (more cuts), higher values looser (fewer cuts). Default `0.3`.
73
60
 
74
- ### Audio + ASR only (skip keyframes)
61
+ ### Skip ASR or keyframes
75
62
 
76
63
  ```bash
77
- python3 <SkillDir>/scripts/deconstruct_video.py \
78
- --url "https://example.com/video.mp4" \
79
- --skip-keyframes
80
- ```
81
-
82
- ### Keyframes only (skip ASR)
64
+ # audio + scenes only (skip keyframes)
65
+ python3 <SkillDir>/scripts/parse_via_render.py --url "https://example.com/video.mp4" --skip-keyframes
83
66
 
84
- ```bash
85
- python3 <SkillDir>/scripts/deconstruct_video.py \
86
- --url "https://example.com/video.mp4" \
87
- --skip-asr
67
+ # keyframes + scenes only (skip ASR)
68
+ python3 <SkillDir>/scripts/parse_via_render.py --url "https://example.com/video.mp4" --skip-asr
88
69
  ```
89
70
 
90
71
  ### JSON pipeline mode
91
72
 
92
73
  ```bash
93
- python3 <SkillDir>/scripts/deconstruct_video.py \
74
+ python3 <SkillDir>/scripts/parse_via_render.py \
94
75
  --url "https://example.com/video.mp4" \
95
76
  --json-output
96
77
  ```
97
78
 
98
- Prints only the final `deconstruction.json` payload to stdout — no progress lines — suitable for piping.
99
-
100
- See every option: `python3 <SkillDir>/scripts/deconstruct_video.py --help`.
79
+ Prints only the final manifest JSON to stdout — no progress lines — suitable for piping into downstream tools.
101
80
 
102
81
  ## Common CLI flags
103
82
 
104
83
  | Flag | Description | Default |
105
84
  |------|-------------|---------|
106
- | `--url` | Remote video URL (mutually exclusive with `--local`). | — |
107
- | `--local` | Local video path (mutually exclusive with `--url`). | — |
108
- | `-o` / `--output-dir` | Output directory. | `./deconstructed_<timestamp>` |
109
- | `--scene-threshold` | Scene-change threshold 0.0–1.0. | `0.3` |
110
- | `--private-token` | Override `privateToken`. | env var |
111
- | `--json-output` | Pipeline mode — JSON-only stdout. | off |
85
+ | `--url` | Direct remote video URL (required). | — |
86
+ | `--scene-threshold` | Scene-change sensitivity 0.0–1.0. | `0.3` |
112
87
  | `--skip-asr` | Skip the ASR step. | off |
113
88
  | `--skip-keyframes` | Skip the keyframe-extraction step. | off |
89
+ | `--json-output` | Pipeline mode — JSON-only stdout. | off |
114
90
 
115
- ## Output layout
116
-
117
- The script writes the following under `--output-dir`:
118
-
119
- ```
120
- <output-dir>/
121
- ├── audio.mp3 # Extracted audio file
122
- ├── deconstruction.json # Asset-manifest JSON
123
- └── keyframes/ # Keyframe images
124
- ├── frame_001.jpg
125
- ├── frame_002.jpg
126
- └── ...
127
- ```
91
+ ## Result manifest shape
128
92
 
129
- ### `deconstruction.json` shape
93
+ `parse_via_render.py` prints the service's `result` payload — a structured manifest whose top-level fields are:
130
94
 
131
95
  ```json
132
96
  {
133
- "source": {
134
- "url": "original URL (if any)",
135
- "localPath": "local video path",
136
- "durationMs": 12345
137
- },
138
- "audio": {
139
- "localPath": "output_dir/audio.mp3",
140
- "url": "uploaded OSS URL"
141
- },
142
- "asr": {
143
- "text": "full recognized transcript",
144
- "duration": 3816,
145
- "utterances": [
146
- {
147
- "text": "per-utterance text",
148
- "startTime": 0,
149
- "endTime": 1000,
150
- "words": [...]
151
- }
152
- ]
153
- },
154
- "scenes": [
155
- {
156
- "index": 0,
157
- "startTimeSec": 0.0,
158
- "endTimeSec": 3.5,
159
- "keyframe": "keyframes/frame_001.jpg"
160
- }
161
- ],
162
- "keyframes": [
163
- {
164
- "index": 0,
165
- "timestampSec": 0.0,
166
- "localPath": "keyframes/frame_001.jpg"
167
- }
168
- ]
97
+ "source": { "url": "original URL", "durationMs": 12345 },
98
+ "audio": { "url": "cloud audio URL" },
99
+ "asr": { "text": "full transcript", "utterances": [ { "text": "...", "startTime": 0, "endTime": 1000, "words": [] } ] },
100
+ "scenes": [ { "index": 0, "startTimeSec": 0.0, "endTimeSec": 3.5, "keyframe": "cloud keyframe URL" } ],
101
+ "keyframes":[ { "index": 0, "timestampSec": 0.0, "url": "cloud keyframe URL" } ]
169
102
  }
170
103
  ```
171
104
 
172
- ## Deconstruction structural analysis
173
-
174
- Reads the deconstruction artifacts and extracts hook, narrative structure, copy pacing, scene composition, etc., emitting a structured analysis report. Script path: `<SkillDir>/scripts/analyze_video.py`.
175
-
176
- The analysis report can be used to:
177
- - Feed an AI to write a new script with the same structure.
178
- - Guide a human re-creator to replicate the video's rhythm.
179
- - Inform downstream template development.
105
+ (Exact fields are defined by the ab-render service; treat the above as representative.)
180
106
 
181
- ### Basic usage
107
+ ## Optional local tools (not the skill entry)
182
108
 
183
- ```bash
184
- python3 <SkillDir>/scripts/analyze_video.py \
185
- -i ./output/deconstructed_xxx/deconstruction.json
186
- ```
187
-
188
- ### Specify the output directory
189
-
190
- ```bash
191
- python3 <SkillDir>/scripts/analyze_video.py \
192
- -i ./output/deconstructed_xxx/ \
193
- -o ./reports/
194
- ```
109
+ Two extra scripts ship alongside the entry for local / offline use. They are **not** invoked by the `video_parser` tool and are **not** the skill entry — call them directly with `python3` only when you specifically need local processing:
195
110
 
196
- ### Pipeline mode
111
+ - **`scripts/deconstruct_video.py`** — a fully local deconstruction alternative that **requires ffmpeg** (`brew install ffmpeg`) and writes `audio.mp3` + `keyframes/` + `deconstruction.json` to an `--output-dir`. Use when there is no ab-render service available. Flags: `--url` / `--local`, `-o/--output-dir`, `--scene-threshold`, `--skip-asr`, `--skip-keyframes`, `--json-output`.
112
+ - **`scripts/analyze_video.py`** — a second stage that reads a `deconstruction.json` (or the manifest above saved to disk) and emits a structural-analysis report (hook, narrative structure, pacing, CTA, …). Flags: `-i/--input`, `-o/--output-dir`, `--json-output`.
197
113
 
198
- ```bash
199
- python3 <SkillDir>/scripts/analyze_video.py \
200
- -i ./output/deconstructed_xxx/ \
201
- --json-output
202
- ```
203
-
204
- Prints only the `analysis.json` payload to stdout, no progress text, suitable for piping.
205
-
206
- ### analyze_video.py flags
207
-
208
- | Flag | Description | Default |
209
- |------|-------------|---------|
210
- | `-i` / `--input` | Path to `deconstruction.json` or its parent directory. | required |
211
- | `-o` / `--output-dir` | Output directory. | same as input |
212
- | `--json-output` | Pipeline mode (JSON-only stdout). | off |
213
-
214
- ### Output files
215
-
216
- `analysis.json`: the structured analysis report. Covers the following dimensions:
217
-
218
- | Field | Description |
219
- |-------|-------------|
220
- | `source` | Original video information (URL, duration). |
221
- | `overview` | Language, scene count, copy length, asset types. |
222
- | `hook` | Opening-hook text, duration, strategy classification. |
223
- | `narrativeStructure` | Per-scene narrative structure (role, duration share, text density). |
224
- | `pacing` | Pacing analysis (scene-duration distribution, rhythm type). |
225
- | `textStructure` | Copy structure (speaking rate, silent gaps, keywords). |
226
- | `audioPattern` | Audio pattern (speech vs. silence ratio). |
227
- | `cta` | CTA detection (closing copy, whether an explicit CTA exists). |
228
- | `assets` | Asset manifest (audio, keyframes). |
114
+ ```bash
115
+ python3 <SkillDir>/scripts/analyze_video.py -i ./deconstruction.json
116
+ ```
229
117
 
230
118
  ## Error handling
231
119
 
232
- - **ffmpeg not installed**: the script detects this and prompts `brew install ffmpeg`.
233
- - **401 / token missing** (non-OpenClaw): check or set the token via the Feishu link above.
120
+ - **`RENDER_API_URL` not configured**: the script exits with a clear message; set the ab-render service URL.
121
+ - **401 / token missing** (non-OpenClaw): set `PRIV_TOKEN`.
234
122
  - **Business `code != 0`**: read the `msg` printed on stderr.
235
- - **Video download failed**: confirm the URL is a direct video link (page links such as Douyin / Xiaohongshu are not supported).
236
- - **ASR timeout**: large files can take a while; the script defaults to a 300-second timeout.
237
- - **No scene change detected**: too-high threshold can yield zero cuts — try lowering `--scene-threshold`.
238
- - **Network**: check connectivity and `MM_API_BASE_URL`.
123
+ - **Video download failed**: confirm the URL is a direct video link (share/page links are not supported).
124
+ - **Timeout**: very long videos may exceed the ~10-minute poll window; retry or pre-trim.
125
+ - **No scene change detected**: a too-high threshold yields zero cuts — lower `--scene-threshold`.
@@ -2,20 +2,20 @@
2
2
  "name": "video-parser",
3
3
  "toolName": "video_parser",
4
4
  "tier": "tool",
5
+ "category": "consuming",
5
6
  "title": "Video Deconstruction",
6
7
  "description": "Video deconstruction: split a video into reusable assets (audio extraction, ASR transcript, keyframe extraction, scene segmentation).",
7
- "envVars": ["PRIV_TOKEN", "MM_API_BASE_URL", "AGENT_NAME", "RENDER_API_URL"],
8
+ "envVars": ["RENDER_API_URL", "PRIV_TOKEN", "CONVERSATION_ID"],
8
9
  "scriptPath": "scripts/parse_via_render.py",
9
10
  "parameters": {
10
11
  "type": "object",
11
12
  "properties": {
12
- "url": { "type": "string", "description": "Remote video URL (or use local)" },
13
- "local": { "type": "string", "description": "Local video path (or use url)" },
13
+ "url": { "type": "string", "description": "Direct remote video URL (required). Share/page links are not supported." },
14
14
  "scene_threshold": { "type": "number", "description": "Scene-cut detection threshold 0.0-1.0 (default 0.3)" },
15
15
  "skip_asr": { "type": "boolean", "description": "Skip the ASR step" },
16
16
  "skip_keyframes": { "type": "boolean", "description": "Skip the keyframe-extraction step" },
17
17
  "json_output": { "type": "boolean", "description": "Pipeline mode — emit JSON only on stdout" }
18
18
  },
19
- "required": []
19
+ "required": ["url"]
20
20
  }
21
21
  }