cawcut-ad-video
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseCawCut Ad Video
CawCut 广告视频
Create a complete product ad video through the 5-stage AD Video wizard: Product → Config → Idea → Storyboard → Video. Each stage maps to commands against the developer API. Full command flag tables live in ; stage semantics and the invalidation matrix live in ; authoring a script edit lives in ; error recovery lives in . them at using the base directory printed at the top of this skill body — do not / for them.
cawcut ad-videoreferences/api.mdreferences/stages.mdreferences/script-editing.mdreferences/troubleshooting.mdRead<base_dir>/references/<file>.mdfindgrepReply language. All live replies — prose, status updates, / questions and option labels — follow the user's language for that turn. This is judged from the user's actual chat message content for that turn — not from whether the tool call itself carried an parameter, which is an unrelated, optional pass-through field. This includes implicit triggers: if the skill was invoked because the user's message matched a trigger phrase (e.g. "广告视频", "商品广告", "make an ad for …") rather than the literal command, that trigger phrase is the user's chat message content for the turn — reply in its language from the very first response, including when asking for the missing product URL. Only default to English when the invocation carries no language signal at all (e.g. literally just alone with no other text); once the user adds a real request in another language, follow that language for the rest of the session. Keep CLI commands/flags, JSON keys, URLs, and raw error codes in their original form regardless of reply language, and never paste raw English CLI output as the user-facing answer — summarize it in .
AskUserQuestionAskQuestionSkillargs/cawcut-ad-video/cawcut-ad-videoreply_language通过 5 阶段广告视频向导创建完整的产品广告视频:产品 → 配置 → 创意 → 分镜 → 视频。每个阶段都对应一组针对开发者 API 的 命令。完整命令参数表位于 ;各阶段语义和失效矩阵位于 ;脚本编辑方法位于 ;错误恢复方法位于 。使用此 skill 正文顶部打印的基础目录,通过 读取 ——切勿使用 或 查找这些文件。
cawcut ad-videoreferences/api.mdreferences/stages.mdreferences/script-editing.mdreferences/troubleshooting.mdRead<base_dir>/references/<file>.mdfindgrep回复语言。 所有实时回复——说明文字、状态更新、 / 问题及选项标签——都应遵循用户本轮使用的语言。判断依据是用户本轮实际聊天消息的内容,而不是 工具调用是否携带 参数;后者只是一个无关的可选透传字段。这也包括隐式触发:如果 skill 是因为用户消息匹配了触发短语(例如“广告视频”、“商品广告”、“make an ad for …”)而被调用,那么该触发短语就是本轮用户的聊天消息内容——从第一次回复开始就应使用该语言,包括询问缺失的产品 URL 时。只有在调用完全没有语言信号时(例如仅输入 且没有其他文本),才默认使用英语;一旦用户用另一种语言提出实际请求,本会话余下时间都应使用该语言。无论回复语言如何,CLI 命令和参数、JSON 键、URL 及原始错误代码都应保持原样;绝不能将原始英文 CLI 输出直接作为面向用户的答案,而应在 中进行总结。
AskUserQuestionAskQuestionSkillargs/cawcut-ad-videoreply_languageStep 0 — Bootstrap
第 0 步 — 启动
Choose the login mode by execution environment
根据执行环境选择登录模式
Choose by where commands actually run. On the user's own computer, run ; the CLI opens the browser and waits for approval. In a remote environment, VM, or container without access to the user's browser, run and send the user to open and approve on their own computer. Both modes support JSON; needing structured output is not a reason to choose device authorization. Agent names, non-TTY shells, and localhost URLs alone do not establish where commands run. Use the known execution context and explicit mode flags to override CLI auto-detection.
cawcut auth login --browser --jsoncawcut auth login --device-auth --jsonauthorization_urlFor device login, then run . If your tool has a short execution timeout, use single calls instead, following . After device login is interrupted or returns , resume the saved current request rather than creating a new link. Browser login uses the legacy loopback callback; if interrupted, restart instead of using . is not authentication success. Only retry the original operation after . Denial/expiry ends this request; do not silently create an endless sequence of authorization links. Never ask for a password, token, or a code copied back from the web page.
cawcut auth login --complete --wait --timeout 60 --json--complete --jsonnext_poll_after_secondswait_timeoutcawcut auth login --browser --json--completestatus=pendingstatus=authenticatedRun bootstrap as a silent guardrail, not as a user-facing phase.
- At most once per AI session. If any command has already succeeded in this AI session, skip all bootstrap checks and continue.
cawcut - Use only one explicit check command: .
cawcut upgrade check --json- If , run
"update_available": trueyourself in the host shell (do not ask the user to type it). After it succeeds, continue this skill from Step 1.cawcut upgrade - If , continue without upgrading.
"ahead_of_registry": true - If is missing, ask the user to install it:
cawcut. Then continue bootstrap.npm install -g @ubnt/cawcut - For other check failures, continue and mention the warning only if a later CLI command fails.
- If
- Do not print "bootstrap checks passed" — move directly to Step 1.
- Let the first real command validate auth. If it fails with token/auth errors (including ), follow the login steps above. Use the mode appropriate to the execution environment, and retry the failed command once only after
Token expired. Run the CLI commands yourself; the user only approves in their browser.status=authenticated - Structured user-ask tool (check every session). In Claude Code, use for every enumerable choice; in Cursor, use
AskUserQuestion. If either is available, you must use it for all enumerable decisions in this skill — platform, resolution, duration, video type, storyboard style, language, idea candidate, credit confirmation — including when the candidate list is longer than the popup holds: a long list still opens the tool, with its leading entries as the options and the full numbered list beside them so any later entry can be typed back (see Interactive selection).AskQuestion
根据命令实际运行的位置选择模式。在用户自己的计算机上,运行 ;CLI 会打开浏览器并等待批准。在无法访问用户浏览器的远程环境、VM 或容器中,运行 ,并将 发给用户,让用户在自己的计算机上打开并批准。两种模式都支持 JSON;需要结构化输出并不构成选择设备授权的理由。Agent 名称、非 TTY shell 和 localhost URL 本身不能说明命令的实际运行位置。应使用已知的执行上下文和显式模式参数覆盖 CLI 的自动检测。
cawcut auth login --browser --jsoncawcut auth login --device-auth --jsonauthorization_url完成设备登录后,运行 。如果工具的执行超时时间较短,则改为单次调用 ,并遵循 。设备登录被中断或返回 后,应恢复已保存的当前请求,而不是创建新链接。浏览器登录使用旧版 loopback callback;如果被中断,应重新运行 ,而不是使用 。 不代表认证成功。只有在 后,才能重试原始操作。拒绝或过期意味着本次请求结束;不要无休止地创建授权链接。绝不能要求用户提供密码、token,或从网页复制验证码。
cawcut auth login --complete --wait --timeout 60 --json--complete --jsonnext_poll_after_secondswait_timeoutcawcut auth login --browser --json--completestatus=pendingstatus=authenticated将启动过程作为静默护栏执行,而不是面向用户的阶段。
- 每个 AI 会话最多执行一次。 如果本 AI 会话中已有任何 命令成功执行,则跳过所有启动检查并继续。
cawcut - 只使用一条显式检查命令: 。
cawcut upgrade check --json- 如果 ,应在宿主 shell 中亲自运行
"update_available": true(不要让用户手动输入)。成功后,从第 1 步继续。cawcut upgrade - 如果 ,无需升级,直接继续。
"ahead_of_registry": true - 如果缺少 ,请用户安装:
cawcut。然后继续启动流程。npm install -g @ubnt/cawcut - 如果发生其他检查失败,继续执行;仅当后续 CLI 命令失败时再提示该警告。
- 如果
- 不要输出“启动检查已通过”——直接进入第 1 步。
- 让第一条真实命令验证认证。如果命令因 token 或认证错误而失败(包括 ),执行上述登录步骤。选用适合当前执行环境的模式,并且仅在
Token expired后重试失败命令一次。应亲自运行 CLI 命令;用户只需在浏览器中批准。status=authenticated - 结构化用户询问工具(每个会话都要检查)。 在 Claude Code 中,对每个可枚举选项都使用 ;在 Cursor 中使用
AskUserQuestion。如果任一工具可用,则本 skill 中所有可枚举决策都必须使用它——包括平台、分辨率、时长、视频类型、分镜风格、语言、创意候选项和 credits 确认——即使候选项数量超过弹窗容量也一样:长列表仍应打开工具,前几项作为选项显示,并在完整编号列表旁展示所有候选项,以便用户输入后续条目的编号(参见“交互式选择”)。AskQuestion
Wizard overview
向导概览
① Product create → analyze run → poll → review/confirm analysis
② Config options → pick settings → config set
③ Idea idea run → poll → pick candidate (or custom) → idea select
④ Storyboard storyboard run → poll → (optional edit / regenerate) → image quote → confirm → image run (×2 if hand-drawn)
⑤ Video video quote → confirm → video run → poll → downloadOne project = one . Every long stage is triggered without , then polled with the bundled script (see Polling). is the single polling point — only it returns the map.
video_ad_id--waitcawcut ad-video detail <id> --jsonoutputs① Product create → analyze run → poll → review/confirm analysis
② Config options → pick settings → config set
③ Idea idea run → poll → pick candidate (or custom) → idea select
④ Storyboard storyboard run → poll → (optional edit / regenerate) → image quote → confirm → image run (×2 if hand-drawn)
⑤ Video video quote → confirm → video run → poll → download一个项目对应一个 。每个长阶段都应先不带 触发,再使用内置脚本轮询(参见“轮询”)。 是唯一的轮询点——只有它会返回 映射。
video_ad_id--waitcawcut ad-video detail <id> --jsonoutputsBundled-script runner
内置脚本运行器
Use CLI commands directly for project operations and detail inspection. Choose bundled helpers for the host platform. Windows helpers require PowerShell and the installed CLI, not Python, jq, bash, or external curl; do not ask Windows users to install those tools for this skill. macOS/Linux helpers require jq; neither platform uses Python.
cawcutcawcut.sh- macOS/Linux:
bash <base_dir>/scripts/<name>.sh … - Windows PowerShell:
powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\<name>.ps1" …
The three helpers (, , ) have equivalent and implementations. Use the matching extension and platform-specific flags throughout one run; for example, on bash is on PowerShell.
pollscript_plandownload_media.sh.ps1--kind video-Kind video项目操作和详情检查应直接使用 CLI 命令。根据宿主平台选择内置辅助脚本。Windows 辅助脚本需要 PowerShell 和已安装的 CLI,而不是 Python、jq、bash 或外部 curl;不要要求 Windows 用户为此安装这些工具。macOS/Linux 的 辅助脚本需要 jq;两个平台都不使用 Python。
cawcutcawcut.sh- macOS/Linux:
bash <base_dir>/scripts/<name>.sh … - Windows PowerShell:
powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\<name>.ps1" …
三个辅助脚本(、、)均提供等效的 和 实现。一次运行期间应始终使用匹配的扩展名和平台专用参数;例如,bash 中的 在 PowerShell 中对应 。
pollscript_plandownload_media.sh.ps1--kind video-Kind videoStage ① — Product
第 ① 阶段 — 产品
- Collect the product URL (Amazon/Shopify/official page all work). If the user didn't give one, ask in plain text (open-ended). Do NOT ask for a project name — the backend auto-names the project from the product info after analysis/export.
- → capture
cawcut ad-video create --url "<url>" --json.idis optional at create (an empty draft is allowed), but pass it whenever the user gave one —--urlrequires it later regardless.analyze runis optional; omit it and the backend auto-detects after analysis. Only pass--categoryif the user explicitly asked for a specific title.--name - (no
cawcut ad-video analyze run <id> --url "<the product URL from step 1>" --json; the API requires--waitin the body), then poll with the platform runner:urlon macOS/Linux, orpoll.sh <id> '.status' stage_completed 600 15on Windows.poll.ps1 <id> '.status' stage_completed 600 15 - Read and present the analysis result:
detail <id> --json- Product info verbatim — every populated field:
product_info,name,description(each in full, numbered),selling_points[],target_audience,brand_style,promotional_info,source_platform. Do not summarize, truncate, or translate the product content itself — it is the user's marketing copy and must be shown as returned; only your surrounding labels and prose follow reply_language.category - Product images — first download them with the platform-specific helper:
download_media. Then present by category (labels in reply_language):<id> --kind product- Logo () and Primary images (
logo_images): preview each inline via the host's send-file tool (e.g. Claude'sprimary_images) with the local path just downloaded (e.g.SendUserFile,~/Downloads/ad-video-<name>/product/logo-1.jpg) — a markdown link only renders as a clickable line, not a visible image.primary-1.jpg - Additional product images (): if inline previews are supported, list them one by one, each labeled
images[]with the image sent inline; cap at the first 9 — if there are more, say so (e.g.Product Image #n) and tell the user the rest are in the…and N–9 morefolder. If inline previews are not supported, list them as a table of local paths (<dest>/product/+ path) and point to the same folder.# - No column in either form — the local path is a preview reference only. These files exist so the user can see the images; never hand them back to the CLI. Then ask the user to confirm or correct via AskUserQuestion:
asset_id
- Logo (
- → continue to Stage ②
✅ Looks good - → collect the changes and pass only those fields to
✏️ Edit product info: the command reads the project first and carries every field you did not pass forward. (The API replacescawcut ad-video product set <id> …wholesale, so a bareproduct_infowould otherwise erase the description, the images and the selling points.) Re-run--product-nameafterwards if the URL or the product changed materially.analyze run- An image list is replaced by passing it, so /
--logo/--primary-imagetake the complete kept list — that is also how an image is removed. A replaced list drops its own selection, and the backend falls back to its defaults (every candidate for--asset, the first candidate for logo/primary). Never re-send the files you downloaded: the persisted refs already come back as--assets.asset_id - Pass a local path only for an image the user supplies from outside the project; the CLI uploads it as a brand-new asset (7-day TTL).
- clears the description and
--description ""empties the selling points — an empty list is a real state, not "unset".--clear-selling-points
- An image list is replaced by passing it, so
- Product info verbatim — every populated
- If the user wants to abort a running analysis: .
cawcut ad-video analyze cancel <id>
Material rights (state this once, when the user supplies their own image). Images uploaded through the CLI are not run through the material compliance check and are accepted as-is — there is no rights attestation step to walk through, unlike the web portal. That check exists to catch third-party logos and celebrity likeness; on this path the responsibility for holding the rights to the material sits with the user. Say so plainly in one sentence before uploading, so nobody assumes the tool vetted it. This applies only to new files the user supplies: images already in the project (crawler-extracted product images) were never subject to it.
- 收集产品 URL(Amazon、Shopify 或官网均可)。如果用户没有提供,应使用纯文本开放式提问。不要询问项目名称——后端会在分析或导出后根据产品信息自动命名项目。
- → 捕获
cawcut ad-video create --url "<url>" --json。创建时id是可选项(允许创建空草稿),但只要用户提供了 URL 就应传入——后续的--url无论如何都要求提供 URL。analyze run是可选项;应将其省略,由后端在分析后自动检测。只有用户明确要求特定标题时,才传入--category。--name - (不要使用
cawcut ad-video analyze run <id> --url "<the product URL from step 1>" --json;API 要求请求体中包含--wait),然后使用平台对应的运行器轮询:macOS/Linux 使用url,Windows 使用poll.sh <id> '.status' stage_completed 600 15。poll.ps1 <id> '.status' stage_completed 600 15 - 读取 并展示分析结果:
detail <id> --json- 逐字展示产品信息——每个已填充的 字段:
product_info、name、description(每项完整展示并编号)、selling_points[]、target_audience、brand_style、promotional_info、source_platform。不要总结、截断或翻译产品内容本身——这是用户的营销文案,必须按返回结果原样展示;只有周边标签和说明应遵循category。reply_language - 产品图片——先使用平台对应的 辅助脚本下载:
download_media。然后按类别展示(标签遵循<id> --kind product):reply_language- Logo()和主图(
logo_images):使用宿主的文件发送工具(例如 Claude 的primary_images)逐个以内联方式预览,并提供刚下载的本地路径(例如SendUserFile、~/Downloads/ad-video-<name>/product/logo-1.jpg)。Markdown 链接只会显示为可点击文本,无法呈现可见图片。primary-1.jpg - 其他产品图片():如果支持内联预览,应逐张列出,每张标记为
images[]并以内联方式发送图片;最多展示前 9 张。如果图片更多,应说明剩余数量(例如Product Image #n),并告知其余图片位于…and N–9 more文件夹。如果不支持内联预览,则以本地路径表格(<dest>/product/加路径)列出,并指向同一文件夹。# - 两种形式都不得包含 列——本地路径只是预览引用。这些文件用于让用户查看图片;绝不能将其回传给 CLI。 然后通过 AskUserQuestion 请求用户确认或修正:
asset_id
- Logo(
- → 继续第 ② 阶段。
✅ 看起来没问题 - → 收集修改内容,并且仅将发生变化的字段传给
✏️ 编辑产品信息:该命令会先读取项目,并保留所有未传入的字段。(API 会整体替换cawcut ad-video product set <id> …,因此只传product_info会错误地清除描述、图片和卖点。)如果 URL 或产品发生实质性变化,之后应重新运行--product-name。analyze run- 图片列表会因传入而被替换,因此 /
--logo/--primary-image必须传入完整的保留列表——这也是删除图片的方式。替换后的列表会丢弃自身选择,后端随后回退到默认值(--asset使用所有候选项,logo 和主图使用第一个候选项)。绝不能重新发送下载的文件:已持久化的引用会以--asset形式返回。asset_id - 只有当用户提供项目之外的新图片时,才传入本地路径;CLI 会将其作为全新素材上传(TTL 为 7 天)。
- 会清空描述,
--description ""会清空卖点——空列表是一种真实状态,并不表示“未设置”。--clear-selling-points
- 图片列表会因传入而被替换,因此
- 逐字展示产品信息——每个已填充的
- 如果用户希望中止正在运行的分析:。
cawcut ad-video analyze cancel <id>
素材权利(仅在用户提供自己的图片时说明一次)。 通过 CLI 上传的图片不会经过素材合规检查,而是被原样接受——此路径没有需要完成的版权确认步骤,而 Web 门户存在该步骤。该检查用于发现第三方 logo 和名人肖像;在这条路径上,持有素材权利的责任由用户承担。上传前应清楚地用一句话说明这一点,以免用户误以为工具已经审核过素材。这仅适用于用户提供的新文件;项目中已有的图片(爬虫提取的产品图片)从未经过该检查。
Stage ② — Config (all options are runtime-fetched, never hardcoded)
第 ② 阶段 — 配置(所有选项均在运行时获取,绝不硬编码)
-
→ read
cawcut ad-video options --video-ad-id <id> --json(publish_to, languages, aspect_ratios, resolutions, duration_ranges, video_types, storyboard_styles, video_models, defaults).video_settings -
Ask the axes in this order — video type first, because it decides both whether captions are a question at all and which model/resolution combinations are legal.
- video type — one option per entry in .
video_types - video model — only when is non-empty. Candidates are
video_models, defaultvideo_models[].value. The table that carries the list has three columns beyonddefaults.video_model: the model, its#, and a default mark onsupported_resolutions— the resolutions belong in view because picking a model is what decides which ones stay legal on the next axis. With exactly one candidate, state it in prose and carry on (one option is not a choice); with none, skip the axis entirely.defaults.video_model - resolution — default , then clamp to the chosen model's
defaults.resolution(the same list the server validates against). Never offer a resolution the model does not support:supported_resolutionsrejects it. When no model was chosen, offer the fullconfig setlist.resolutions - auto captions — only for and
product_with_voiceover. Captions are burned in from the voiceover/dialogue track, sotalking_videohas nothing to caption; do not ask, and passproduct_showcaseexplicitly (mirrors the web portal, which forces the value off for that type).--auto-captions false - platform — show each option's in its description (e.g.
default_aspect_ratio), language, duration, storyboard style (explain: hand-drawn adds a sketch pass before photo-realistic panels).TikTok — 9:16 - Aspect ratio is NOT a question. It is bound to the platform (mirrors the web portal, which has no ratio selector): take the picked option's
publish_toautomatically. Never ask for a ratio separately; only if the user explicitly overrides should you deviate from the platform default.default_aspect_ratio
Batch the axes into one AskUserQuestion call — one question per axis, at most four questions per call. A long axis (platform, language, video model) follows Interactive selection's overflow shape: the full list is a numbered table in the message body —plus what that axis actually carries: a platform's label with its#, a language's label, a model's label with itsdefault_aspect_ratioand default mark. The question itself is one short sentence, the popup holds the leading entries as options labelled with their number, and the entries past them are answered by number or name in the free-text field. Each axis keeps its own table and its own numbering — the answer goes into that axis's own field, so never ask for two numbers at once (supported_resolutions).1 + 10 - video type — one option per entry in
-
cawcut ad-video config set <id> --publish-to … --language … --aspect-ratio <the platform's default_aspect_ratio> --resolution … --duration-seconds … --video-type … --video-model … --storyboard-style … --auto-captions … --json
video_model-
→ 读取
cawcut ad-video options --video-ad-id <id> --json(video_settings、publish_to、languages、aspect_ratios、resolutions、duration_ranges、video_types、storyboard_styles、video_models)。defaults -
按以下顺序询问各维度——先询问视频类型,因为它既决定是否需要询问字幕,也决定哪些模型与分辨率组合合法。
- 视频类型——中的每一项对应一个选项。
video_types - 视频模型——**仅当 非空时询问。**候选项为
video_models,默认值为video_models[].value。承载该列表的表除defaults.video_model外还有三列:模型、#,以及supported_resolutions中的默认标记——分辨率必须显示,因为选择模型正是决定下一维度中哪些分辨率仍合法的原因。如果只有一个候选项,应在说明文字中直接给出并继续(一个选项不构成选择);如果没有候选项,则完全跳过该维度。defaults.video_model - 分辨率——默认值为 ,然后限制到所选模型的
defaults.resolution范围内(与服务器用于验证的列表相同)。绝不能提供模型不支持的分辨率,否则supported_resolutions会拒绝请求。没有选择模型时,提供完整的config set列表。resolutions - 自动字幕——**仅适用于 和
product_with_voiceover。**字幕会从旁白或对话音轨中烧录进画面,因此talking_video没有可添加字幕的内容;不要询问,并明确传入product_showcase(这与 Web 门户一致,后者会强制该类型关闭字幕)。--auto-captions false - 平台——在每个选项的说明中显示其 (例如
default_aspect_ratio),以及语言、时长、分镜风格(说明:手绘风格会在照片级画面之前增加草图处理步骤)。TikTok — 9:16 - **宽高比不是问题项。**它与平台绑定(与 Web 门户一致,后者没有宽高比选择器):应自动采用所选 选项的
publish_to。绝不能单独询问宽高比;只有用户明确覆盖时,才偏离平台默认值。default_aspect_ratio
将这些维度合并到一次 AskUserQuestion 调用中——每个维度一个问题,每次调用最多四个问题。长维度(平台、语言、视频模型)应遵循“交互式选择”的溢出形式:完整列表以编号表格显示在消息正文中——加该维度实际包含的信息:平台的#和label、语言的default_aspect_ratio、模型的label、label和默认标记。问题本身应是一句简短的话;弹窗将靠前条目作为选项显示,弹窗无法容纳的条目可通过自由文本输入。每个维度都有自己的表格和编号——答案应进入该维度自己的字段,因此绝不能同时要求输入两个编号(如supported_resolutions)。1 + 10 - 视频类型——
-
cawcut ad-video config set <id> --publish-to … --language … --aspect-ratio <the platform's default_aspect_ratio> --resolution … --duration-seconds … --video-type … --video-model … --storyboard-style … --auto-captions … --json
video_modelStage ③ — Idea
第 ③ 阶段 — 创意
- , poll until
cawcut ad-video idea run <id> --json.outputs.generated_marketing_messages.status === "succeeded" - Reveal up to eight, then stop. One run generates up to 16 candidates but hands back one page (4) at a time, so a fresh read shows only the first four. Read
detail, and calloutputs.generated_marketing_messages(re-readingcawcut ad-video idea load-more <id> --jsoneach time) until eight are visible ordetail— whichever comes first:has_more === false- only reveals already-generated candidates — no LLM call, no credit cost.
load-more - Eight is the ceiling, not a target. A project holding fewer shows fewer; once eight are visible you stop revealing even if is still true. This flow has no "one more batch" step.
has_more - State the number you are showing, and never imply the set is everything the project holds when it is not.
- Present them as a table, in full, verbatim — one row per candidate: , title, content. No summarizing, no paraphrasing, no truncating, no translating: this is the user's marketing copy and they are choosing between the whole set. Lead with the count (e.g. "Showing 8 candidates:"). The
#column is the popup's index — this table is what any number the popup could not hold is typed against.# - One decision, one question. The choice is a single AskUserQuestion — never a candidate question followed by a second one asking what to do with that answer. Four options, in this order:
- ,
1. <title>,2. <title>— the first three candidates, each labelled with its number3. <title> - → submit the idea text the user or agent produced with
✏️ Write my own(60–10000 chars, mirroring the web custom-idea limits). This follows the portal custom path: the CLI sends that text asidea select <id> --custom "<text>"withcustom_content; do not turn it into a generated-candidate selection or add a title wrapper.is_custom: true - a candidate from the fourth to the eighth is selected by typing its number or its title into the free-text field; name that in the question's own text, in , so the user answers from a set the question stated rather than guessing
reply_language
- On a pick: . Offer light edits to title/content before submitting.
cawcut ad-video idea select <id> --message-id <mm-N> --title "<title>" --content "<content>" --json - A fresh batch is user-initiated only. is not a menu entry: when the user asks for one in their own words ("换一批" / "regenerate"), run
idea regenerate+ poll — it clears the current selection, so warn first — then re-reveal (step 2) and re-ask (step 4).cawcut ad-video idea regenerate <id> --json
- ,轮询直到
cawcut ad-video idea run <id> --json。outputs.generated_marketing_messages.status === "succeeded" - 最多展示八项,然后停止。一次运行最多生成 16 个候选项,但 每次只返回一页(4 个),因此新读取时只能看到前四项。读取
detail,并调用outputs.generated_marketing_messages(每次都重新读取cawcut ad-video idea load-more <id> --json),直到已显示八项或detail,以先达到者为准:has_more === false- 只会显示已经生成的候选项——不会调用 LLM,也不会消耗 credits。
load-more - **八项是上限,而不是目标。**如果项目中只有更少的候选项,就显示实际数量;显示八项后,即使 仍为 true 也应停止。本流程没有“再加载一批”的步骤。
has_more - 应说明当前展示的数量;当项目仍持有更多候选项时,绝不能让用户误以为展示的是全部内容。
- 以表格形式完整、逐字展示候选项——每行一个候选项:、标题、内容。不要总结、改写、截断或翻译:这是用户的营销文案,用户需要在完整候选内容之间进行选择。以数量开头,例如“正在展示 8 个候选项:”。
#列是弹窗的索引——弹窗无法容纳的条目将以此表格中的编号作为输入依据。# - 一个决策,只问一个问题。选择必须是单次 AskUserQuestion——绝不能先询问候选问题,再用第二个问题询问如何处理第一个问题的答案。提供四个选项,顺序如下:
- 、
1. <title>、2. <title>——前三个候选项,每个都带编号。3. <title> - → 提交用户或 Agent 生成的创意文本,使用
✏️ 自行编写(60–10000 个字符,与 Web 自定义创意的限制一致)。这遵循门户的自定义路径:CLI 会将该文本作为idea select <id> --custom "<text>"发送,并设置custom_content;不要将其改为生成候选项选择,也不要添加标题包装。is_custom: true - 第四至第八个候选项可通过在自由文本字段中输入编号或标题进行选择。问题自身的文字应使用 ,并说明用户应从已列出的候选项中回答,避免用户猜测。
reply_language
- 选定后:。提交前可对标题或内容进行少量编辑。
cawcut ad-video idea select <id> --message-id <mm-N> --title "<title>" --content "<content>" --json - 新一批创意只能由用户主动触发。不是菜单项:当用户用自己的话提出要求时(“换一批”/“regenerate”),运行
idea regenerate并轮询——该操作会清除当前选择,因此必须先发出警告——然后重新显示候选项(第 2 步)并重新询问(第 4 步)。cawcut ad-video idea regenerate <id> --json
Stage ④ — Storyboard
第 ④ 阶段 — 分镜
-
(optional
cawcut ad-video storyboard run <id> --jsonfrom the options storyboard section), poll until--style-id— the 3600 deadline from the polling table, which is a give-up line and never a figure to quote.outputs.storyboard_plan.status === "succeeded" -
Read→
detail <id> --json. Show the plan as a concise shot table. The table contains the user-reviewable fields only; prompts remain in the stored plan but are not displayed:outputs.storyboard_plan.value- plan level, above the table: ,
titlesynopsis - per shot, in this order: (Shot #),
shot_no(Duration),duration(Shot Type),shot_type(Shot Description),shot_description(Voiceover — that is its business meaning),dialogue(Camera),camera(Light),light(SFX)sfx - Omit the Voiceover column for (the web portal hides it too — that type has no spoken track).
product_showcase - Never show ,
shot_prompt, or their statuses in the table or elsewhere in the reply. They are implementation fields, not review content.video_prompt
Then ask via AskUserQuestion:- → continue
✅ Looks good - → the script is a file in the project folder; the table is rendered from it. Run the platform-specific
✏️ Work on the scripthelper withscript_planonce — it writes the plan totake <id>and prints the paths. Edit that file, never a plan retyped from context. After every change, write the fields you touched into a small edits JSON and run the same helper with<dest>/storyboard/script.json: it applies them, lists what it applied, and prints the whole current review table. Say what changed, then paste that table into your reply text — the script's stdout is a tool result the user does not see; only what you write in your own message counts as shown. A diff alone makes them hold the rest of the script in their head. Nothing reaches the server until they confirm, so a half-formed idea costs no revision, no invalidation and no credit. When you customize any user-visible shot field, you must also update the hiddenshow <plan.json> <edits.json>and/orshot_promptin the same working copy according to the association table invideo_prompt; submit them but do not display them. The platform's own Prompt Generator is the alternative when you would rather not write them. The edits-file format, the prompt shapes, the trigger mapping and the server's limits are inreferences/script-editing.md— read it before the first edit.references/script-editing.md- Editable per-shot fields: ,
duration,shot_type,shot_description(the Voiceover column),dialogue,camera,light,sfx,shot_prompt. Never add or remove shots — the count comes from the generated script, and the portal's editor has no such action either. Rewriting a shot's content is the way to change it.video_prompt/shot_typeoption values come fromcamera→ storyboard section, never hardcoded. Everything else in the file (cawcut ad-video options --json,assets,character_refs, the prompt statuses) is carried through untouched — the script refuses to edit it.scene_ref - Save once: ; on
cawcut ad-video storyboard set <id> --base-revision <value.revision> --plan-file <dest>/storyboard/script.json --jsonre-read detail, take a fresh copy and re-apply the same edits, save again — the edits file is your record, so nothing is lost. The CLI checks the whole plan against the server's own409before sending it: a refusal names the shot and field, nothing was sent, and the shot at fault is often one you did not edit — fix that field and save again, never resend the same plan. Ifconstraintswas used, the save only starts asynchronous prompt generation: poll--update-promptsto.outputs.storyboard_plan.prompt_statusbefore treating either prompt as ready;succeededalone is not sufficient.storyboard_plan.status: succeeded - or the shot set changing invalidates the sketch, the panels and the final video: say so before the save, then run the invalidation protocol.
duration
- Editable per-shot fields:
- →
🔁 Regenerate the script+ poll. Warn before running it: this discards every edit — the script LLM writes a new script from the same idea and config. It is the only way back to a platform-written script; a plain re-run will not do it (unchanged inputs make it a no-op). Re-show the table and ask again.cawcut ad-video storyboard run <id> --force
- plan level, above the table:
-
Quote before every image run:→ AskUserQuestion to confirm the credit cost → immediately
cawcut ad-video image quote <id> --generation-type <type> --json(token TTL is 5 minutes — do not pause between confirm and run). See Quote timing.cawcut ad-video image run <id> --generation-type <type> --confirmation-token <token> --json -
Branch by storyboard style (from Stage ② config):
- : pass 1
hand_drawn_then_photo→ poll--generation-type hand_drawn→ preview the sketch (Media preview & downloads), AskUserQuestionstoryboard_sketch/✅ Continue to photo-realistic→ pass 2✏️ Edit script→ poll--generation-type photo_realisticstoryboard_image - : single
photo_realistic_onlyrun → poll--generation-type photo_realisticstoryboard_image
-
Generation-stage polls use the longest deadline in the polling table (3600). On success, preview the sketch/grid image per Media preview & downloads (platform-specific), then continue.
download_media <id> --kind storyboard
-
(可从选项的分镜部分传入
cawcut ad-video storyboard run <id> --json),轮询直到--style-id——轮询表中的 3600 是放弃等待的截止时间,只是停止线,绝不能作为时长向用户引用。outputs.storyboard_plan.status === "succeeded" -
读取→
detail <id> --json。以简洁的镜头表格展示计划。表格只包含供用户审核的字段;提示词仍保存在计划中,但不予展示:outputs.storyboard_plan.value- 在表格上方展示计划级信息:、
title。synopsis - 每个镜头按以下顺序展示:(镜头 #)、
shot_no(时长)、duration(镜头类型)、shot_type(镜头描述)、shot_description(旁白——这是其业务含义)、dialogue(镜头运动)、camera(光线)、light(音效)。sfx - 对于 ,省略旁白列(Web 门户也会隐藏该列——这种类型没有语音轨道)。
product_showcase - 绝不在回复中或其他位置展示 、
shot_prompt及其状态。它们是实现字段,不是审核内容。video_prompt
然后通过 AskUserQuestion 询问:- → 继续。
✅ 看起来没问题 - → 脚本是项目文件夹中的文件;表格由该文件渲染。使用平台对应的
✏️ 修改脚本辅助脚本执行一次script_plan——它会将计划写入take <id>并打印路径。应编辑该文件,绝不能根据上下文重新键入计划。每次更改后,将修改的字段写入一个小型 edits JSON,并使用同一辅助脚本执行<dest>/storyboard/script.json:它会应用修改、列出已应用的内容,并打印当前完整审核表。说明修改内容后,将该表粘贴到回复正文中——脚本的 stdout 是用户看不到的工具结果,只有自行写入消息中的内容才算展示。在用户确认前,任何内容都不会发送到服务器,因此格式不完整的创意不会产生修订、失效或 credits 消耗。**修改任何用户可见的镜头字段时,还必须在同一工作副本中根据show <plan.json> <edits.json>的关联表更新隐藏的references/script-editing.md和/或shot_prompt;可以提交,但不要展示。**edits 文件格式、提示词结构、触发映射和服务器限制位于video_prompt——首次编辑前必须读取。references/script-editing.md- 每个镜头可编辑的字段:、
duration、shot_type、shot_description(旁白列)、dialogue、camera、light、sfx、shot_prompt。绝不能添加或删除镜头——镜头数量来自生成的脚本,门户编辑器也没有此类操作。修改镜头内容是改变它的方式。video_prompt/shot_type的可选值来自camera的分镜部分,绝不能硬编码。文件中的其他所有内容(cawcut ad-video options --json、assets、character_refs、提示词状态)都会原样保留——脚本拒绝编辑这些内容。scene_ref - 只保存一次:;遇到
cawcut ad-video storyboard set <id> --base-revision <value.revision> --plan-file <dest>/storyboard/script.json --json时重新读取 detail,获取最新副本并重新应用相同修改,再次保存——edits 文件是修改记录,因此不会丢失任何内容。CLI 会在发送前根据服务器自身的409检查整个计划:如果被拒绝,会指出有问题的镜头和字段,而且不会发送任何内容;有问题的地方往往不是用户编辑过的镜头——应修复该字段后再次保存,绝不能重复发送同一计划。如果使用了constraints,保存只会启动异步提示词生成:必须轮询--update-prompts直到.outputs.storyboard_plan.prompt_status,才能将任一提示词视为就绪;仅凭succeeded并不足够。storyboard_plan.status: succeeded - 修改 或镜头集合会使草图、分镜画面和最终视频失效:保存前应告知用户,然后执行失效协议。
duration
- 每个镜头可编辑的字段:
- →
🔁 重新生成脚本并轮询。运行前必须警告:这会丢弃所有修改——脚本 LLM 会根据相同创意和配置编写新脚本。这是恢复平台生成脚本的唯一方式;普通重新运行不会有效果(输入未变时是 no-op)。重新展示表格并再次询问。cawcut ad-video storyboard run <id> --force
- 在表格上方展示计划级信息:
-
每次运行图片生成前都要先报价:→ 使用 AskUserQuestion 确认 credits 费用 → 立即执行
cawcut ad-video image quote <id> --generation-type <type> --json(token 的 TTL 为 5 分钟——确认与运行之间不要暂停)。参见“报价时机”。cawcut ad-video image run <id> --generation-type <type> --confirmation-token <token> --json -
根据第 ② 阶段的分镜风格进行分支:
- :先使用
hand_drawn_then_photo运行第 1 次 → 轮询--generation-type hand_drawn→ 预览草图(参见“媒体预览与下载”)→ 使用 AskUserQuestion 询问storyboard_sketch/✅ 继续生成照片级画面→ 使用第 2 次运行和✏️ 修改脚本→ 轮询--generation-type photo_realistic。storyboard_image - :使用
photo_realistic_only运行一次 → 轮询--generation-type photo_realistic。storyboard_image
-
生成阶段应使用轮询表中的最长截止时间(3600)。成功后,按照“媒体预览与下载”预览草图或网格图片(使用平台对应的),然后继续。
download_media <id> --kind storyboard
Stage ⑤ — Video
第 ⑤ 阶段 — 视频
- → AskUserQuestion confirm credits → immediately
cawcut ad-video video quote <id> --json(addcawcut ad-video video run <id> --confirmation-token <token> --jsononly when the user explicitly re-renders an unchanged result).--force - Poll until one of /
final_video/voiceover_final_videoistalking_video— the 3600 deadline from the polling table.succeeded - On success: download the video into the project folder with platform-specific and take the printed path (
download_media <id> --kind videois only for when you haven't triggered the run yet). See Media preview & downloads.cawcut ad-video video run <id> --wait --download - Report and the result URL, then hand off unconditionally to
local_pathwithcawcut-vn— no VN menu, no<local_path>commands of your own (decide-once: the receiving skill owns the VN decision and you never re-check).cawcut vn
- → 使用 AskUserQuestion 确认 credits → 立即执行
cawcut ad-video video quote <id> --json(仅当用户明确要求重新渲染未更改的结果时,才添加cawcut ad-video video run <id> --confirmation-token <token> --json)。--force - 轮询,直到 /
final_video/voiceover_final_video中的一个为talking_video——使用轮询表中的 3600 截止时间。succeeded - 成功后:使用平台对应的 将视频下载到项目文件夹,并获取打印出的路径(只有尚未触发运行时,才使用
download_media <id> --kind video)。参见“媒体预览与下载”。cawcut ad-video video run <id> --wait --download - 报告 和结果 URL,然后无条件移交给
local_path,并传入cawcut-vn——不显示 VN 菜单,也不自行执行任何<local_path>命令(一次决定原则:接收方 skill 负责 VN 决策,绝不重新检查)。cawcut vn
Media preview & downloads
媒体预览与下载
All media URLs the API returns (product images, storyboard sketch/grid, final video) are pre-signed — plain downloads them directly, no extra signing step. If one ever 403s, re-read and retry with the fresh URL (product image URLs are re-signed on every call).
curldetaildetailPreview priority (product logo/primary images, storyboard sketch/grid):
- If the host has a send-file-to-user tool (e.g. Claude's ), download the file first, then send it so the user previews inline in the chat.
SendUserFile - Otherwise the downloaded file (macOS) or just report the local path.
open
Always use the bundled downloader instead of hand-rolling curl/jq pipelines:
bash
undefinedAPI 返回的所有媒体 URL(产品图片、分镜草图或网格、最终视频)都是预签名的——可以直接使用普通 下载,无需额外签名步骤。如果某个 URL 返回 403,应重新读取 并使用最新 URL 重试(每次调用 都会重新签署产品图片 URL)。
curldetaildetail预览优先级(产品 logo 和主图、分镜草图或网格):
- 如果宿主提供向用户发送文件的工具(例如 Claude 的 ),应先下载文件,再发送文件,让用户在聊天中内联预览。
SendUserFile - 否则,在 macOS 上使用 打开下载的文件,或仅报告本地路径。
open
始终使用内置下载器,不要自行拼接 curl 或 jq 管道:
bash
undefinedmacOS/Linux
macOS/Linux
bash <base_dir>/scripts/download_media.sh <id> [--kind product|storyboard|video|all] [dest_dir]
bash <base_dir>/scripts/download_media.sh <id> [--kind product|storyboard|video|all] [dest_dir]
Windows PowerShell
Windows PowerShell
powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\download_media.ps1" <id> [-Kind product|storyboard|video|all] [-Destination <dir>]
Default dest: `~/Downloads/ad-video-<product-name>` (falls back to `ad-video-<id>` before the project is named). Layout: `product/` (logo + primary + additional images), `storyboard/` (sketch + panel grid), final video at the folder root. It prints every saved path — relay those paths to the user in `reply_language`.powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\download_media.ps1" <id> [-Kind product|storyboard|video|all] [-Destination <dir>]
默认目标目录:`~/Downloads/ad-video-<product-name>`(项目命名前会回退为 `ad-video-<id>`)。目录结构:`product/`(logo、主图和其他图片)、`storyboard/`(草图和分镜网格),最终视频位于文件夹根目录。脚本会打印每个保存路径——应使用 `reply_language` 将这些路径转达给用户。Invalidation semantics (mandatory — mirrors the web portal)
失效语义(强制执行——与 Web 门户一致)
After every mutation (, , , ):
product setconfig setidea selectstoryboard set- Immediately run . First distinguish cleared artifacts from retained-but-stale artifacts; never infer either from the project history.
cawcut ad-video detail <id> --json- A mutation to any creative config field — ,
--duration-seconds,--language,--video-type, or--storyboard-style— clears downstream content. If--publish-tono longer has a usable succeededoutputs.generated_marketing_messages/value, the previous Idea does not exist in the current project state. Do not ask whether to reuse, keep, or continue using that Idea; do not offer it as an option. Continue at Stage ③ withselection, poll it to success, select a fresh Idea, then generate a new storyboard.cawcut ad-video idea run <id> - Only an output that remains present with and
status: "succeeded"is retained-but-stale and eligible for the regeneration/later decision. A cleared output is neither stale nor reusable.need_regenerate === true
- A mutation to any creative config field —
- Collect every retained-but-stale with
outputs.<key>. If any exist: list the affected artifacts to the user in one message, then a single AskUserQuestion:need_regenerate === true- → re-run the affected stages in pipeline order (
🔄 Regenerate affected→storyboard run→image quote/run), quoting + confirming credits again per generation stagevideo quote/run - → continue without regenerating; note the stale artifacts
⏸ Later
- Never regenerate mid-edit — batch all user edits first, then regenerate once. A script save must only show affected outputs as needing regeneration; it must never trigger an image or video quote/run. Running /
image runhappens only after the user explicitly confirms that stage's fresh quote, and is rejected while an upstream output hasvideo run(need_regenerate=true).VIDEO_AD_NEEDS_REGENERATION
每次变更后(、、、):
product setconfig setidea selectstoryboard set- 立即运行 。首先区分已清除的产物与保留但已过期的产物;绝不能根据项目历史推断二者。
cawcut ad-video detail <id> --json- 修改任何创意配置字段——、
--duration-seconds、--language、--video-type或--storyboard-style——都会清除下游内容。如果--publish-to不再包含可用的、状态为 succeeded 的outputs.generated_marketing_messages/value,则之前的 Idea 并不存在于当前项目状态中。**不要询问是否复用、保留或继续使用该 Idea;也不要将其作为选项提供。**继续执行第 ③ 阶段:运行selection,轮询至成功,选择一个新 Idea,然后生成新的分镜。cawcut ad-video idea run <id> - 只有仍然存在、状态为 且
succeeded的输出,才属于保留但已过期,并可选择重新生成或稍后处理。已清除的输出既不是过期产物,也不能复用。need_regenerate === true
- 修改任何创意配置字段——
- 收集所有 的
need_regenerate === true。如果存在,应在一条消息中列出受影响产物,然后发出一次 AskUserQuestion:outputs.<key>- → 按管道顺序重新运行受影响阶段(
🔄 重新生成受影响内容→storyboard run→image quote/run),每个生成阶段都要重新报价并确认 credits。video quote/run - → 不重新生成,继续执行;同时记录这些产物已过期。
⏸ 稍后处理
- 绝不能在中途编辑时重新生成——应先批量完成所有用户修改,然后只重新生成一次。脚本保存只能显示需要重新生成的产物;绝不能触发图片或视频报价和运行。只有用户明确确认相应阶段的新报价后,才能运行 /
image run;当上游输出存在video run时,运行会被拒绝(need_regenerate=true)。VIDEO_AD_NEEDS_REGENERATION
Quote timing (mandatory)
报价时机(强制执行)
confirmation_tokenrunCheck the balance before asking to spend (mandatory). / also report , and alongside . Look at them before the confirmation question:
image quotevideo quotecredits_balancecredits_shortfallcredits_sufficientestimated_credits- → say the numbers plainly ("this needs X, your balance is Y, short by Z") and send the user to top up on the web portal. The confirmation menu should offer
credits_sufficient === false/💳 Top up creditsinstead of a routine confirm-and-run — do not walk a user into a charge they cannot pay for.⏸ Not now - , or the balance is unavailable (
credits_sufficient === true— the lookup is best-effort and must never block) → normal confirmation.credits_balance_error - Never present an insufficient balance as a generic failure after the fact. The whole point is to say it before the run.
- Topping up takes longer than the token's 5 minutes: on the user's return, re-quote → re-confirm → run. Never reuse the stale token.
confirmation_tokenrun请求消费前必须检查余额(强制执行)。 / 还会同时报告 、 和 ,以及 。确认问题前应先查看这些字段:
image quotevideo quotecredits_balancecredits_shortfallcredits_sufficientestimated_credits- → 清楚说明数字(“本次需要 X,你的余额为 Y,还差 Z”),并让用户前往 Web 门户充值。确认菜单应提供
credits_sufficient === false/💳 充值 credits,而不是常规的确认并运行选项——绝不能让用户在无法支付的情况下进入扣费流程。⏸ 暂不 - ,或者余额不可用(
credits_sufficient === true——余额查询是尽力而为的,绝不能因此阻塞)→ 正常确认。credits_balance_error - 绝不能将余额不足作为运行后的普通失败来呈现。重点是在运行前就明确告知用户。
- 充值耗时可能超过 token 的 5 分钟有效期:用户返回后,应重新报价 → 重新确认 → 再次运行。绝不能复用过期 token。
Polling (bundled script — mandatory)
轮询(内置脚本——强制执行)
Trigger without , then poll with the platform-specific bundled script. Never hand-roll polling loops — an ad-hoc loop corrupts the JSON under zsh (echo interprets escapes) and spins forever without matching a terminal state.
--waitecho "$var" | jq\nbash
bash <base_dir>/scripts/poll.sh <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]先不带 触发,然后使用平台对应的内置脚本轮询。绝不能自行编写轮询循环——临时的 循环在 zsh 下会因 解释 转义而破坏 JSON,并可能永远无法匹配到终止状态。
--waitecho "$var" | jqecho\nbash
bash <base_dir>/scripts/poll.sh <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]Windows PowerShell
Windows PowerShell
powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\poll.ps1" <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]
| Stage | json_path | success_value | timeout_s |
|---|---|---|---|
| ① analyze | `.status` | `stage_completed` | 600 |
| ③ idea | `.outputs.generated_marketing_messages.status` | `succeeded` | 600 |
| ④ plan | `.outputs.storyboard_plan.status` | `succeeded` | 3600 |
| ④ prompts | `.outputs.storyboard_plan.prompt_status` | `succeeded` | 600 |
| ④ sketch | `.outputs.storyboard_sketch.status` | `succeeded` | 3600 |
| ④ panels | `.outputs.storyboard_image.status` | `succeeded` | 3600 |
| ⑤ video | `.outputs.final_video.status // .outputs.voiceover_final_video.status // .outputs.talking_video.status` | `succeeded` | 3600 |
The Windows helper parses these dotted paths and `//` fallbacks internally; `json_path` does not require jq on Windows.
Exit codes: `0` success · `2` failed (report the output's `error` from `detail --json`; see `references/troubleshooting.md`) · `3` timeout → AskUserQuestion `⏳ Continue waiting` (run the script again) / `❌ Stop` (project persists; resume later with `cawcut ad-video detail <id>`) · `4` output unparseable → stop polling, run `cawcut ad-video detail <id> --json` once and inspect (auth/CLI issue).
**The script does not notify — the host's tool does.** `poll.sh` ends on an exit code and nothing else. Never raise an OS notification from the shell (`osascript`, `notify-send`, BurntToast), never add one back into the script, and never register a notification permission on the user's machine on your own initiative: a toast fired from a shell command is a different channel from the app's own notification, and it is not what the user asked for.powershell -ExecutionPolicy Bypass -File "<base_dir>\scripts\poll.ps1" <id> '<json_path>' <success_value> <timeout_s> [interval_s=30]
| 阶段 | json_path | success_value | timeout_s |
|---|---|---|---|
| ① 分析 | `.status` | `stage_completed` | 600 |
| ③ 创意 | `.outputs.generated_marketing_messages.status` | `succeeded` | 600 |
| ④ 计划 | `.outputs.storyboard_plan.status` | `succeeded` | 3600 |
| ④ 提示词 | `.outputs.storyboard_plan.prompt_status` | `succeeded` | 600 |
| ④ 草图 | `.outputs.storyboard_sketch.status` | `succeeded` | 3600 |
| ④ 分镜画面 | `.outputs.storyboard_image.status` | `succeeded` | 3600 |
| ⑤ 视频 | `.outputs.final_video.status // .outputs.voiceover_final_video.status // .outputs.talking_video.status` | `succeeded` | 3600 |
退出码:`0` 成功 · `2` 失败(报告 `detail --json` 输出中的 `error`;参见 `references/troubleshooting.md`)· `3` 超时 → 使用 AskUserQuestion 询问 `⏳ 继续等待`(再次运行脚本)/ `❌ 停止`(项目会保留;之后可通过 `cawcut ad-video detail <id>` 恢复)· `4` 输出无法解析 → 停止轮询,运行一次 `cawcut ad-video detail <id> --json` 并进行检查(认证或 CLI 问题)。
**脚本不发送通知——由宿主工具发送。**`poll.sh` 以退出码结束,不会产生其他输出。绝不能从 shell 发出操作系统通知(`osascript`、`notify-send`、BurntToast),也绝不能向脚本中添加此类通知,或主动在用户机器上注册通知权限:shell 命令触发的 toast 与应用自身的通知属于不同通道,而且这并不是用户所要求的。Telling the user how long it will take
告知用户预计耗时
Never estimate generation time yourself — and never turn a deadline into a duration. The server computes the runtime, and carries it as (milliseconds, present while a stage is running). reads it on its first tick and prints one line — relay that figure if there is one, and nothing more. If you need it directly, it is on any read.
detailestimated_running_mspoll.shestimated: about Nmdetail --jsonWhen the API gives no figure (the field is absent or zero — the lookup is best-effort and can miss), fall back to the order of magnitude, never to the deadline: these stages run on the order of minutes (the node estimates behind them are seconds to a couple of minutes), so say "about a few minutes" / "大概几分钟" and add that you will say the moment it lands. Do not say the timing is unknown, and do not hedge upward.
The column is a give-up deadline, not a runtime. Saying "at most about an hour" because the row says is the same error as inventing a range: it is the point at which the script stops waiting, typically an order of magnitude past the real runtime. The deadline never appears in a reply, not even hedged with "最多" / "at most".
timeout_s3600**绝不能自行估算生成时间,也绝不能把截止时间当作时长。**运行时间由服务器计算, 中会在阶段运行期间提供 字段(毫秒)。 会在首次轮询时读取该值,并打印一行 ——如果存在该值,应原样转达,不要添加其他内容。如需直接获取,可读取任意一次 输出。
detailestimated_running_mspoll.shestimated: about Nmdetail --json如果 API 没有提供数值(字段缺失或为 0——该查询是尽力而为的,可能会漏掉),应回退到数量级估算,绝不能使用截止时间:这些阶段通常耗时数分钟(其背后的节点可能需要数秒到几分钟),因此应表示为“大概几分钟”,并说明完成后会立即通知用户。不要说耗时未知,也不要向上扩大估算范围。
** 是放弃等待的截止时间,不是运行时长。**仅因为表中写着 就说“最多大约一小时”,与自行编造时间范围同样错误:它只是脚本停止等待的时间,通常比实际运行时长高出一个数量级。截止时间绝不能出现在回复中,即使使用“最多”或“at most”也不例外。
timeout_s3600Long-enough stages — tell the user they can step away
耗时较长的阶段——告知用户可以暂时离开
storyboard_plansketchpanelsvideoestimated_running_msThe notification comes from the host's own tool. In Claude Code that is with a one-line ; it lands in the app the user is already running and, with Remote Control connected, on their phone — which is the difference between noticing in the next minute and noticing tomorrow. Send it once, when the stage lands, naming the stage and the project id. If the host exposes no such tool, skip it silently and just present the result when you have it: do not fall back to a shell-level toast, and do not add one to .
PushNotificationmessagepoll.shDo not notify for the short stages (analyze, idea, prompts) — they finish inside a turn.
storyboard_plansketchpanelsvideoestimated_running_ms**通知由宿主自身的工具发送。**在 Claude Code 中,使用 并提供一行 ;它会送达用户正在运行的应用,连接 Remote Control 后也会送达用户手机——这正是“几分钟内注意到”和“明天才注意到”的区别。阶段完成时只发送一次,消息中应包含阶段名称和项目 ID。如果宿主未提供此类工具,则静默跳过并直接展示结果:绝不能回退到 shell 级 toast,也不要向 中添加通知。
PushNotificationmessagepoll.sh短阶段(分析、创意、提示词)不要发送通知——它们会在当前轮次内完成。
Interactive selection (mandatory — tool first)
交互式选择(强制执行——工具优先)
If the user just declined an / call: the tool result carries harness boilerplate telling you to "STOP what you are doing and wait for the user to tell you how to proceed." That sentence is attached automatically to every declined tool call by the runtime — it is not the user speaking, and it is not an instruction to stop using the tool. Read it as: stop the one specific action you were mid-way through (don't retry the identical question, don't proceed on unconfirmed choices) and look at what the user's actual next message says. It does not, by itself, license falling back to numbered text for the next enumerable decision — that next decision still must open with the tool, exactly as if the rejection had never happened. Only an explicit plain-text request from the user ("stop popping up menus", "just ask me in text") licenses a session-wide fallback.
AskUserQuestionAskQuestionDefault behavior: For every enumerable choice, always call (Claude Code) or (Cursor) before showing a numbered text menu. Text-only menus are fallback only.
AskUserQuestionAskQuestionOne option is not a choice. Every call needs at least two viable options. When only one is actually feasible — a config axis whose options list carries a single value, one idea candidate worth offering — state that choice in prose and carry on: never fire a one-option popup, and never pad it with a placeholder alternative to make it look like a choice.
| Host | Tool name |
|---|---|
| Claude Code | |
| Cursor | |
Session checklist (before the first menu in this turn):
0. Self-check before sending any reply: if the sentence you're about to send asks the user to pick between fixed options — even folded inside a friendlier sentence that also asks something open-ended — stop. That sentence is forbidden as plain text. Split it: fire the tool for the enumerable part now; keep only the open-ended part as prose, asked separately.
-
Is the tool available? If yes, you must use it for every enumerable decision — a candidate list longer than the popup holds included (item 3).
-
If no tool exists (CLI-only host), use numbered text in.
reply_language -
If the candidate count exceeds one question's four options (long category, model, or candidate lists), still open the tool, and give each part of the answer its own home:
- the full list → a numbered text table in the message body, one row per entry: plus the columns that entry actually carries — a platform's
#, a language's label, a model'sdefault_aspect_ratioand default mark, a candidate's title and content. Never inline the list into the question text: a 22-model roster pasted into the question header is unreadable and buries the question. The table and the call ship together.supported_resolutions - the question text → one short sentence naming the decision, plus the pointer for whatever the popup cannot hold (e.g. "entries 4–22 can be typed below by number or name", in ).
reply_language - the popup → the leading entries as options, labelled with their number, as many as fit while keeping a slot for any action that must stay clickable beside them (e.g. a write-your-own).
- the free-text field → whatever the popup could not hold: any later entry, by its number or its name.
The numbering is the contract between table and popup. Anything the popup can hold stays clickable; a typed answer is only for what it cannot. One question per decision — never a second question asking what to do with the answer to the first. - the full list → a numbered text table in the message body, one row per entry:
Forbidden while the tool is available: a numbered text menu instead of the call, "reply with the number or name" with no popup behind it, or asking the user to type enum values from memory. A long list's numbered table is not a text menu — it is the call's index, and it never ships alone.
Never hardcode option values. Platform, resolution, duration, video type, video model, storyboard style, language, and style IDs come from output this session — not from this document, not from training data.
cawcut ad-video options --json如果用户刚刚拒绝了一次 / 调用:工具结果会附带一段固定提示,大意是“停止当前操作,等待用户告知如何继续”。这句话由运行时自动附加到每一次被拒绝的工具调用中;它不是用户发言,也不是要求停止使用该工具。应将其理解为:停止当前正在执行的那一个具体操作(不要重试完全相同的问题,也不要在尚未确认选项时继续),然后查看用户实际发送的下一条消息。该提示本身并不允许在下一次可枚举决策中改用编号文本菜单;即使问题被拒绝,下一次决策仍必须像从未拒绝过一样先打开工具。只有用户明确以纯文本提出要求(“别再弹菜单了”、“直接用文字问我”)时,才允许在整个会话中使用纯文本回退。
AskUserQuestionAskQuestion**默认行为:**对于每个可枚举选项,始终在显示编号文本菜单之前调用 (Claude Code)或 (Cursor)。
AskUserQuestionAskQuestion**一个选项不构成选择。**每次调用至少需要两个可行选项。当实际上只有一个可行选项时——例如配置维度只有一个值,或只有一个值得提供的创意候选项——应在说明文字中直接给出该选项并继续:绝不能发起只有一个选项的弹窗,也绝不能为了凑数而添加无意义的替代选项。
| 宿主 | 工具名称 |
|---|---|
| Claude Code | |
| Cursor | |
会话检查表(在本轮第一次显示菜单前):
0. **发送任何回复前进行自检:**如果即将发送的句子要求用户从固定选项中进行选择——即使它被包装在更友好的句子中,同时还提出了开放式问题——也应停止。禁止以纯文本发送这种句子。应将其拆分:立即为可枚举部分调用工具;仅将开放式部分保留为单独的说明文字。
- 工具是否可用?如果可用,则本 skill 中每个可枚举决策必须使用该工具——包括候选项数量超过弹窗容量的情况(第 3 项)。
- 如果没有工具(纯 CLI 宿主),则使用 编写编号文本菜单。
reply_language - 如果候选项数量超过单个问题可容纳的四个选项(长类别、模型或候选项列表),仍应打开工具,并为答案的每个部分设置独立位置:
- 完整列表 → 消息正文中的编号文本表格,每行一个条目:加该条目实际包含的信息——平台的
#、语言的default_aspect_ratio、模型的label和默认标记、候选项的标题和内容。绝不能将列表内联到问题文本中:将 22 个模型的清单粘贴到问题标题中既难读,又会掩盖真正的问题。表格和调用必须一起发出。supported_resolutions - 问题文本 → 一句简短的话,说明需要做出的决定,并指出弹窗无法容纳的条目应如何输入(例如“可通过编号或名称输入第 4–22 项”,使用 )。
reply_language - 弹窗 → 将靠前条目作为可点击选项并标注编号,在不占用必须保留的操作项(例如自行编写)的情况下尽量填满。
- 自由文本字段 → 容纳弹窗无法显示的任何后续条目,可按编号或名称输入。
- 完整列表 → 消息正文中的编号文本表格,每行一个条目:
编号是表格与弹窗之间的约定。弹窗能够容纳的条目应保持可点击;只有弹窗无法容纳的条目才使用文本输入。一个问题只处理一个决策——绝不能先问完一个问题,再用第二个问题询问如何处理第一个问题的答案。
**工具可用时禁止:**用编号文本菜单代替调用、发送“回复编号或名称”但背后没有弹窗,或要求用户凭记忆输入枚举值。长列表的编号表格不是文本菜单——它是调用的索引,绝不能单独发送。
绝不能硬编码选项值。平台、分辨率、时长、视频类型、视频模型、分镜风格、语言和 style ID 均来自本次会话中 的输出——不能来自本文档或训练数据。
cawcut ad-video options --jsonUX Rules
UX 规则
- Be concise. Default output is the result URL and local path. No raw JSON dumps unless debugging (is for the polling script).
--json - Reply language: see the top-of-file rule — follow the user's language for every reply (including the first one and all AskUserQuestion questions/labels), and summarize CLI output in instead of pasting raw English.
reply_language - Do not call CawCut HTTP APIs with curl — the CLI handles auth and tokens.
- Choice-first — every enumerable decision (config axes, idea candidates, credit confirmations, regenerate confirmations) goes through AskUserQuestion/AskQuestion per Interactive selection.
- One project per request. If the user asks for several ad videos, finish one wizard before starting the next.
- V1 contract only. Do not consume or promise character/scene (V2) capabilities; they are not exposed to developer tokens.
- Credits: always quote + confirm before /
image run. Never spend credits without an explicit user confirmation in this session, and read the quote's balance figures first — an insufficient balance is stated before the confirmation question, not discovered by a failed run (see Quote timing).video run - Long stages (,
storyboard_plan,sketch,panels) take minutes, not seconds: say so once — quoting the server'svideo, or "a few minutes" when there is none (see Polling); never the deadline — tell the user they can step away, and let the notification bring them back. Don't narrate each poll tick.estimated_running_ms
- 保持简洁。默认输出结果 URL 和本地路径。除非用于调试,否则不要输出原始 JSON dump(用于轮询脚本)。
--json - 回复语言:参见文件顶部的规则——每次回复都应遵循用户语言(包括第一次回复及所有 AskUserQuestion 问题和标签),并在 中总结 CLI 输出,而不是粘贴原始英文。
reply_language - 不要使用 curl 调用 CawCut HTTP API——CLI 会处理认证和 token。
- 选择优先——每个可枚举决策(配置维度、创意候选项、credits 确认、重新生成确认)都应按照“交互式选择”使用 AskUserQuestion/AskQuestion。
- **每次请求只处理一个项目。**如果用户要求制作多个广告视频,应先完成一个向导,再开始下一个。
- **仅支持 V1 contract。**不得使用或承诺角色和场景(V2)能力;开发者 token 尚未开放这些能力。
- Credits:运行 /
image run前始终必须报价并确认。没有本次会话中的明确用户确认,绝不能消费 credits;并且必须先读取报价中的余额数据——余额不足应在确认问题之前告知用户,而不是运行失败后才发现(参见“报价时机”)。video run - 长阶段(、
storyboard_plan、sketch、panels)耗时以分钟计,而不是秒:应告知一次——引用服务器的video,没有时使用“大概几分钟”(参见“轮询”);绝不能使用截止时间——告知用户可以暂时离开,并由通知召回。不要逐次播报轮询状态。estimated_running_ms
CLI quick reference
CLI 快速参考
bash
cawcut ad-video create [--url <u>] [--name <n>] [--category <c>] [--json]
cawcut ad-video detail <id> [--json]
cawcut ad-video options [--video-ad-id <id>] [--language <l>] [--json]
cawcut ad-video product set <id> [--product-name …] [--description …] [--selling-point …] [--clear-selling-points] [--logo <ref>] [--primary-image <ref>] [--asset <ref>…] [--target-audience …] [--promo-*] [--json] # only the fields passed change
cawcut ad-video config set <id> [--publish-to …] [--language …] [--aspect-ratio …] [--resolution …] [--duration-seconds …] [--video-type …] [--video-model …] [--storyboard-style …] [--auto-captions …] [--json]
cawcut ad-video analyze run <id> --url <u> [--wait] [--json] · analyze cancel <id>
cawcut ad-video idea run <id> [--wait] [--json] · idea select <id> (--message-id <m> | --custom <text>) · idea regenerate <id> · idea load-more <id>
cawcut ad-video storyboard run <id> [--style-id <s>] [--force] [--wait] [--json] · storyboard set <id> --base-revision <n> (--plan <json> | --plan-file <path>) [--update-prompts --prompt-target <shot_id>:<fields>…] # run --force rewrites the script, discarding saved edits
cawcut ad-video image quote <id> --generation-type <hand_drawn|photo_realistic> · image run <id> [--confirmation-token <t>] [--generation-type <t>] [--force] [--wait] [--download]
cawcut ad-video video quote <id> · video run <id> [--confirmation-token <t>] [--force] [--wait] [--download]bash
cawcut ad-video create [--url <u>] [--name <n>] [--category <c>] [--json]
cawcut ad-video detail <id> [--json]
cawcut ad-video options [--video-ad-id <id>] [--language <l>] [--json]
cawcut ad-video product set <id> [--product-name …] [--description …] [--selling-point …] [--clear-selling-points] [--logo <ref>] [--primary-image <ref>] [--asset <ref>…] [--target-audience …] [--promo-*] [--json] # only the fields passed change
cawcut ad-video config set <id> [--publish-to …] [--language …] [--aspect-ratio …] [--resolution …] [--duration-seconds …] [--video-type …] [--video-model …] [--storyboard-style …] [--auto-captions …] [--json]
cawcut ad-video analyze run <id> --url <u> [--wait] [--json] · analyze cancel <id>
cawcut ad-video idea run <id> [--wait] [--json] · idea select <id> (--message-id <m> | --custom <text>) · idea regenerate <id> · idea load-more <id>
cawcut ad-video storyboard run <id> [--style-id <s>] [--force] [--wait] [--json] · storyboard set <id> --base-revision <n> (--plan <json> | --plan-file <path>) [--update-prompts --prompt-target <shot_id>:<fields>…] # run --force rewrites the script, discarding saved edits
cawcut ad-video image quote <id> --generation-type <hand_drawn|photo_realistic> · image run <id> [--confirmation-token <t>] [--generation-type <t>] [--force] [--wait] [--download]
cawcut ad-video video quote <id> · video run <id> [--confirmation-token <t>] [--force] [--wait] [--download]Errors
错误
| Symptom | Action |
|---|---|
| Follow the login steps in Step 0; retry the failed command once after |
| An upstream output is stale — re-run the flagged stage first (see Invalidation semantics) |
| Confirmation token expired/invalid | Re-quote, re-confirm with the user, run again (see Quote timing) |
| Revision mismatch — re-read |
| Another stage run is in flight — poll |
| Bad enum, unmet stage gate, or a resolution the chosen |
| Insufficient credits (from the quote) | |
| CLI not found | |
Full recovery trees: .
references/troubleshooting.md| 症状 | 操作 |
|---|---|
| 执行第 0 步中的登录流程;在 |
| 上游输出已过期——先重新运行被标记的阶段(参见“失效语义”) |
| 确认 token 过期或无效 | 重新报价、重新向用户确认,再次运行(参见“报价时机”) |
| 修订版本不匹配——重新读取 |
| 另一个阶段正在运行——轮询 |
| 无效枚举值、未满足阶段门槛,或所选 |
| 报价显示 credits 不足 | |
| 找不到 CLI | |
完整恢复流程见 。
references/troubleshooting.md