@remixmate/cli 0.10.0 → 0.11.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "version": "0.10.0",
4
- "generatedAt": "2026-10-08T23:39:22.710Z",
3
+ "version": "0.11.0",
4
+ "generatedAt": "2026-10-09T01:32:09.848Z",
5
5
  "skills": [
6
6
  {
7
7
  "id": "doc-parse",
@@ -1,2 +1,5 @@
1
- var n=Object.defineProperty;var i=(r,o)=>n(r,"name",{value:o,configurable:!0});import s from"node:path";import{CHECK_ENTRY as c}from"./constants.js";import{requireProjectRoot as m,staleRulesHint as a}from"./project.js";import{runProcess as f}from"./proc.js";async function C(r={}){const o=m(r.cwd),t=a(o);t&&process.stderr.write(t+`
2
- `);const e=[s.join(o,c)];return r.name&&e.push(r.name),r.json&&e.push("--json"),r.noWrite&&e.push("--no-write"),(await f(process.execPath,e,{cwd:o,stdio:r.stdio??"inherit"})).code}i(C,"runCheck");export{C as runCheck};
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,12 +1,12 @@
1
- var k=Object.defineProperty;var m=(n,s)=>k(n,"name",{value:s,configurable:!0});import{cleanDiagnosticText as u}from"../diagnostics.js";import{EXIT as i,SkillError as d}from"../errors.js";import{flagNumber as l,flagString as o,parseTemplateArgs as E,usageError as f}from"./args.js";import{runCheck as b}from"./check.js";import{runUpgrade as S}from"./upgrade.js";import{STARTER_PACKAGE as g,STARTER_VERSION as h}from"./constants.js";import{runInit as y}from"./init.js";import{runList as R}from"./list.js";import{runPreview as T}from"./preview.js";import{isPublishConfirmation as j,runPublish as P}from"./publish.js";import{MAX_STILLS as A,runStill as N}from"./still.js";const X="template",w=["init","check","still","preview","publish","list","upgrade"],c={init:{flags:{force:"bool","skip-install":"bool"},maxPositionals:1,usage:`remixmate template init <dir> [--force] [--skip-install] [--json]
2
- 从 ${g}@${h} 创建模板工程并安装依赖(无需登录)。
1
+ var T=Object.defineProperty;var p=(r,s)=>T(r,"name",{value:s,configurable:!0});import{cleanDiagnosticText as f}from"../diagnostics.js";import{EXIT as n,SkillError as g}from"../errors.js";import{flagNumber as l,flagString as o,parseTemplateArgs as A,usageError as h}from"./args.js";import{runCheck as R}from"./check.js";import{runUpgrade as b}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 N}from"./publish.js";import{MAX_STILLS as O,runStill as U}from"./still.js";import{cachedUpdateInfo as _,freshUpdateInfo as I,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
7
  `},still:{flags:{frames:"string","composition-id":"string",scale:"string","out-dir":"string"},maxPositionals:1,usage:`remixmate template still <name> [--frames start,mid,end] [--composition-id ID] [--scale N] [--out-dir DIR] [--json]
8
8
  用工程内的 Remotion 在本地渲染关键帧 PNG(无需登录、不占平台资源),供用户确认画面。
9
- --frames 取值:start / mid / end / 帧号 / 秒数(如 2.5s),逗号分隔,最多 ${A} 帧;默认 start,mid,end。
9
+ --frames 取值:start / mid / end / 帧号 / 秒数(如 2.5s),逗号分隔,最多 ${O} 帧;默认 start,mid,end。
10
10
  默认输出到 output/stills/<name>/;--scale 0.5 可缩小图片。
11
11
  `},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
12
  先 check,再把模板提交到远程渲染器出预览 mp4(需登录)。
@@ -20,9 +20,11 @@ var k=Object.defineProperty;var m=(n,s)=>k(n,"name",{value:s,configurable:!0});i
20
20
  `},list:{flags:{},maxPositionals:0,usage:`remixmate template list [--json]
21
21
  列出你发布的私有模板(需登录)。
22
22
  `},upgrade:{flags:{"dry-run":"bool","with-docs":"bool",force:"bool"},maxPositionals:0,usage:`remixmate template upgrade [--dry-run] [--with-docs] [--force] [--json]
23
- 把工程的本地校验规则(.remixmate/)更新到 ${g}@${h}(无需登录)。
24
- 不改动 templates/、src/、examples/、package.json;--with-docs 同时刷新 AGENTS.md / CLAUDE.md(旧文件存为 *.orig)。
25
- `}};function O(){return`Usage: remixmate template <command> [options]
23
+ 把工程的本地校验规则与工作流(.remixmate/)更新到 ${w}@${u}(无需登录)。
24
+ 通常不用手动运行:check / still / preview / publish 发现规则过期会自动更新(REMIXMATE_NO_AUTO_UPGRADE=1 关闭)。
25
+ 没改过的 AGENTS.md / CLAUDE.md 一起更新,改过的保留、新版写 *.new;--with-docs 强制覆盖(旧文件存为 *.orig)。
26
+ 不改动 templates/、src/、examples/、package.json。
27
+ `}};function C(){return`Usage: remixmate template <command> [options]
26
28
 
27
29
  Commands:
28
30
  init <dir> 创建模板工程(无需登录)
@@ -34,8 +36,13 @@ Commands:
34
36
  upgrade 更新工程的本地校验规则(无需登录)
35
37
 
36
38
  Common options: --json --token <PrivToken> --api-base-url <url>
37
- 环境变量:AB_RENDER_URL(渲染服务地址)、REMIXMATE_STARTER_TARBALL(init 使用本地 starter 包)
39
+ 环境变量:AB_RENDER_URL(渲染服务地址)、REMIXMATE_STARTER_TARBALL(init 使用本地 starter 包)、
40
+ REMIXMATE_NO_AUTO_UPGRADE=1(不自动更新工程规则)、REMIXMATE_NO_UPDATE_CHECK=1(不检查 cli 新版本)
38
41
  运行 remixmate template <command> --help 查看子命令说明。
39
- `}m(O,"templateUsage");function C(n){const[s,...e]=n;if(!s||s==="--help"||s==="-h"||s==="help")return{sub:"help",args:{positionals:[],flags:{}}};if(!w.includes(s))throw f(`未知子命令: template ${s}(可选:${w.join(" | ")})`);const t=c[s];return{sub:s,args:E(e,t.flags,{maxPositionals:t.maxPositionals})}}m(C,"parseTemplateCommand");async function I(n,s){const{flags:e,positionals:t}=s,r=e.json===!0,a=o(e,"token"),p=o(e,"api-base-url");switch(n){case"init":if(!t[0])throw f("缺少目标目录:remixmate template init <dir>");return await y({dir:t[0],force:e.force===!0,json:r,skipInstall:e["skip-install"]===!0}),i.OK;case"check":return b({name:t[0],json:r,noWrite:e["no-write"]===!0});case"still":return await N({name:t[0],frames:o(e,"frames"),compositionId:o(e,"composition-id"),scale:l(e,"scale"),outDir:o(e,"out-dir"),json:r}),i.OK;case"preview":return await T({name:t[0],propsJson:o(e,"props-json"),durationSec:l(e,"duration-sec"),compositionId:o(e,"composition-id"),timeoutSec:l(e,"timeout-sec"),json:r,token:a,apiBaseUrl:p}),i.OK;case"publish":{const x=await P({name:t[0],displayName:o(e,"name"),dryRun:e["dry-run"]===!0,yes:e.yes===!0,skipPreviewCheck:e["skip-preview-check"]===!0,json:r,token:a,apiBaseUrl:p});return j(x)?i.NEEDS_CONFIRMATION:i.OK}case"list":return await R({json:r,token:a,apiBaseUrl:p}),i.OK;case"upgrade":return await S({dryRun:e["dry-run"]===!0,withDocs:e["with-docs"]===!0,force:e.force===!0,json:r}),i.OK}}m(I,"dispatch");async function F(n){const s=n.includes("--json");let e;try{const t=C(n);return e=t.sub,e==="help"?(process.stdout.write(O()),i.OK):t.args.flags.help?(process.stdout.write("Usage: "+c[e].usage),i.OK):await I(e,t.args)}catch(t){const r=t instanceof d?t.exitCode:i.ERROR,a=t instanceof d?t.message:`❌ unexpected error: ${t.stack??String(t)}`;return process.stderr.write(u(a,8192)+`
40
- `),r===i.USAGE&&e&&e!=="help"&&process.stderr.write("Usage: "+c[e].usage),s&&process.stdout.write(JSON.stringify({ok:!1,exitCode:r,error:u(a.replace(/^❌\s*/,""),2048)})+`
41
- `),r}}m(F,"runTemplateCommand");export{c as SUB_SPECS,w as TEMPLATE_SUBCOMMANDS,X as TEMPLATE_VERB,C as parseTemplateCommand,F as runTemplateCommand,O as templateUsage};
42
+ `}p(C,"templateUsage");function M(r){const[s,...e]=r;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:A(e,t.flags,{maxPositionals:t.maxPositionals})}}p(M,"parseTemplateCommand");async function v(r,s){const{flags:e,positionals:t}=s,i=e.json===!0,a=o(e,"token"),c=o(e,"api-base-url");switch(r){case"init":{if(!t[0])throw h("缺少目标目录:remixmate template init <dir>");const m=await I();return m&&process.stderr.write(`${E(m)}
43
+ 当前 cli 会用 starter ${u} 创建工程,可能不是最新的工作流与校验规则。建议先升级 cli 再 init。
44
+ `),await P({dir:t[0],force:e.force===!0,json:i,skipInstall:e["skip-install"]===!0,...m?{cliUpdate:m}:{}}),n.OK}case"check":return R({name:t[0],json:i,noWrite:e["no-write"]===!0});case"still":return await U({name:t[0],frames:o(e,"frames"),compositionId:o(e,"composition-id"),scale:l(e,"scale"),outDir:o(e,"out-dir"),json:i}),n.OK;case"preview":return await S({name:t[0],propsJson:o(e,"props-json"),durationSec:l(e,"duration-sec"),compositionId:o(e,"composition-id"),timeoutSec:l(e,"timeout-sec"),json:i,token:a,apiBaseUrl:c}),n.OK;case"publish":{const m=await N({name:t[0],displayName:o(e,"name"),dryRun:e["dry-run"]===!0,yes:e.yes===!0,skipPreviewCheck:e["skip-preview-check"]===!0,json:i,token:a,apiBaseUrl:c});return j(m)?n.NEEDS_CONFIRMATION:n.OK}case"list":return await y({json:i,token:a,apiBaseUrl:c}),n.OK;case"upgrade":return await b({dryRun:e["dry-run"]===!0,withDocs:e["with-docs"]===!0,force:e.force===!0,json:i}),n.OK}}p(v,"dispatch");function k(){try{const r=_();r&&process.stderr.write(`
45
+ ${E(r)}
46
+ `)}catch{}}p(k,"printCachedUpdateHint");async function Q(r){const s=r.includes("--json");let e;try{const t=M(r);if(e=t.sub,e==="help")return process.stdout.write(C()),n.OK;if(t.args.flags.help)return process.stdout.write("Usage: "+d[e].usage),n.OK;const i=await v(e,t.args);return e!=="init"&&k(),i}catch(t){const i=t instanceof g?t.exitCode:n.ERROR,a=t instanceof g?t.message:`❌ unexpected error: ${t.stack??String(t)}`;return process.stderr.write(f(a,8192)+`
47
+ `),i===n.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:i,error:f(a.replace(/^❌\s*/,""),2048)})+`
48
+ `),i}}p(Q,"runTemplateCommand");export{d as SUB_SPECS,x as TEMPLATE_SUBCOMMANDS,z as TEMPLATE_VERB,M as parseTemplateCommand,Q as runTemplateCommand,C as templateUsage};
@@ -1 +1 @@
1
- const t="@remixmate/template-starter",e="0.4.0",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};
1
+ const t="@remixmate/template-starter",e="0.4.1",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};
@@ -1,5 +1,5 @@
1
- var x=Object.defineProperty;var m=(e,i)=>x(e,"name",{value:i,configurable:!0});import{existsSync as w,mkdirSync as k,mkdtempSync as T,readdirSync as E,rmSync as u,statSync as g}from"node:fs";import $ from"node:os";import o from"node:path";import{EXIT as f,SkillError as a}from"../errors.js";import{emitProgress as c}from"../progress.js";import{STARTER_PACKAGE as N,STARTER_TARBALL_ENV as A,STARTER_VERSION as R}from"./constants.js";import{Output as j}from"./project.js";import{runNpm as y,runProcess as G}from"./proc.js";import{skillInstallHint as b}from"../install.js";function v(e){if(!w(e))return!1;if(!g(e).isDirectory())throw new a(`❌ ${e} 已存在且不是目录`,f.USAGE);return E(e).length>0}m(v,"isNonEmptyDir");async function C(e,i){const l=(process.env[A]??"").trim();if(l){const t=o.resolve(l);if(!w(t))throw new a(`❌ ${A} 指向的文件不存在: ${t}`,f.USAGE);return i.info(`使用本地 starter 包: ${t}`),{file:t,label:`local:${o.basename(t)}`}}const n=`${N}@${R}`;i.info(`下载 ${n} …`);const r=await y(["pack",n,"--json","--silent"],{cwd:e,stdio:"pipe"});if(r.code!==0){const t=r.stderr.trim().split(`
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 ${n} 失败(退出码 ${r.code})。检查网络与 npm registry 配置。
4
- ${t}`,f.BACKEND_UNREACHABLE)}let s;try{const t=JSON.parse(r.stdout);t[0]?.filename&&(s=o.join(e,t[0].filename))}catch{}if(s??=E(e).filter(t=>t.endsWith(".tgz")).map(t=>o.join(e,t))[0],!s||!w(s))throw new a("❌ npm pack 未产出 tarball");return{file:s,label:n}}m(C,"obtainStarterTarball");async function K(e){const i=new j(!!e.json);if(!e.dir)throw new a("❌ 缺少目标目录:remixmate template init <dir>",f.USAGE);const l=e.dir==="~"||/^~[\\/]/.test(e.dir)?o.join($.homedir(),e.dir.slice(1)):e.dir,n=o.resolve(l);if(v(n)&&!e.force)throw new a(`❌ 目标目录非空: ${n}
5
- 换一个空目录,或确认可以覆盖后加 --force。`,f.USAGE);const r=T(o.join($.tmpdir(),"remixmate-starter-"));try{c({phase:"template-init:fetch",progress:.1});const{file:s,label:t}=await C(r,i);c({phase:"template-init:extract",progress:.4}),k(n,{recursive:!0});const p=await G("tar",["-xzf",s,"-C",n,"--strip-components=1"],{stdio:"pipe"});if(p.code!==0)throw new a(`❌ 解压 starter 失败(tar 退出码 ${p.code}): ${p.stderr.trim().slice(0,300)}`);let d=!1;if(!e.skipInstall){c({phase:"template-init:install",progress:.6}),i.info("安装依赖(npm install)…");const S=await y(["install","--no-audit","--no-fund"],{cwd:n,stdio:"stderr"});if(S.code!==0)throw new a(`❌ npm install 失败(退出码 ${S.code})。工程文件已解压到 ${n},修复后在该目录重跑 npm install。`);d=!0}c({phase:"template-init:done",progress:1}),U(i,n,d);const h={ok:!0,dir:n,starter:t,installed:d};return i.result({...h}),h}finally{u(r,{recursive:!0,force:!0})}}m(K,"runInit");function U(e,i,l){const n=/^[\w@%+=:,./\\~-]+$/.test(i)?i:`"${i}"`;e.info(""),e.info(`✅ 模板工程已创建: ${i}`),e.info(""),e.info("下一步:"),e.info(` cd ${n}`),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(i,"AGENTS.md")+",或在该目录重开会话");let r=null;try{r=b()}catch{}r&&(e.info(""),e.info(r))}m(U,"printNextSteps");export{C as obtainStarterTarball,K as runInit};
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};
@@ -1,4 +1,4 @@
1
- var _=Object.defineProperty;var l=(o,e)=>_(o,"name",{value:e,configurable:!0});import{existsSync as x,mkdirSync as O,mkdtempSync as R,rmSync as U}from"node:fs";import q from"node:os";import d from"node:path";import{EXIT as P,SkillError as j}from"../errors.js";import{emitProgress as b}from"../progress.js";import{usageError as y}from"./args.js";import{STILLS_DIR as B}from"./constants.js";import{Output as G,readJsonFile as T,requireProjectRoot as X,requireTemplateDir as z,validateTemplateName as J}from"./project.js";import{runProcess as k}from"./proc.js";const h=30,Z=5,v=6,L="node_modules/@remotion/cli/remotion-cli.js";function H(o,e){return`${o}-${e}`.replace(/[^A-Za-z0-9-]/g,"-")}l(H,"studioCompositionId");function K(o,e){const i=new Set;let t=o[e];for(;t?.$ref;){if(i.has(t.$ref))return{};i.add(t.$ref),t=o[t.$ref]}return t??{}}l(K,"resolveSlot");function Q(o,e,i){const t=Array.isArray(e?.scenes)?e.scenes:[],a=o.slotMapping??{},r=t.find(m=>m.purpose&&K(a,m.purpose).compositionId===i.compositionId)??t.find(m=>m.purpose===i.slot)??t[0],s=typeof r?.duration=="number"&&r.duration>0?r.duration:o.supportedDurations?.min??Z;return Math.max(1,Math.round(s*h))}l(Q,"sampleDurationFrames");function V(o,e){const i=e-1,t=o.split(",").map(r=>r.trim()).filter(Boolean);if(t.length===0)throw y("--frames 不能为空");const a=[];for(const r of t){let s;if(r==="start")s=0;else if(r==="mid")s=Math.floor(i/2);else if(r==="end")s=i;else if(/^\d+(\.\d+)?s$/.test(r))s=Math.round(Number(r.slice(0,-1))*h);else if(/^\d+$/.test(r))s=Number(r);else throw y(`--frames 取值不合法: '${r}'(可用 start / mid / end / 帧号 / 秒数如 2.5s)`);s=Math.min(Math.max(0,s),i),a.some(m=>m.frame===s)||a.push({label:r,frame:s})}if(a.length>v)throw y(`--frames 最多 ${v} 帧,收到 ${a.length} 帧`);return a}l(V,"parseFrameSpec");async function mo(o){const e=new G(!!o.json),i=J(o.name),t=X(o.cwd),a=z(t,i),r=d.join(t,L);if(!x(r))throw new j(`❌ 找不到 ${L}:工程依赖未安装。在工程根目录运行 npm install 后重试。`,P.USAGE);const s=T(d.join(a,"template.json")),m=Array.isArray(s.compositions)?s.compositions:[],f=o.compositionId?m.find(n=>n.compositionId===o.compositionId):m[0];if(!f)throw new j(o.compositionId?`❌ composition "${o.compositionId}" 不在该模板中。可选: ${m.map(n=>n.compositionId).join(", ")}`:"❌ template.json 未声明 compositions",P.USAGE);const D=d.join(a,"dsl-example.json"),N=x(D)?T(D):null,p=Q(s,N,f),$=V(o.frames??"start,mid,end",p),S=H(i,String(f.compositionId)),A=d.resolve(t,o.outDir??d.join(B,i));O(A,{recursive:!0}),e.info(`模板 ${i}`),e.info(`composition ${S}(${(p/h).toFixed(1)}s,${p} 帧)`),e.info(`截帧 ${$.map(n=>`${n.label}=${n.frame}`).join(", ")}`),e.info("首次运行会下载 headless Chrome,可能需要几分钟。");const E=l((n,w)=>new j(`❌ ${n}失败(remotion 退出码 ${w},错误详情见上方输出)。
2
- 用 npm run dev 打开 Remotion Studio 定位问题,或先运行 remixmate template check。`),"fail"),g=R(d.join(q.tmpdir(),"remixmate-still-")),u=[];try{b({phase:"template-still:bundle",progress:0});const n=await k(process.execPath,[r,"bundle","src/index.ts","--out-dir",g,"--log=error"],{cwd:t,stdio:"stderr"});if(n.code!==0)throw E("打包工程",n.code);for(const[w,c]of $.entries()){b({phase:"template-still:render",progress:(w+1)/($.length+1),frame:c.frame});const I=d.join(A,`${i}-f${String(c.frame).padStart(5,"0")}.png`),F=[r,"still",g,S,I,`--frame=${c.frame}`,"--log=error"];o.scale!==void 0&&F.push(`--scale=${o.scale}`);const C=await k(process.execPath,F,{cwd:t,stdio:"stderr"});if(C.code!==0||!x(I))throw E(`第 ${c.frame} 帧渲染`,C.code);u.push({label:c.label,frame:c.frame,timeSec:Number((c.frame/h).toFixed(2)),path:I})}}finally{U(g,{recursive:!0,force:!0})}b({phase:"template-still:done",progress:1,stills:u.map(n=>n.path),awaitingConfirmation:!0}),e.info(`
3
- ✅ 关键帧已生成:`);for(const n of u)e.info(` ${n.label.padEnd(6)} ${n.timeSec}s ${n.path}`);e.info(`
4
- ⏸ 确认点:请把这些图片展示给用户确认画面效果,按反馈修改后可重新截帧;用户确认后再做远程预览。`);const M={ok:!0,name:i,compositionId:String(f.compositionId),studioCompositionId:S,durationFrames:p,stills:u,awaitingConfirmation:!0};return e.result({...M}),M}l(mo,"runStill");export{v as MAX_STILLS,V as parseFrameSpec,mo as runStill,Q as sampleDurationFrames,H as studioCompositionId};
1
+ var R=Object.defineProperty;var l=(o,t)=>R(o,"name",{value:t,configurable:!0});import{existsSync as x,mkdirSync as _,mkdtempSync as O,rmSync as U}from"node:fs";import q from"node:os";import d from"node:path";import{EXIT as P,SkillError as j}from"../errors.js";import{emitProgress as b}from"../progress.js";import{usageError as y}from"./args.js";import{ensureRulesCurrent as B}from"./check.js";import{STILLS_DIR as G}from"./constants.js";import{Output as X,readJsonFile as T,requireProjectRoot as z,requireTemplateDir as J,validateTemplateName as Z}from"./project.js";import{runProcess as k}from"./proc.js";const h=30,H=5,v=6,L="node_modules/@remotion/cli/remotion-cli.js";function K(o,t){return`${o}-${t}`.replace(/[^A-Za-z0-9-]/g,"-")}l(K,"studioCompositionId");function Q(o,t){const i=new Set;let e=o[t];for(;e?.$ref;){if(i.has(e.$ref))return{};i.add(e.$ref),e=o[e.$ref]}return e??{}}l(Q,"resolveSlot");function V(o,t,i){const e=Array.isArray(t?.scenes)?t.scenes:[],a=o.slotMapping??{},r=e.find(m=>m.purpose&&Q(a,m.purpose).compositionId===i.compositionId)??e.find(m=>m.purpose===i.slot)??e[0],s=typeof r?.duration=="number"&&r.duration>0?r.duration:o.supportedDurations?.min??H;return Math.max(1,Math.round(s*h))}l(V,"sampleDurationFrames");function W(o,t){const i=t-1,e=o.split(",").map(r=>r.trim()).filter(Boolean);if(e.length===0)throw y("--frames 不能为空");const a=[];for(const r of e){let s;if(r==="start")s=0;else if(r==="mid")s=Math.floor(i/2);else if(r==="end")s=i;else if(/^\d+(\.\d+)?s$/.test(r))s=Math.round(Number(r.slice(0,-1))*h);else if(/^\d+$/.test(r))s=Number(r);else throw y(`--frames 取值不合法: '${r}'(可用 start / mid / end / 帧号 / 秒数如 2.5s)`);s=Math.min(Math.max(0,s),i),a.some(m=>m.frame===s)||a.push({label:r,frame:s})}if(a.length>v)throw y(`--frames 最多 ${v} 帧,收到 ${a.length} 帧`);return a}l(W,"parseFrameSpec");async function lo(o){const t=new X(!!o.json),i=Z(o.name),e=z(o.cwd),a=J(e,i);await B(e);const r=d.join(e,L);if(!x(r))throw new j(`❌ 找不到 ${L}:工程依赖未安装。在工程根目录运行 npm install 后重试。`,P.USAGE);const s=T(d.join(a,"template.json")),m=Array.isArray(s.compositions)?s.compositions:[],f=o.compositionId?m.find(n=>n.compositionId===o.compositionId):m[0];if(!f)throw new j(o.compositionId?`❌ composition "${o.compositionId}" 不在该模板中。可选: ${m.map(n=>n.compositionId).join(", ")}`:"❌ template.json 未声明 compositions",P.USAGE);const D=d.join(a,"dsl-example.json"),N=x(D)?T(D):null,p=V(s,N,f),$=W(o.frames??"start,mid,end",p),S=K(i,String(f.compositionId)),A=d.resolve(e,o.outDir??d.join(G,i));_(A,{recursive:!0}),t.info(`模板 ${i}`),t.info(`composition ${S}(${(p/h).toFixed(1)}s,${p} 帧)`),t.info(`截帧 ${$.map(n=>`${n.label}=${n.frame}`).join(", ")}`),t.info("首次运行会下载 headless Chrome,可能需要几分钟。");const E=l((n,g)=>new j(`❌ ${n}失败(remotion 退出码 ${g},错误详情见上方输出)。
2
+ 用 npm run dev 打开 Remotion Studio 定位问题,或先运行 remixmate template check。`),"fail"),w=O(d.join(q.tmpdir(),"remixmate-still-")),u=[];try{b({phase:"template-still:bundle",progress:0});const n=await k(process.execPath,[r,"bundle","src/index.ts","--out-dir",w,"--log=error"],{cwd:e,stdio:"stderr"});if(n.code!==0)throw E("打包工程",n.code);for(const[g,c]of $.entries()){b({phase:"template-still:render",progress:(g+1)/($.length+1),frame:c.frame});const I=d.join(A,`${i}-f${String(c.frame).padStart(5,"0")}.png`),C=[r,"still",w,S,I,`--frame=${c.frame}`,"--log=error"];o.scale!==void 0&&C.push(`--scale=${o.scale}`);const F=await k(process.execPath,C,{cwd:e,stdio:"stderr"});if(F.code!==0||!x(I))throw E(`第 ${c.frame} 帧渲染`,F.code);u.push({label:c.label,frame:c.frame,timeSec:Number((c.frame/h).toFixed(2)),path:I})}}finally{U(w,{recursive:!0,force:!0})}b({phase:"template-still:done",progress:1,stills:u.map(n=>n.path),awaitingConfirmation:!0}),t.info(`
3
+ ✅ 关键帧已生成:`);for(const n of u)t.info(` ${n.label.padEnd(6)} ${n.timeSec}s ${n.path}`);t.info(`
4
+ ⏸ 确认点:请把这些图片展示给用户确认画面效果,按反馈修改后可重新截帧;用户确认后再做远程预览。`);const M={ok:!0,name:i,compositionId:String(f.compositionId),studioCompositionId:S,durationFrames:p,stills:u,awaitingConfirmation:!0};return t.result({...M}),M}l(lo,"runStill");export{v as MAX_STILLS,W as parseFrameSpec,lo as runStill,V as sampleDurationFrames,K as studioCompositionId};
@@ -1,4 +1,4 @@
1
- var F=Object.defineProperty;var l=(e,r)=>F(e,"name",{value:r,configurable:!0});import{cpSync as L,existsSync as p,mkdirSync as N,mkdtempSync as P,readFileSync as R,renameSync as _,rmSync as y,writeFileSync as S}from"node:fs";import T from"node:os";import n from"node:path";import{EXIT as B,SkillError as g}from"../errors.js";import{emitProgress as k}from"../progress.js";import{DOC_FILES as C,RULES_DIR as u,RULES_VERSION_FILE as U,STARTER_VERSION as H}from"./constants.js";import{obtainStarterTarball as J}from"./init.js";import{Output as q,compareVersions as z,readRulesVersion as A,requireProjectRoot as X}from"./project.js";import{runProcess as G}from"./proc.js";function K(e,r){const i={...e.devDependencies,...e.dependencies},c={...r.devDependencies,...r.dependencies};return Object.entries(c).filter(([o,s])=>i[o]!==s).map(([o,s])=>({name:o,project:i[o]??null,starter:s})).sort((o,s)=>o.name.localeCompare(s.name))}l(K,"diffDependencies");function b(e){try{return JSON.parse(R(e,"utf8"))}catch{return{}}}l(b,"readPkg");async function ie(e={}){const r=new q(!!e.json),i=X(e.cwd),c=A(i),o=H;if(c&&z(c,o)>=0&&!e.force){r.info(`✅ 本地校验规则已是最新(starter ${c},当前 cli 对应 ${o})。`);const a={ok:!0,from:c,to:o,upgraded:!1,dryRun:!!e.dryRun,docs:[],dependencyHints:[]};return r.result({...a}),a}const s=P(n.join(T.tmpdir(),"remixmate-upgrade-"));try{k({phase:"template-upgrade:fetch",progress:.2});const{file:a,label:w}=await J(s,r),f=n.join(s,"starter");N(f,{recursive:!0});const j=await G("tar",["-xzf",a,"-C",f,"--strip-components=1"],{stdio:"pipe"});if(j.code!==0)throw new g(`❌ 解压 starter 失败(tar 退出码 ${j.code}): ${j.stderr.trim().slice(0,300)}`);const v=n.join(f,u);if(!p(n.join(v,"check","index.mjs")))throw new g(`❌ ${w} 里没有 ${u}/check/index.mjs,不是有效的 starter 包`);const $=e.withDocs?C.filter(t=>p(n.join(f,t))):[],x=K(b(n.join(i,"package.json")),b(n.join(f,"package.json")));r.info(`升级本地校验规则:starter ${c??"(未知版本)"} → ${o}(${w})`),r.info(` 替换 ${u}/(check 脚本、schema、安全策略)`);for(const t of $)r.info(` 刷新 ${t}(内容不同时旧文件备份为 ${t}.orig)`);if(r.info(" 不改动 templates/、src/、examples/、package.json"),!e.dryRun){k({phase:"template-upgrade:apply",progress:.6});const t=n.join(i,u),d=n.join(i,`${u}.upgrading`),h=n.join(i,`${u}.previous`);y(d,{recursive:!0,force:!0}),y(h,{recursive:!0,force:!0}),L(v,d,{recursive:!0});const O=n.join(d,n.relative(u,U));p(O)||S(O,JSON.stringify({starterVersion:o},null,2)+`
2
- `,"utf8"),p(t)&&_(t,h),_(d,t),y(h,{recursive:!0,force:!0});for(const D of $){const m=n.join(i,D),I=R(n.join(f,D),"utf8");if(p(m)){const V=R(m,"utf8");if(V===I)continue;S(`${m}.orig`,V,"utf8")}S(m,I,"utf8")}}for(const t of x)r.warn(` ⚠ 新 starter 钉的 ${t.name}@${t.starter},你的工程是 ${t.project??"(未安装)"}。需要时手动执行 npm i ${t.name}@${t.starter}(升级 core 可能影响模板效果,先 check + preview 再发布)。`);k({phase:"template-upgrade:done",progress:1}),r.info(e.dryRun?`
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 看新规则下是否还有问题。`);const E={ok:!0,from:c,to:o,upgraded:!e.dryRun,dryRun:!!e.dryRun,docs:$,dependencyHints:x};return r.result({...E}),E}catch(a){throw a instanceof g?a:new g(`❌ 升级失败: ${a.message}`,B.ERROR)}finally{y(s,{recursive:!0,force:!0})}}l(ie,"runUpgrade");export{K as diffDependencies,ie as runUpgrade};
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.10.0",
3
+ "version": "0.11.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",
@@ -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
- 2. **Open the project and read `<dir>/AGENTS.md`** before writing any code. If the session is not
69
- rooted in `<dir>`, read that file by absolute path (or suggest reopening the session there).
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
@@ -109,15 +113,18 @@ Exit code 3 from `publish` is the checkpoint, not a failure: nothing was uploade
109
113
  ```
110
114
  `remixmate template list` shows the user's private templates.
111
115
 
112
- ## Keeping the local rules current
116
+ ## Staying current (mostly automatic)
113
117
 
114
- The project's check rules (`.remixmate/`) are copied in at init time. When `check` / `preview` /
115
- `publish` print "本地校验规则来自 starter …" the platform rules have moved on — run:
116
- ```bash
117
- remixmate template upgrade # replaces .remixmate/ only; templates/ untouched
118
- remixmate template upgrade --with-docs # also refresh AGENTS.md / CLAUDE.md (old copies kept as *.orig)
119
- ```
120
- Then re-run `check`. Dependency changes (e.g. a newer template-core) are reported, never applied.
118
+ - **Project rules + workflow**: `check` / `still` / `preview` / `publish` update `.remixmate/`
119
+ (check rules and WORKFLOW.md) by themselves when it is older than the cli, and print
120
+ 「↻ 已自动更新本地校验规则与工作流」. After that line, re-read `.remixmate/WORKFLOW.md`.
121
+ `templates/` is never touched. Offline it only warns; `remixmate template upgrade` is the
122
+ manual fallback.
123
+ - **AGENTS.md / CLAUDE.md**: updated along with it unless the user edited them; then the new
124
+ version is written to `*.new` — mention it, don't merge without asking.
125
+ - **The cli itself**: when any output says 「remixmate 有新版本」, ask the user, run
126
+ `npm i -g @remixmate/cli@latest`, then re-run the command.
127
+ - **Dependencies** (template-core, remotion) are reported, never changed automatically.
121
128
 
122
129
  ## Notes
123
130
 
@@ -2,6 +2,6 @@
2
2
  "skillName": "template-authoring",
3
3
  "repoName": "agent-skill-media-maker",
4
4
  "skillId": "pending-template-authoring",
5
- "version": "V4",
5
+ "version": "V5",
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
  }