@remixmate/cli 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/manifest.json +2 -2
- package/dist/template/check.js +5 -2
- package/dist/template/commands.js +24 -13
- package/dist/template/constants.js +1 -1
- package/dist/template/docs.js +2 -0
- package/dist/template/init.js +4 -4
- package/dist/template/still-cases.js +1 -0
- package/dist/template/still-sheet.js +18 -0
- package/dist/template/still.js +4 -4
- package/dist/template/upgrade.js +3 -3
- package/dist/update-check.js +2 -0
- package/package.json +1 -1
- package/skills/template-authoring/SKILL.md +27 -15
- package/skills/template-authoring/version.json +1 -1
package/dist/manifest.json
CHANGED
package/dist/template/check.js
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
|
-
var
|
|
2
|
-
`);
|
|
1
|
+
var o=Object.defineProperty;var t=(r,e)=>o(r,"name",{value:e,configurable:!0});import i from"node:path";import{CHECK_ENTRY as c,STARTER_VERSION as a}from"./constants.js";import{compareVersions as u,readRulesVersion as p,requireProjectRoot as f,staleRulesHint as m}from"./project.js";import{runProcess as d}from"./proc.js";import{runUpgrade as R}from"./upgrade.js";const h="REMIXMATE_NO_AUTO_UPGRADE";async function w(r){const e=p(r);if(e&&u(e,a)>=0)return;const s=m(r);if(!e||process.env[h]==="1"){s&&process.stderr.write(s+`
|
|
2
|
+
`);return}try{await R({cwd:r,auto:!0})}catch(n){process.stderr.write(`⚠ 自动更新本地规则失败(${n.message.replace(/^❌\s*/,"").split(`
|
|
3
|
+
`)[0]}),继续使用旧规则。
|
|
4
|
+
`),s&&process.stderr.write(s+`
|
|
5
|
+
`)}}t(w,"ensureRulesCurrent");async function O(r={}){const e=f(r.cwd);await w(e);const s=[i.join(e,c)];return r.name&&s.push(r.name),r.json&&s.push("--json"),r.noWrite&&s.push("--no-write"),(await d(process.execPath,s,{cwd:e,stdio:r.stdio??"inherit"})).code}t(O,"runCheck");export{h as NO_AUTO_UPGRADE_ENV,w as ensureRulesCurrent,O as runCheck};
|
|
@@ -1,13 +1,17 @@
|
|
|
1
|
-
var
|
|
2
|
-
从 ${
|
|
1
|
+
var A=Object.defineProperty;var p=(i,s)=>A(i,"name",{value:s,configurable:!0});import{cleanDiagnosticText as f}from"../diagnostics.js";import{EXIT as r,SkillError as g}from"../errors.js";import{flagNumber as c,flagString as o,parseTemplateArgs as R,usageError as h}from"./args.js";import{runCheck as b}from"./check.js";import{runUpgrade as T}from"./upgrade.js";import{STARTER_PACKAGE as w,STARTER_VERSION as u}from"./constants.js";import{runInit as P}from"./init.js";import{runList as y}from"./list.js";import{runPreview as S}from"./preview.js";import{isPublishConfirmation as j,runPublish as _}from"./publish.js";import{MAX_FRAMES_PER_CASE as N,runStill as O}from"./still.js";import{cachedUpdateInfo as U,freshUpdateInfo as C,updateHint as E}from"../update-check.js";const z="template",x=["init","check","still","preview","publish","list","upgrade"],d={init:{flags:{force:"bool","skip-install":"bool"},maxPositionals:1,usage:`remixmate template init <dir> [--force] [--skip-install] [--json]
|
|
2
|
+
从 ${w}@${u} 创建模板工程并安装依赖(无需登录)。
|
|
3
3
|
目标目录非空时拒绝,除非 --force。--skip-install 只解压不执行 npm install。
|
|
4
4
|
`},check:{flags:{"no-write":"bool"},maxPositionals:1,usage:`remixmate template check [name] [--json] [--no-write]
|
|
5
5
|
在模板工程内运行本地校验(类型、schema、安全策略、props 契约、core 导出)。
|
|
6
6
|
不传 name 校验 templates/ 下全部模板;默认刷新 coreVersion/coreExports,--no-write 只检查。
|
|
7
|
-
`},still:{flags:{frames:"string","composition-id":"string",scale:"string","out-dir":"string"},maxPositionals:1,usage:`remixmate template still <name> [--
|
|
8
|
-
用工程内的 Remotion 在本地渲染关键帧 PNG
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
`},still:{flags:{scenes:"string",frames:"string","composition-id":"string",scale:"string","out-dir":"string","no-sheet":"bool"},maxPositionals:1,usage:`remixmate template still <name> [--scenes 1,3,cover] [--frames settled,intro] [--composition-id ID] [--scale N] [--out-dir DIR] [--no-sheet] [--json]
|
|
8
|
+
用工程内的 Remotion 在本地渲染关键帧 PNG(无需登录、不占平台资源),并拼成带标注的对比图,供用户确认画面。
|
|
9
|
+
默认:dsl-example.json 的每个场景(外加没有场景用到的 composition,如封面),按场景数分配帧:
|
|
10
|
+
1 个场景 → intro / middle(超过 30 秒为 33% / 67%)/ settled / final;2–3 个 → intro / settled;≥4 个 → settled。
|
|
11
|
+
同一场景内完全相同的帧只保留一张。template.json 的 previewHints.keyframes 可为指定场景声明关键帧。
|
|
12
|
+
--scenes:场景序号(从 1 开始)、purpose 或场景 id,逗号分隔;改完代码后只重截改动的场景。
|
|
13
|
+
--frames:settled / intro / middle / final / start / end / 百分比(33%)/ 帧号 / 秒数(2.5s),作用于每个选中的场景,每场景最多 ${N} 帧。
|
|
14
|
+
默认输出到 output/stills/<name>/(拼图为 <name>-sheet-N.png,每页 12 格);--scale 0.5 可缩小单张图片。
|
|
11
15
|
`},preview:{flags:{"props-json":"string","duration-sec":"string","composition-id":"string","timeout-sec":"string"},maxPositionals:1,usage:`remixmate template preview <name> [--props-json JSON] [--duration-sec N] [--composition-id ID] [--timeout-sec N] [--json]
|
|
12
16
|
先 check,再把模板提交到远程渲染器出预览 mp4(需登录)。
|
|
13
17
|
样例数据取自模板的 dsl-example.json,可用 --props-json 覆盖(传数组 = 每个元素一页)。
|
|
@@ -20,9 +24,11 @@ var k=Object.defineProperty;var m=(n,s)=>k(n,"name",{value:s,configurable:!0});i
|
|
|
20
24
|
`},list:{flags:{},maxPositionals:0,usage:`remixmate template list [--json]
|
|
21
25
|
列出你发布的私有模板(需登录)。
|
|
22
26
|
`},upgrade:{flags:{"dry-run":"bool","with-docs":"bool",force:"bool"},maxPositionals:0,usage:`remixmate template upgrade [--dry-run] [--with-docs] [--force] [--json]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
27
|
+
把工程的本地校验规则与工作流(.remixmate/)更新到 ${w}@${u}(无需登录)。
|
|
28
|
+
通常不用手动运行:check / still / preview / publish 发现规则过期会自动更新(REMIXMATE_NO_AUTO_UPGRADE=1 关闭)。
|
|
29
|
+
没改过的 AGENTS.md / CLAUDE.md 一起更新,改过的保留、新版写 *.new;--with-docs 强制覆盖(旧文件存为 *.orig)。
|
|
30
|
+
不改动 templates/、src/、examples/、package.json。
|
|
31
|
+
`}};function I(){return`Usage: remixmate template <command> [options]
|
|
26
32
|
|
|
27
33
|
Commands:
|
|
28
34
|
init <dir> 创建模板工程(无需登录)
|
|
@@ -34,8 +40,13 @@ Commands:
|
|
|
34
40
|
upgrade 更新工程的本地校验规则(无需登录)
|
|
35
41
|
|
|
36
42
|
Common options: --json --token <PrivToken> --api-base-url <url>
|
|
37
|
-
环境变量:AB_RENDER_URL(渲染服务地址)、REMIXMATE_STARTER_TARBALL(init 使用本地 starter
|
|
43
|
+
环境变量:AB_RENDER_URL(渲染服务地址)、REMIXMATE_STARTER_TARBALL(init 使用本地 starter 包)、
|
|
44
|
+
REMIXMATE_NO_AUTO_UPGRADE=1(不自动更新工程规则)、REMIXMATE_NO_UPDATE_CHECK=1(不检查 cli 新版本)
|
|
38
45
|
运行 remixmate template <command> --help 查看子命令说明。
|
|
39
|
-
`}
|
|
40
|
-
|
|
41
|
-
`),r}
|
|
46
|
+
`}p(I,"templateUsage");function v(i){const[s,...e]=i;if(!s||s==="--help"||s==="-h"||s==="help")return{sub:"help",args:{positionals:[],flags:{}}};if(!x.includes(s))throw h(`未知子命令: template ${s}(可选:${x.join(" | ")})`);const t=d[s];return{sub:s,args:R(e,t.flags,{maxPositionals:t.maxPositionals})}}p(v,"parseTemplateCommand");async function M(i,s){const{flags:e,positionals:t}=s,n=e.json===!0,a=o(e,"token"),l=o(e,"api-base-url");switch(i){case"init":{if(!t[0])throw h("缺少目标目录:remixmate template init <dir>");const m=await C();return m&&process.stderr.write(`${E(m)}
|
|
47
|
+
当前 cli 会用 starter ${u} 创建工程,可能不是最新的工作流与校验规则。建议先升级 cli 再 init。
|
|
48
|
+
`),await P({dir:t[0],force:e.force===!0,json:n,skipInstall:e["skip-install"]===!0,...m?{cliUpdate:m}:{}}),r.OK}case"check":return b({name:t[0],json:n,noWrite:e["no-write"]===!0});case"still":return await O({name:t[0],scenes:o(e,"scenes"),noSheet:e["no-sheet"]===!0,frames:o(e,"frames"),compositionId:o(e,"composition-id"),scale:c(e,"scale"),outDir:o(e,"out-dir"),json:n}),r.OK;case"preview":return await S({name:t[0],propsJson:o(e,"props-json"),durationSec:c(e,"duration-sec"),compositionId:o(e,"composition-id"),timeoutSec:c(e,"timeout-sec"),json:n,token:a,apiBaseUrl:l}),r.OK;case"publish":{const m=await _({name:t[0],displayName:o(e,"name"),dryRun:e["dry-run"]===!0,yes:e.yes===!0,skipPreviewCheck:e["skip-preview-check"]===!0,json:n,token:a,apiBaseUrl:l});return j(m)?r.NEEDS_CONFIRMATION:r.OK}case"list":return await y({json:n,token:a,apiBaseUrl:l}),r.OK;case"upgrade":return await T({dryRun:e["dry-run"]===!0,withDocs:e["with-docs"]===!0,force:e.force===!0,json:n}),r.OK}}p(M,"dispatch");function k(){try{const i=U();i&&process.stderr.write(`
|
|
49
|
+
${E(i)}
|
|
50
|
+
`)}catch{}}p(k,"printCachedUpdateHint");async function Q(i){const s=i.includes("--json");let e;try{const t=v(i);if(e=t.sub,e==="help")return process.stdout.write(I()),r.OK;if(t.args.flags.help)return process.stdout.write("Usage: "+d[e].usage),r.OK;const n=await M(e,t.args);return e!=="init"&&k(),n}catch(t){const n=t instanceof g?t.exitCode:r.ERROR,a=t instanceof g?t.message:`❌ unexpected error: ${t.stack??String(t)}`;return process.stderr.write(f(a,8192)+`
|
|
51
|
+
`),n===r.USAGE&&e&&e!=="help"&&process.stderr.write("Usage: "+d[e].usage),e&&e!=="help"&&e!=="init"&&k(),s&&process.stdout.write(JSON.stringify({ok:!1,exitCode:n,error:f(a.replace(/^❌\s*/,""),2048)})+`
|
|
52
|
+
`),n}}p(Q,"runTemplateCommand");export{d as SUB_SPECS,x as TEMPLATE_SUBCOMMANDS,z as TEMPLATE_VERB,v as parseTemplateCommand,Q as runTemplateCommand,I as templateUsage};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
const t="@remixmate/template-starter",e="0.4.
|
|
1
|
+
const t="@remixmate/template-starter",e="0.4.2",E=".remixmate/version.json",o=".remixmate",_=["AGENTS.md","CLAUDE.md"],r="REMIXMATE_STARTER_TARBALL",s=".remixmate/check/index.mjs",R=".remixmate.json",x=".remixmate-state",n="output/stills",p="https://api-render.remixmate.ai",T=1280,c=720,A=60,S=["__pycache__","node_modules","dist",".DS_Store","*.test.*","*.spec.*"];export{s as CHECK_ENTRY,p as DEFAULT_RENDER_BASE_URL,_ as DOC_FILES,R as IDENTITY_FILE,T as PREVIEW_MAX_LONG_EDGE,A as PREVIEW_MAX_SECONDS,c as PREVIEW_MAX_SHORT_EDGE,o as RULES_DIR,E as RULES_VERSION_FILE,t as STARTER_PACKAGE,r as STARTER_TARBALL_ENV,e as STARTER_VERSION,x as STATE_DIR,n as STILLS_DIR,S as TAR_EXCLUDES};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var j=Object.defineProperty;var r=(e,t)=>j(e,"name",{value:t,configurable:!0});import{createHash as D}from"node:crypto";import{existsSync as l,readFileSync as p,rmSync as w,writeFileSync as d}from"node:fs";import u from"node:path";import{DOC_FILES as g,RULES_VERSION_FILE as S}from"./constants.js";const h=r(e=>D("sha256").update(e).digest("hex"),"sha256");function O(e,t,n,o=!1){return e===null?"created":e===t?"unchanged":n===void 0?"replaced-backup":h(e)===n?"replaced":o?"replaced-backup":"kept-modified"}r(O,"planDoc");function N(e,t,n,o={}){const i=[],m={};for(const f of g){const k=u.join(t,f);if(!l(k))continue;const a=p(k,"utf8"),c=u.join(e,f),x=l(c)?p(c,"utf8"):null,y=n?.[f],s=O(x,a,y,o.force);i.push({file:f,action:s}),m[f]=s==="kept-modified"?y:h(a),!o.dryRun&&(s==="replaced-backup"&&d(`${c}.orig`,x,"utf8"),s==="kept-modified"?d(`${c}.new`,a,"utf8"):(s!=="unchanged"&&d(c,a,"utf8"),w(`${c}.new`,{force:!0})))}return{outcomes:i,hashes:m}}r(N,"applyDocs");function _(e){const t={};for(const n of g){const o=u.join(e,n);l(o)&&(t[n]=h(p(o,"utf8")))}return t}r(_,"hashDocs");function b(e){try{return JSON.parse(p(u.join(e,S),"utf8"))}catch{return{}}}r(b,"readMarker");function R(e){const t=b(e).docHashes;if(!t||typeof t!="object")return null;const n={};for(const[o,i]of Object.entries(t))typeof i=="string"&&(n[o]=i);return n}r(R,"readDocHashes");function A(e,t){const n=b(e);d(u.join(e,S),JSON.stringify({...n,docHashes:t},null,2)+`
|
|
2
|
+
`,"utf8")}r(A,"writeDocHashes");const E={unchanged:"已是最新",created:"已创建",replaced:"已更新","replaced-backup":"已更新(旧文件备份为 *.orig)","kept-modified":"你改过,保留原样;新版本写到 *.new,需要时合并"};function C(e){return`${e.file} ${E[e.action].replace(/\*/g,e.file)}`}r(C,"describeDoc");export{N as applyDocs,C as describeDoc,_ as hashDocs,O as planDoc,R as readDocHashes,h as sha256,A as writeDocHashes};
|
package/dist/template/init.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
var x=Object.defineProperty;var m=(e,
|
|
1
|
+
var x=Object.defineProperty;var m=(e,n)=>x(e,"name",{value:n,configurable:!0});import{existsSync as f,mkdirSync as k,mkdtempSync as T,readdirSync as E,rmSync as u,statSync as N}from"node:fs";import $ from"node:os";import o from"node:path";import{EXIT as c,SkillError as a}from"../errors.js";import{emitProgress as p}from"../progress.js";import{RULES_VERSION_FILE as g,STARTER_PACKAGE as R,STARTER_TARBALL_ENV as A,STARTER_VERSION as j}from"./constants.js";import{hashDocs as U,writeDocHashes as G}from"./docs.js";import{Output as b}from"./project.js";import{runNpm as y,runProcess as v}from"./proc.js";import{skillInstallHint as C}from"../install.js";function I(e){if(!f(e))return!1;if(!N(e).isDirectory())throw new a(`❌ ${e} 已存在且不是目录`,c.USAGE);return E(e).length>0}m(I,"isNonEmptyDir");async function _(e,n){const l=(process.env[A]??"").trim();if(l){const i=o.resolve(l);if(!f(i))throw new a(`❌ ${A} 指向的文件不存在: ${i}`,c.USAGE);return n.info(`使用本地 starter 包: ${i}`),{file:i,label:`local:${o.basename(i)}`}}const t=`${R}@${j}`;n.info(`下载 ${t} …`);const r=await y(["pack",t,"--json","--silent"],{cwd:e,stdio:"pipe"});if(r.code!==0){const i=r.stderr.trim().split(`
|
|
2
2
|
`).slice(-5).join(`
|
|
3
|
-
`);throw new a(`❌ npm pack ${
|
|
4
|
-
${
|
|
5
|
-
换一个空目录,或确认可以覆盖后加 --force。`,
|
|
3
|
+
`);throw new a(`❌ npm pack ${t} 失败(退出码 ${r.code})。检查网络与 npm registry 配置。
|
|
4
|
+
${i}`,c.BACKEND_UNREACHABLE)}let s;try{const i=JSON.parse(r.stdout);i[0]?.filename&&(s=o.join(e,i[0].filename))}catch{}if(s??=E(e).filter(i=>i.endsWith(".tgz")).map(i=>o.join(e,i))[0],!s||!f(s))throw new a("❌ npm pack 未产出 tarball");return{file:s,label:t}}m(_,"obtainStarterTarball");async function X(e){const n=new b(!!e.json);if(!e.dir)throw new a("❌ 缺少目标目录:remixmate template init <dir>",c.USAGE);const l=e.dir==="~"||/^~[\\/]/.test(e.dir)?o.join($.homedir(),e.dir.slice(1)):e.dir,t=o.resolve(l);if(I(t)&&!e.force)throw new a(`❌ 目标目录非空: ${t}
|
|
5
|
+
换一个空目录,或确认可以覆盖后加 --force。`,c.USAGE);const r=T(o.join($.tmpdir(),"remixmate-starter-"));try{p({phase:"template-init:fetch",progress:.1});const{file:s,label:i}=await _(r,n);p({phase:"template-init:extract",progress:.4}),k(t,{recursive:!0});const d=await v("tar",["-xzf",s,"-C",t,"--strip-components=1"],{stdio:"pipe"});if(d.code!==0)throw new a(`❌ 解压 starter 失败(tar 退出码 ${d.code}): ${d.stderr.trim().slice(0,300)}`);f(o.join(t,g))&&G(t,U(t));let w=!1;if(!e.skipInstall){p({phase:"template-init:install",progress:.6}),n.info("安装依赖(npm install)…");const S=await y(["install","--no-audit","--no-fund"],{cwd:t,stdio:"stderr"});if(S.code!==0)throw new a(`❌ npm install 失败(退出码 ${S.code})。工程文件已解压到 ${t},修复后在该目录重跑 npm install。`);w=!0}p({phase:"template-init:done",progress:1}),D(n,t,w);const h={ok:!0,dir:t,starter:i,installed:w,...e.cliUpdate?{cliUpdate:e.cliUpdate}:{}};return n.result({...h}),h}finally{u(r,{recursive:!0,force:!0})}}m(X,"runInit");function D(e,n,l){const t=/^[\w@%+=:,./\\~-]+$/.test(n)?n:`"${n}"`;e.info(""),e.info(`✅ 模板工程已创建: ${n}`),e.info(""),e.info("下一步:"),e.info(` cd ${t}`),l||e.info(" npm install"),e.info(" npm run dev # 打开 Remotion Studio 本地预览"),e.info(" remixmate template check <name> # 本地校验(无需登录)"),e.info(" remixmate template still <name> # 本地渲染关键帧 PNG,确认画面(无需登录)"),e.info(" remixmate template preview <name> # 远程渲染预览 mp4(需登录)"),e.info(" remixmate template publish <name> # 打印待发布摘要;确认后加 --yes 发布(需登录)"),e.info(""),e.info("在本地 agent 里使用:"),e.info(" Codex / WorkBuddy 在工程目录打开,agent 会读取 AGENTS.md"),e.info(" Claude Code 在工程目录打开,agent 会读取 CLAUDE.md(与 AGENTS.md 内容相同)"),e.info(" TraeWork 只认项目级 skill:在工程目录执行一次 remixmate install --project"),e.info(" 其它宿主若还没装 skill:remixmate install"),e.info(" 当前会话不在工程目录里时:先读 "+o.join(n,"AGENTS.md")+",或在该目录重开会话");let r=null;try{r=C()}catch{}r&&(e.info(""),e.info(r))}m(D,"printNextSteps");export{_ as obtainStarterTarball,X as runInit};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S=Object.defineProperty;var u=(r,i)=>S(r,"name",{value:i,configurable:!0});import{usageError as m}from"./args.js";import{buildSubtitleSegments as b}from"./preview.js";const d=30,$=5,y=6,L=80;function g(r,i){const e=new Set;let t=r[i];for(;t?.$ref;){if(e.has(t.$ref))return{};e.add(t.$ref),t=r[t.$ref]}return t??{}}u(g,"resolveSlot");function A(r,i){return i.split(".").reduce((e,t)=>e&&typeof e=="object"?e[t]:void 0,r)}u(A,"getPath");function M(r,i){const e={};for(const[t,o]of Object.entries(i??{})){if(!o||typeof o.from!="string")continue;const n=o.from;if(n==="textLayers"){const f=(r.textLayers??[]).find(s=>s.role===o.role);f?.content!==void 0&&(e[t]=f.content)}else{const f=A(r,n);f!==void 0&&(e[t]=f)}}return e}u(M,"applyExtractors");function F(r,i){return typeof i?.duration=="number"&&i.duration>0?i.duration:r.supportedDurations?.min??$}u(F,"sceneSeconds");function x(r,i,e,t,o,n){const f=Math.max(1,Math.round(F(r,e)*d)),s=r.slotMapping??{},a=g(s,n),c=a.compositionId?a:g(s,t.slot),p=e?M(e,t.slot==="cover"?void 0:c.propExtractors):{};e?.customPayload&&(p.templateData=e.customPayload);const h={sceneId:e?.id??`case-${o}`,compositionId:t.compositionId,startFrame:0,endFrame:f,durationFrames:f,startTime:0,endTime:f/d,props:p,subtitleSegments:b(e,f),transition:{type:i?.transitions?.default??"none",durationFrames:Math.round((i?.transitions?.duration??0)*d)}};return{index:o,purpose:n,sceneId:typeof e?.id=="string"?e.id:null,compositionId:String(t.compositionId),durationFrames:f,props:{entry:h,resolvedAssets:Array.isArray(i?.assets)?i.assets:[],typography:r.defaultTypography}}}u(x,"buildCase");function T(r,i){const e=Array.isArray(r.compositions)?r.compositions:[];if(e.length===0)return[];const t=Array.isArray(i?.scenes)?i.scenes:[],o=r.slotMapping??{},n=u(s=>{const a=s?g(o,s).compositionId:void 0;return e.find(c=>c.compositionId===a)??e.find(c=>c.slot===s)??e[0]},"compFor"),f=t.map((s,a)=>x(r,i,s,n(s.purpose),a+1,String(s.purpose??"scene")));for(const s of e){if(f.some(c=>c.compositionId===s.compositionId))continue;const a=t.find(c=>c.purpose===s.slot)??t[0];f.push(x(r,i,a,s,f.length+1,String(s.slot??"composition")))}return f}u(T,"enumerateCases");function D(r,i,e){let t=r;if(e&&(t=t.filter(n=>n.compositionId===e),t.length===0))throw m(`composition "${e}" 不在该模板中。可选: ${[...new Set(r.map(n=>n.compositionId))].join(", ")}`);if(!i||i.trim()==="all")return t;const o=[];for(const n of i.split(",").map(f=>f.trim()).filter(Boolean)){const f=/^\d+$/.test(n)?t.filter(s=>s.index===Number(n)):t.filter(s=>s.purpose===n||s.sceneId===n);if(f.length===0)throw m(`--scenes 里的 '${n}' 不存在。可选:1–${r.length},或 ${[...new Set(r.map(s=>s.purpose))].join(" / ")}`);for(const s of f)o.includes(s)||o.push(s)}return o.sort((n,f)=>n.index-f.index)}u(D,"selectCases");function l(r,i){const e=i-1,t=u(n=>Math.min(Math.max(0,Math.round(n)),e),"clamp");if(typeof r=="number")return t(r*d);const o=r.trim();if(o==="start")return 0;if(o==="end")return e;if(o==="intro")return t(Math.min(1.5*d,i*.2));if(o==="middle"||o==="mid")return t(e/2);if(o==="settled")return t(Math.min(i*.85,e-d/2));if(o==="final")return t(e-d*.2);if(/^\d+(\.\d+)?%$/.test(o))return t(Number(o.slice(0,-1))/100*e);if(/^\d+(\.\d+)?s$/.test(o))return t(Number(o.slice(0,-1))*d);if(/^\d+$/.test(o))return t(Number(o));throw m(`帧取值不合法: '${o}'(可用 settled / intro / middle / final / start / end / 百分比如 33% / 帧号 / 秒数如 2.5s)`)}u(l,"anchorFrame");function I(r,i){const e=r.split(",").map(o=>o.trim()).filter(Boolean);if(e.length===0)throw m("--frames 不能为空");const t=[];for(const o of e){const n=l(o,i);t.some(f=>f.frame===n)||t.push({label:o,frame:n})}if(t.length>y)throw m(`--frames 每个场景最多 ${y} 帧,收到 ${t.length} 帧`);return t}u(I,"parseFrameSpec");function P(r,i,e=()=>{}){const t=r.previewHints?.keyframes;if(t===void 0)return[];if(!Array.isArray(t))return e(" ⚠ previewHints.keyframes 应为数组,已忽略"),[];const o=[];return t.forEach((n,f)=>{const s=n?.scene===void 0?1:n.scene,a=n?.at===void 0?"settled":n.at,c=typeof a=="number"?a>=0:typeof a=="string";if(!Number.isInteger(s)||s<1||s>i||!c){e(` ⚠ previewHints.keyframes[${f}] 无效(scene 应为 1–${i},at 为锚点或秒数),已忽略`);return}try{l(a,2)}catch{e(` ⚠ previewHints.keyframes[${f}].at='${a}' 无法识别,已忽略`);return}o.push({scene:s,at:a,...typeof n.label=="string"?{label:n.label}:{},...typeof n.expect=="string"?{expect:n.expect}:{}})}),o}u(P,"readDeclaredKeyframes");const w=30*d;function E(r,i){return r>=4?["settled"]:r>=2?["intro","settled"]:["intro",...i>w?["33%","67%"]:["middle"],"settled","final"]}u(E,"defaultAnchors");function j(r,i,e,t=r.length){const o=new Map;for(const n of r){if(i){o.set(n,I(i,n.durationFrames));continue}const f=e.filter(a=>a.scene===n.index);if(f.length===0){const a=[];for(const c of E(t,n.durationFrames)){const p=l(c,n.durationFrames);a.some(h=>h.frame===p)||a.push({label:c,frame:p})}o.set(n,a);continue}const s=[];for(const a of f){const c=l(a.at,n.durationFrames);s.some(p=>p.frame===c)||s.push({label:a.label??String(a.at),frame:c,...a.expect?{expect:a.expect}:{}})}o.set(n,s.slice(0,y))}return o}u(j,"planFrames");export{d as FPS,y as MAX_FRAMES_PER_CASE,L as MAX_TOTAL_STILLS,l as anchorFrame,E as defaultAnchors,T as enumerateCases,I as parseFrameSpec,j as planFrames,P as readDeclaredKeyframes,D as selectCases};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
var T=Object.defineProperty;var m=(t,e)=>T(t,"name",{value:e,configurable:!0});import{mkdirSync as k,mkdtempSync as y,rmSync as S,writeFileSync as v}from"node:fs";import H from"node:os";import h from"node:path";import{STATE_DIR as R}from"./constants.js";import{runProcess as x}from"./proc.js";const $=12,f=32,d=24,C=56,E=92;function W(t,e,a){const o=e/a,c=o<.8?6:o<=1.25?4:3,i=o<.8?360:o<=1.25?520:720,r=Math.max(1,Math.min(t,c)),s=i,n=Math.round(s/o),l=Math.max(1,Math.ceil(t/r)),p=f*2+r*s+(r-1)*d,u=f*2+C+l*(n+E)+(l-1)*d;return{cols:r,thumbW:s,thumbH:n,width:F(p),height:F(u)}}m(W,"sheetLayout");const F=m(t=>Math.ceil(t/2)*2,"even"),j=`import React from 'react';
|
|
2
|
+
import { AbsoluteFill, Composition, Img, registerRoot, staticFile } from 'remotion';
|
|
3
|
+
const h = React.createElement;
|
|
4
|
+
const FONT = '-apple-system, "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", system-ui, sans-serif';
|
|
5
|
+
const clamp2 = { display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' };
|
|
6
|
+
const Sheet = (p) => h(AbsoluteFill, { style: { backgroundColor: '#111318', padding: ${f}, fontFamily: FONT, color: '#e5e7eb' } },
|
|
7
|
+
h('div', { style: { height: ${C}, fontSize: 28, fontWeight: 600, color: '#f9fafb' } }, p.title),
|
|
8
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: 'repeat(' + p.cols + ', ' + p.thumbW + 'px)', columnGap: ${d}, rowGap: ${d} } },
|
|
9
|
+
p.items.map((it, i) => h('div', { key: i },
|
|
10
|
+
h(Img, { src: staticFile(it.file), style: { display: 'block', width: p.thumbW, height: p.thumbH, objectFit: 'contain', backgroundColor: '#000', borderRadius: 8, outline: '1px solid #2b2f36' } }),
|
|
11
|
+
h('div', { style: { height: ${E}, paddingTop: 10, overflow: 'hidden' } },
|
|
12
|
+
h('div', { style: { fontSize: 22, fontWeight: 600, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' } }, it.title),
|
|
13
|
+
it.sub ? h('div', { style: Object.assign({ marginTop: 4, fontSize: 18, lineHeight: 1.35, color: '#9ca3af' }, clamp2) }, it.sub) : null)))));
|
|
14
|
+
const Root = () => h(Composition, { id: 'Sheet', component: Sheet, durationInFrames: 1, fps: 30, width: 640, height: 360,
|
|
15
|
+
defaultProps: { items: [], cols: 1, thumbW: 100, thumbH: 100, width: 640, height: 360, title: '' },
|
|
16
|
+
calculateMetadata: ({ props }) => ({ width: props.width, height: props.height }) });
|
|
17
|
+
registerRoot(Root);
|
|
18
|
+
`;async function _(t){const e=[];for(let i=0;i<t.items.length;i+=$)e.push(t.items.slice(i,i+$));if(e.length===0)return[];const a=h.join(t.root,R);k(a,{recursive:!0});const o=y(h.join(a,"still-sheet-")),c=y(h.join(H.tmpdir(),"remixmate-sheet-"));try{const i=h.join(o,"entry.js");v(i,j,"utf8");const r=await x(process.execPath,[t.remotionCli,"bundle",i,"--out-dir",c,`--public-dir=${t.publicDir}`,"--log=error"],{cwd:t.root,stdio:"stderr"});if(r.code!==0)throw new Error(`拼图打包失败(remotion 退出码 ${r.code})`);const s=[];for(const[n,l]of e.entries()){const p=W(l.length,t.frameW,t.frameH),u=e.length>1?`${t.title}(${n+1}/${e.length})`:t.title,g=h.join(o,`props-${n+1}.json`);v(g,JSON.stringify({...p,items:l,title:u}),"utf8");const b=t.outFile(n+1),w=await x(process.execPath,[t.remotionCli,"still",c,"Sheet",b,`--props=${g}`,"--log=error"],{cwd:t.root,stdio:"stderr"});if(w.code!==0)throw new Error(`拼图第 ${n+1} 页渲染失败(remotion 退出码 ${w.code})`);s.push(b)}return s}finally{S(o,{recursive:!0,force:!0}),S(c,{recursive:!0,force:!0})}}m(_,"renderSheets");export{$ as SHEET_PAGE_SIZE,_ as renderSheets,W as sheetLayout};
|
package/dist/template/still.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
用 npm run dev 打开 Remotion Studio 定位问题,或先运行 remixmate template check。`),"fail"),
|
|
3
|
-
✅ 关键帧已生成:`);for(const
|
|
4
|
-
⏸
|
|
1
|
+
var K=Object.defineProperty;var p=(t,n)=>K(t,"name",{value:n,configurable:!0});import{createHash as W}from"node:crypto";import{existsSync as A,mkdirSync as Z,mkdtempSync as Q,readFileSync as V,rmSync as O,writeFileSync as Y}from"node:fs";import ee from"node:os";import r from"node:path";import{EXIT as C,SkillError as $}from"../errors.js";import{emitProgress as j}from"../progress.js";import{ensureRulesCurrent as te}from"./check.js";import{STILLS_DIR as oe}from"./constants.js";import{Output as ne,readJsonFile as M,requireProjectRoot as re,requireTemplateDir as se,validateTemplateName as ie}from"./project.js";import{runProcess as G}from"./proc.js";import{FPS as H,MAX_TOTAL_STILLS as U,enumerateCases as ae,planFrames as le,readDeclaredKeyframes as ce,selectCases as me}from"./still-cases.js";import{renderSheets as pe}from"./still-sheet.js";import{MAX_FRAMES_PER_CASE as Te}from"./still-cases.js";const X="node_modules/@remotion/cli/remotion-cli.js",q={"16:9":[1920,1080],"9:16":[1080,1920],"1:1":[1080,1080],"3:4":[1080,1440],"4:3":[1440,1080]};function de(t,n){return`${t}-${n}`.replace(/[^A-Za-z0-9-]/g,"-")}p(de,"studioCompositionId");const fe=p((t,n)=>`#${t.index} ${t.purpose}${t.sceneId?` · ${t.sceneId}`:""} · ${(n.frame/H).toFixed(1)}s`,"caseTitle");async function Ce(t){const n=new ne(!!t.json),i=ie(t.name),l=re(t.cwd),D=se(l,i);await te(l);const S=r.join(l,X);if(!A(S))throw new $(`❌ 找不到 ${X}:工程依赖未安装。在工程根目录运行 npm install 后重试。`,C.USAGE);const m=M(r.join(D,"template.json")),F=r.join(D,"dsl-example.json"),J=A(F)?M(F):null,d=ae(m,J);if(d.length===0)throw new $("❌ template.json 未声明 compositions",C.USAGE);const b=me(d,t.scenes,t.compositionId),T=t.frames||t.scenes||t.compositionId?[]:ce(m,d.length,o=>n.warn(o)),k=le(b,t.frames,T,d.length),g=[...k.values()].reduce((o,a)=>o+a.length,0);if(g>U)throw new $(`❌ 本次要渲染 ${g} 帧,超过上限 ${U}。用 --scenes 只截改动过的场景。`,C.USAGE);const f=r.resolve(l,t.outDir??r.join(oe,i));Z(f,{recursive:!0}),n.info(`模板 ${i}`),n.info(`场景 ${b.length} / ${d.length} 个,共 ${g} 帧${T.length?"(含 previewHints.keyframes 声明的关键帧)":""}`),n.info("首次运行会下载 headless Chrome,可能需要几分钟。");const z=p((o,a)=>new $(`❌ ${o}失败(remotion 退出码 ${a},错误详情见上方输出)。
|
|
2
|
+
用 npm run dev 打开 Remotion Studio 定位问题,或先运行 remixmate template check。`),"fail"),y=Q(r.join(ee.tmpdir(),"remixmate-still-")),P=r.join(y,"bundle"),c=[],w=[];let x=0;try{j({phase:"template-still:bundle",progress:0});const o=await G(process.execPath,[S,"bundle","src/index.ts","--out-dir",P,"--log=error"],{cwd:l,stdio:"stderr"});if(o.code!==0)throw z("打包工程",o.code);let a=0;for(const[e,E]of k){const I=r.join(y,`props-${e.index}.json`);Y(I,JSON.stringify(e.props),"utf8");const B=de(i,e.compositionId),_=new Set;for(const s of E){j({phase:"template-still:render",progress:(a+1)/(g+1),scene:e.index,frame:s.frame});const h=r.join(f,`${i}-s${String(e.index).padStart(2,"0")}-f${String(s.frame).padStart(5,"0")}.png`),v=[S,"still",P,B,h,`--frame=${s.frame}`,`--props=${I}`,"--log=error"];t.scale!==void 0&&v.push(`--scale=${t.scale}`);const L=await G(process.execPath,v,{cwd:l,stdio:"stderr"});if(a++,L.code!==0||!A(h)){w.push(`#${e.index} ${e.purpose}(第 ${s.frame} 帧,退出码 ${L.code})`);continue}const N=W("sha256").update(V(h)).digest("hex");if(_.has(N)){O(h,{force:!0}),x++;continue}_.add(N),c.push({scene:e.index,purpose:e.purpose,sceneId:e.sceneId,compositionId:e.compositionId,label:s.label,frame:s.frame,timeSec:Number((s.frame/H).toFixed(2)),path:h,...s.expect?{expect:s.expect}:{}})}}}finally{O(y,{recursive:!0,force:!0})}let u=[];if(!t.noSheet&&c.length>1){j({phase:"template-still:sheet",progress:.98});const[o,a]=q[(m.supportedAspectRatios??[])[0]]??q["16:9"];try{u=await pe({root:l,remotionCli:S,publicDir:f,outFile:p(e=>r.join(f,`${i}-sheet-${e}.png`),"outFile"),items:c.map(e=>{const E=b.find(I=>I.index===e.scene);return{file:r.basename(e.path),title:fe(E,{label:e.label,frame:e.frame}),sub:e.expect??(e.label!=="settled"?e.label:void 0)}}),frameW:o,frameH:a,title:`${typeof m.name=="string"&&m.name?m.name:i} · 关键帧`})}catch(e){n.warn(` ⚠ 拼图生成失败(${e.message}),单张图片不受影响。`)}}if(w.length>0)throw new $(`❌ ${w.length} 个关键帧渲染失败:${w.join(",")}。其余 ${c.length} 张已生成在 ${f}。用 npm run dev 打开 Remotion Studio 定位问题。`);j({phase:"template-still:done",progress:1,stills:c.map(o=>o.path),sheets:u,awaitingConfirmation:!0}),x>0&&n.info(` (${x} 帧与同场景的其它帧完全相同,已去掉)`),n.info(`
|
|
3
|
+
✅ 关键帧已生成:`);for(const o of c)n.info(` #${o.scene} ${o.purpose.padEnd(8)} ${String(o.timeSec).padStart(6)}s ${o.path}`);for(const o of u)n.info(` 拼图 ${o}`);n.info(`
|
|
4
|
+
⏸ 确认点:请把`+(u.length?"拼图":"这些图片")+"展示给用户确认画面效果(每格说明这一刻应该看到什么),按反馈修改后可只重截改动的场景(--scenes);用户确认后再做远程预览。");const R={ok:!0,name:i,stills:c,sheets:u,duplicatesDropped:x,awaitingConfirmation:!0};return n.result({...R}),R}p(Ce,"runStill");export{Te as MAX_FRAMES_PER_CASE,Ce as runStill,de as studioCompositionId};
|
package/dist/template/upgrade.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var
|
|
2
|
-
`,"utf8"),
|
|
1
|
+
var V=Object.defineProperty;var d=(e,r)=>V(e,"name",{value:r,configurable:!0});import{cpSync as B,existsSync as g,mkdirSync as F,mkdtempSync as H,readFileSync as L,renameSync as x,rmSync as p,writeFileSync as N}from"node:fs";import P from"node:os";import i from"node:path";import{EXIT as T,SkillError as l}from"../errors.js";import{emitProgress as $}from"../progress.js";import{RULES_DIR as u,RULES_VERSION_FILE as _,STARTER_VERSION as U}from"./constants.js";import{applyDocs as C,describeDoc as O,readDocHashes as J,writeDocHashes as W}from"./docs.js";import{obtainStarterTarball as q}from"./init.js";import{Output as D,compareVersions as z,readRulesVersion as A,requireProjectRoot as K}from"./project.js";import{runProcess as Q}from"./proc.js";function X(e,r){const o={...e.devDependencies,...e.dependencies},c={...r.devDependencies,...r.dependencies};return Object.entries(c).filter(([t,s])=>o[t]!==s).map(([t,s])=>({name:t,project:o[t]??null,starter:s})).sort((t,s)=>t.name.localeCompare(s.name))}d(X,"diffDependencies");function E(e){try{return JSON.parse(L(e,"utf8"))}catch{return{}}}d(E,"readPkg");class G extends D{static{d(this,"QuietOutput")}constructor(){super(!0)}info(){}}async function ae(e={}){const r=new D(!!e.json||!!e.auto),o=K(e.cwd),c=A(o),t=U,s=d(a=>(e.auto||r.result({...a}),a),"finish");if(c&&z(c,t)>=0&&!e.force)return e.auto||r.info(`✅ 本地校验规则已是最新(starter ${c},当前 cli 对应 ${t})。`),s({ok:!0,from:c,to:t,upgraded:!1,dryRun:!!e.dryRun,docs:[],dependencyHints:[]});const y=H(i.join(P.tmpdir(),"remixmate-upgrade-"));try{$({phase:"template-upgrade:fetch",progress:.2});const{file:a,label:j}=await q(y,e.auto?new G:r),f=i.join(y,"starter");F(f,{recursive:!0});const h=await Q("tar",["-xzf",a,"-C",f,"--strip-components=1"],{stdio:"pipe"});if(h.code!==0)throw new l(`❌ 解压 starter 失败(tar 退出码 ${h.code}): ${h.stderr.trim().slice(0,300)}`);const w=i.join(f,u);if(!g(i.join(w,"check","index.mjs")))throw new l(`❌ ${j} 里没有 ${u}/check/index.mjs,不是有效的 starter 包`);const k=X(E(i.join(o,"package.json")),E(i.join(f,"package.json"))),b=J(o);if(e.auto?r.info(`↻ 已自动更新本地校验规则与工作流(.remixmate/):starter ${c??"(未知版本)"} → ${t},templates/ 未改动。`):(r.info(`升级本地校验规则:starter ${c??"(未知版本)"} → ${t}(${j})`),r.info(` 替换 ${u}/(check 脚本、schema、安全策略、WORKFLOW.md)`),r.info(" 不改动 templates/、src/、examples/、package.json")),!e.dryRun){$({phase:"template-upgrade:apply",progress:.6});const n=i.join(o,u),m=i.join(o,`${u}.upgrading`),R=i.join(o,`${u}.previous`);p(m,{recursive:!0,force:!0}),p(R,{recursive:!0,force:!0}),B(w,m,{recursive:!0});const v=i.join(m,i.relative(u,_));g(v)||N(v,JSON.stringify({starterVersion:t},null,2)+`
|
|
2
|
+
`,"utf8"),g(n)&&x(n,R),x(m,n),p(R,{recursive:!0,force:!0})}const{outcomes:S,hashes:I}=C(o,f,b,{force:e.withDocs,dryRun:e.dryRun});e.dryRun||W(o,I);for(const n of S)n.action==="kept-modified"?r.warn(` ⚠ ${O(n)}`):e.auto&&n.action==="unchanged"||r.info(` ${O(n)}`);for(const n of k)r.warn(` ⚠ 新 starter 钉的 ${n.name}@${n.starter},你的工程是 ${n.project??"(未安装)"}。需要时手动执行 npm i ${n.name}@${n.starter}(升级 core 可能影响模板效果,先 check + preview 再发布)。`);return $({phase:"template-upgrade:done",progress:1}),e.auto||r.info(e.dryRun?`
|
|
3
3
|
[dry-run] 未写入任何文件。`:`
|
|
4
|
-
✅ 已更新。接着运行 remixmate template check 看新规则下是否还有问题。`)
|
|
4
|
+
✅ 已更新。接着运行 remixmate template check 看新规则下是否还有问题。`),s({ok:!0,from:c,to:t,upgraded:!e.dryRun,dryRun:!!e.dryRun,docs:S,dependencyHints:k})}catch(a){throw a instanceof l?a:new l(`❌ 升级失败: ${a.message}`,T.ERROR)}finally{p(y,{recursive:!0,force:!0})}}d(ae,"runUpgrade");export{X as diffDependencies,ae as runUpgrade};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var A=Object.defineProperty;var r=(t,e)=>A(t,"name",{value:e,configurable:!0});import{spawn as k}from"node:child_process";import{mkdirSync as w,readFileSync as m,writeFileSync as x}from"node:fs";import{homedir as S}from"node:os";import o from"node:path";import{fileURLToPath as T}from"node:url";const i="@remixmate/cli",v="REMIXMATE_NO_UPDATE_CHECK",d=1440*60*1e3,M=2500,N=1e4,l=T(import.meta.url),R=o.resolve(o.dirname(l),"..");function u(){return o.join(S(),".config","remixmate","update-check.json")}r(u,"cacheFile");function g(){try{const t=JSON.parse(m(o.join(R,"package.json"),"utf8"));return t.name===i&&typeof t.version=="string"?t.version:null}catch{return null}}r(g,"ownVersion");function y(t=process.env){return t[v]==="1"||!!t.CI}r(y,"updateCheckDisabled");function C(t,e){const n=r(c=>c.replace(/^v/,"").split(/[-+]/)[0].split(".").map(a=>Number(a)||0),"parse"),s=n(t),h=n(e);for(let c=0;c<Math.max(s.length,h.length);c++){const a=(s[c]??0)-(h[c]??0);if(a!==0)return a>0}return!1}r(C,"isNewer");function I(t=process.env){return(t.REMIXMATE_NPM_REGISTRY||t.npm_config_registry||"https://registry.npmjs.org").replace(/\/+$/,"")}r(I,"registry");async function E(t){try{const e=await fetch(`${I()}/${i.replace("/","%2f")}/latest`,{headers:{accept:"application/json"},signal:AbortSignal.timeout(t)});if(!e.ok)return null;const n=await e.json();return typeof n.version=="string"?n.version:null}catch{return null}}r(E,"fetchLatest");function _(){try{const t=JSON.parse(m(u(),"utf8"));return typeof t.checkedAt!="number"?null:{checkedAt:t.checkedAt,latest:typeof t.latest=="string"?t.latest:null}}catch{return null}}r(_,"readCache");function f(t){try{w(o.dirname(u()),{recursive:!0}),x(u(),JSON.stringify(t)+`
|
|
2
|
+
`,"utf8")}catch{}}r(f,"writeCache");const p=r((t,e)=>e&&C(e,t)?{current:t,latest:e}:null,"toInfo");function O(t){f({checkedAt:Date.now(),latest:t?.latest??null});try{k(process.execPath,[l,"--refresh"],{detached:!0,stdio:"ignore"}).unref()}catch{}}r(O,"scheduleRefresh");function $(){if(y())return null;const t=g();if(!t)return null;const e=_();return(!e||Date.now()-e.checkedAt>d)&&O(e),p(t,e?.latest??null)}r($,"cachedUpdateInfo");async function H(t=M){if(y())return null;const e=g();if(!e)return null;const n=_();if(n&&n.latest&&Date.now()-n.checkedAt<=d)return p(e,n.latest);const s=await E(t);return f({checkedAt:Date.now(),latest:s??n?.latest??null}),p(e,s??n?.latest??null)}r(H,"freshUpdateInfo");function K(t){return`⚠ remixmate 有新版本 ${t.latest}(当前 ${t.current})。升级:npm i -g ${i}@latest`}r(K,"updateHint"),process.argv[1]&&o.resolve(process.argv[1])===l&&process.argv[2]==="--refresh"&&E(N).then(t=>{t&&f({checkedAt:Date.now(),latest:t})});export{v as NO_UPDATE_CHECK_ENV,i as PACKAGE_NAME,$ as cachedUpdateInfo,E as fetchLatest,H as freshUpdateInfo,C as isNewer,g as ownVersion,y as updateCheckDisabled,K as updateHint};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remixmate/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "AI media generation skills for Claude Code / Codex — 12 skills covering image, video, voice, digital human, web screenshot, web recording, script, template registry, rendering, Jianying export, and video deconstruction.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -41,7 +41,7 @@ explicitly says so in this conversation (e.g. 「不用确认,直接发布」)
|
|
|
41
41
|
| ⏸ | When | Show the user | Proceed when |
|
|
42
42
|
|---|---|---|---|
|
|
43
43
|
| A | before writing any template code | short spec: aspect ratio, duration, scene structure, visual style, adjustable parameters (`customPayload` fields) | user approves / edits the spec |
|
|
44
|
-
| B | after `template still` | the
|
|
44
|
+
| B | after `template still` | the contact sheet (embed its path) + one line per cell on what it should show | user approves the look |
|
|
45
45
|
| C | after `template preview` | the preview URL + what changed since the last round | user approves motion and pacing |
|
|
46
46
|
| D | after `template publish` without `--yes` (exit code 3) | the printed summary: name, new vs. **update of an existing template**, parameters, preview state | user explicitly confirms publishing |
|
|
47
47
|
|
|
@@ -65,8 +65,12 @@ Exit code 3 from `publish` is the checkpoint, not a failure: nothing was uploade
|
|
|
65
65
|
**Needs network**: it runs `npm pack` + `npm install` (a few hundred packages, about a minute).
|
|
66
66
|
In a sandboxed host (e.g. Codex) request network / escalated permission for this command up
|
|
67
67
|
front. Exit code 5 or an npm network error = no network — not a bug in the project.
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
If `init` prints 「remixmate 有新版本」 (JSON: `cliUpdate`), the project would get an older
|
|
69
|
+
starter: ask the user, run `npm i -g @remixmate/cli@latest`, then init again.
|
|
70
|
+
2. **Open the project and read `<dir>/AGENTS.md` and `<dir>/.remixmate/WORKFLOW.md`** before
|
|
71
|
+
writing any code. WORKFLOW.md is the authoritative, auto-updated workflow — it wins over this
|
|
72
|
+
skill and over AGENTS.md. If the session is not rooted in `<dir>`, read both by absolute path
|
|
73
|
+
(or suggest reopening the session there).
|
|
70
74
|
Examples live in `examples/` (read-only — copy one into `templates/<name>/` and edit the copy).
|
|
71
75
|
**⏸ A** — propose the spec and wait for the user before coding.
|
|
72
76
|
3. **Develop with live preview**: `npm run dev` opens Remotion Studio. The first run (and the first
|
|
@@ -79,11 +83,16 @@ Exit code 3 from `publish` is the checkpoint, not a failure: nothing was uploade
|
|
|
79
83
|
template's core contract fields — never hand-edit those.
|
|
80
84
|
5. **Keyframe stills** (local, no login, no platform cost):
|
|
81
85
|
```bash
|
|
82
|
-
remixmate template still <name> [--
|
|
86
|
+
remixmate template still <name> [--scenes 3,5] [--frames intro,settled] [--json]
|
|
83
87
|
```
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
Default: every `dsl-example.json` scene plus uncovered compositions (e.g. the cover), frames
|
|
89
|
+
by budget — 1 scene: intro / middle / settled / final; 2–3: intro / settled; ≥ 4: settled
|
|
90
|
+
(identical frames within a scene are dropped) — and a captioned contact sheet
|
|
91
|
+
(`sheets[]`, 12 per page; single PNGs in `stills[].path`). Templates whose process matters
|
|
92
|
+
declare extra moments in `template.json` → `previewHints.keyframes` (anchors, not exact
|
|
93
|
+
times — see the project's `.remixmate/WORKFLOW.md`).
|
|
94
|
+
**⏸ B** — show the contact sheet (not every single PNG), say per cell what the user should
|
|
95
|
+
see, wait. After a fix re-render only the changed scenes with `--scenes`.
|
|
87
96
|
6. **Remote draft preview** (login required): renders on the real platform renderer.
|
|
88
97
|
```bash
|
|
89
98
|
remixmate template preview <name>
|
|
@@ -109,15 +118,18 @@ Exit code 3 from `publish` is the checkpoint, not a failure: nothing was uploade
|
|
|
109
118
|
```
|
|
110
119
|
`remixmate template list` shows the user's private templates.
|
|
111
120
|
|
|
112
|
-
##
|
|
121
|
+
## Staying current (mostly automatic)
|
|
113
122
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
123
|
+
- **Project rules + workflow**: `check` / `still` / `preview` / `publish` update `.remixmate/`
|
|
124
|
+
(check rules and WORKFLOW.md) by themselves when it is older than the cli, and print
|
|
125
|
+
「↻ 已自动更新本地校验规则与工作流」. After that line, re-read `.remixmate/WORKFLOW.md`.
|
|
126
|
+
`templates/` is never touched. Offline it only warns; `remixmate template upgrade` is the
|
|
127
|
+
manual fallback.
|
|
128
|
+
- **AGENTS.md / CLAUDE.md**: updated along with it unless the user edited them; then the new
|
|
129
|
+
version is written to `*.new` — mention it, don't merge without asking.
|
|
130
|
+
- **The cli itself**: when any output says 「remixmate 有新版本」, ask the user, run
|
|
131
|
+
`npm i -g @remixmate/cli@latest`, then re-run the command.
|
|
132
|
+
- **Dependencies** (template-core, remotion) are reported, never changed automatically.
|
|
121
133
|
|
|
122
134
|
## Notes
|
|
123
135
|
|
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
"skillName": "template-authoring",
|
|
3
3
|
"repoName": "agent-skill-media-maker",
|
|
4
4
|
"skillId": "pending-template-authoring",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "V6",
|
|
6
6
|
"skillDescription": "Local video-template authoring skill: scaffolds a RemixMate template starter project (`remixmate template init <dir>`), then guides the agent through the project's AGENTS.md, `npm run dev`, `remixmate template check`, `remixmate template still` (keyframe PNGs), `remixmate template preview`, `remixmate template publish` (summary first, `--yes` after the user confirms), with mandatory user confirmation checkpoints, and rendering with `remixmate render-video --template-id <templateKey>`.\n\nUse this skill whenever the user asks for any of:\n- 做视频模板 / 做视频模版 / 自定义视频模板 / 创建模板 / 创建一个新的视频模版 / 新建模版\n- template authoring, create a video template\n- preview or publish a template they develop locally"
|
|
7
7
|
}
|