Files
video-create/.claude/skills/video-from-script/scripts/lib/cmd-validate.js
sion123 86b9b7948d feat(video-from-script): 重构工作流为子Agent分步执行并新增提示词模板系统
将视频制作工作流拆分为独立子步骤:分镜 → 图片提示词 → 生图 → 视频提示词 → 生视频 → 成片,每步由子Agent独立执行。引入prompts/目录统一管理提示词模板(分镜.md、图片提示词.md、视频提示词.md),通过account.json的storyboardPrompt/imageStylePrompt/videoStylePrompt字段引用。

变更内容:
- 新增confirmed机制和pipeline.js confirm命令,生图后必须人工确认才能继续
- manifest schema改用shotDesc/narration/duration/directorRef替代旧字段
- 文件命名规则从keyword改为slug(从shotDesc/narration派生)
- 删除旧的storyboard-rules.md和prompt-rules.md
- pipeline.js脚本拆分为lib/目录下的独立模块(cmd-init/cmd-confirm/cmd-validate/phase-*)
- 新增cmd-create-account支持一键创建带prompts目录的账号
- capcut_assemble支持narration字段替代text作为字幕源
- 新增.gitclaude/settings.json权限配置
2026-04-30 21:18:31 +08:00

111 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Command: validate — 校验 manifest.json 完整性
* Command: validate-account — 校验账号目录完整性
*/
const fs = require('fs')
const path = require('path')
const { loadManifest, ACCOUNTS_DIR } = require('./pipeline-utils')
function validateManifest(manifestPath) {
const issues = []
if (!fs.existsSync(manifestPath)) {
console.error(`错误: manifest 不存在: ${manifestPath}`)
process.exit(1)
}
let manifest
try {
manifest = loadManifest(manifestPath)
} catch (e) {
console.error(`错误: JSON 解析失败: ${e.message}`)
process.exit(1)
}
if (!manifest.account) issues.push('缺少顶层 account')
if (!manifest.imageModel) issues.push('缺少顶层 imageModel可选: gemini, mj')
if (!manifest.format) issues.push('缺少顶层 format如 9:16')
if (!manifest.items || !Array.isArray(manifest.items)) issues.push('缺少顶层 items 数组')
if (!manifest.mode) issues.push('缺少顶层 modesingle 或 framePair')
if (manifest.items && Array.isArray(manifest.items)) {
manifest.items.forEach((item, i) => {
const prefix = `items[${i}]`
if (!item.narration && !item.text) issues.push(`${prefix} 缺少 narration 或 text中文旁白`)
if (!item.shotDesc) issues.push(`${prefix} 缺少 shotDesc分镜描述`)
if (!item.imagePrompt) issues.push(`${prefix} 缺少 imagePrompt`)
if (manifest.mode === 'framePair' && !item.lastFramePrompt) {
issues.push(`${prefix} 首尾帧模式缺少 lastFramePromptimagePrompt 作为第一帧)`)
}
if (item.status && !['pending', 'generating', 'done', 'failed'].includes(item.status)) {
issues.push(`${prefix} status 无效: ${item.status}`)
}
})
}
if (issues.length === 0) {
console.log(`✓ Manifest 校验通过: ${manifestPath}`)
console.log(` ${manifest.items?.length || 0} items, account=${manifest.account}, mode=${manifest.mode}`)
} else {
console.error(`✗ 发现 ${issues.length} 个问题:`)
issues.forEach(issue => console.error(` - ${issue}`))
process.exit(1)
}
}
function validateAccount(accountId) {
const issues = []
const accountDir = path.join(ACCOUNTS_DIR, accountId)
if (!fs.existsSync(accountDir)) { console.error(`错误: 账号不存在: ${accountDir}`); process.exit(1) }
const accountPath = path.join(accountDir, 'account.json')
if (!fs.existsSync(accountPath)) { console.error('错误: 缺少 account.json'); process.exit(1) }
let config
try { config = JSON.parse(fs.readFileSync(accountPath, 'utf-8')) }
catch (e) { console.error(`错误: JSON 解析失败: ${e.message}`); process.exit(1) }
if (config.id !== accountId) issues.push(`id 不匹配: json="${config.id}" vs 目录="${accountId}"`)
if (!config.name) issues.push('缺少 name')
if (!config.imageModel) issues.push('缺少 imageModel')
if (!config.defaultFormat) issues.push('缺少 defaultFormat')
const refDir = path.join(accountDir, 'references')
const styles = config.styles || {}
const hasStyleRefs = Object.values(styles).some(s => s.references && s.references.length > 0)
const localRefs = fs.existsSync(refDir)
? fs.readdirSync(refDir).filter(f => /\.(png|jpg|jpeg|webp)$/i.test(f))
: []
if (localRefs.length === 0 && !hasStyleRefs) {
issues.push('无参考图(建议至少 1 张)')
}
const stylesDir = path.join(accountDir, 'styles')
const styleFiles = fs.existsSync(stylesDir)
? fs.readdirSync(stylesDir).filter(f => f.endsWith('.md'))
: []
if (styleFiles.length === 0) {
issues.push('无风格文件styles/ 下至少 1 个 .md')
}
for (const [sName, sConf] of Object.entries(styles)) {
for (const ref of (sConf.references || [])) {
if (!ref.url) issues.push(`styles.${sName}: 参考图 ${ref.file} 缺少 url未上传 OSS`)
}
}
if (issues.length === 0) {
console.log(`✓ 账号校验通过: ${accountId}`)
console.log(` ${config.name}, 模型: ${config.imageModel}+${config.videoModel || '(未指定)'}`)
console.log(` 参考图: ${localRefs.length} 本地, 风格: ${styleFiles.length}`)
} else {
console.error(`✗ 发现 ${issues.length} 个问题:`)
issues.forEach(i => console.error(` - ${i}`))
process.exit(1)
}
}
module.exports = { validateManifest, validateAccount }