@aswless_854771076/ai_short_studio_cli 0.1.95 → 0.1.97

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/README.md +9 -8
  2. package/dist/auth/login-provider.d.ts +2 -0
  3. package/dist/auth/login-provider.js +3 -0
  4. package/dist/base-command.d.ts +6 -0
  5. package/dist/base-command.js +39 -1
  6. package/dist/commands/auth/login.d.ts +2 -1
  7. package/dist/commands/auth/login.js +5 -2
  8. package/dist/commands/auth/logout.d.ts +1 -0
  9. package/dist/commands/auth/signup.d.ts +1 -0
  10. package/dist/commands/auth/status.d.ts +1 -0
  11. package/dist/commands/canvas/apply.d.ts +1 -0
  12. package/dist/commands/canvas/asset/get.d.ts +1 -0
  13. package/dist/commands/canvas/asset/list.d.ts +1 -0
  14. package/dist/commands/canvas/asset/select.d.ts +1 -0
  15. package/dist/commands/canvas/asset/upload.d.ts +1 -0
  16. package/dist/commands/canvas/continuity/analyze.d.ts +1 -0
  17. package/dist/commands/canvas/edge/connect.d.ts +1 -0
  18. package/dist/commands/canvas/edge/disconnect.d.ts +1 -0
  19. package/dist/commands/canvas/edge/list.d.ts +1 -0
  20. package/dist/commands/canvas/episode/create.d.ts +1 -0
  21. package/dist/commands/canvas/episode/list.d.ts +1 -0
  22. package/dist/commands/canvas/folder/copy-cross-project.d.ts +1 -0
  23. package/dist/commands/canvas/folder/create.d.ts +1 -0
  24. package/dist/commands/canvas/folder/delete.d.ts +1 -0
  25. package/dist/commands/canvas/folder/list.d.ts +1 -0
  26. package/dist/commands/canvas/folder/move.d.ts +1 -0
  27. package/dist/commands/canvas/folder/rename.d.ts +1 -0
  28. package/dist/commands/canvas/folder/restore.d.ts +1 -0
  29. package/dist/commands/canvas/get.d.ts +1 -0
  30. package/dist/commands/canvas/inspect.d.ts +1 -0
  31. package/dist/commands/canvas/node/add.d.ts +1 -0
  32. package/dist/commands/canvas/node/delete.d.ts +1 -0
  33. package/dist/commands/canvas/node/edit-resource.d.ts +2 -0
  34. package/dist/commands/canvas/node/edit-resource.js +9 -1
  35. package/dist/commands/canvas/node/edit-shot.d.ts +1 -0
  36. package/dist/commands/canvas/node/get.d.ts +1 -0
  37. package/dist/commands/canvas/node/list.d.ts +1 -0
  38. package/dist/commands/canvas/node/quote.d.ts +17 -0
  39. package/dist/commands/canvas/node/quote.js +13 -0
  40. package/dist/commands/canvas/node/run.d.ts +12 -0
  41. package/dist/commands/canvas/node/run.js +41 -3
  42. package/dist/commands/canvas/node/types.d.ts +1 -0
  43. package/dist/commands/canvas/node/update-many.d.ts +1 -0
  44. package/dist/commands/canvas/node/update.d.ts +1 -0
  45. package/dist/commands/canvas/settings/get.d.ts +1 -0
  46. package/dist/commands/canvas/settings/set.d.ts +24 -0
  47. package/dist/commands/canvas/settings/set.js +60 -1
  48. package/dist/commands/config/ai-provider/capabilities.d.ts +34 -0
  49. package/dist/commands/config/ai-provider/capabilities.js +36 -2
  50. package/dist/commands/config/ai-provider/get.d.ts +1 -0
  51. package/dist/commands/config/ai-provider/list.d.ts +1 -0
  52. package/dist/commands/config/ai-provider/models.d.ts +1 -0
  53. package/dist/commands/config/ai-provider/set.d.ts +1 -0
  54. package/dist/commands/config/ai-provider/test.d.ts +1 -0
  55. package/dist/commands/config/get.d.ts +1 -0
  56. package/dist/commands/config/set.d.ts +1 -0
  57. package/dist/commands/preflight.d.ts +1 -0
  58. package/dist/commands/project/create.d.ts +1 -0
  59. package/dist/commands/project/delete.d.ts +1 -0
  60. package/dist/commands/project/get.d.ts +1 -0
  61. package/dist/commands/project/list.d.ts +1 -0
  62. package/dist/commands/project/provider/get.d.ts +1 -0
  63. package/dist/commands/project/provider/set.d.ts +1 -0
  64. package/dist/commands/project/provider/test.d.ts +1 -0
  65. package/dist/commands/project/update.d.ts +1 -0
  66. package/dist/commands/skill/install.d.ts +1 -0
  67. package/dist/commands/skill/install.js +1 -0
  68. package/dist/commands/skill/path.d.ts +1 -0
  69. package/dist/commands/skill/path.js +1 -0
  70. package/dist/commands/skill/status.d.ts +1 -0
  71. package/dist/commands/skill/status.js +1 -0
  72. package/dist/commands/task/cancel.d.ts +1 -0
  73. package/dist/commands/task/get.d.ts +1 -0
  74. package/dist/commands/task/wait.d.ts +1 -0
  75. package/dist/domain/vvicat-api.d.ts +3 -0
  76. package/dist/domain/vvicat-api.js +3 -0
  77. package/dist/output/formatter.d.ts +11 -0
  78. package/dist/output/formatter.js +20 -0
  79. package/dist/skill/command-flags.js +7 -4
  80. package/dist/skill/skill-manager.d.ts +88 -15
  81. package/dist/skill/skill-manager.js +61 -16
  82. package/package.json +1 -1
  83. package/scripts/postinstall.mjs +7 -12
  84. package/skills/using-vvicat-ai-short-studio-cli/SKILL.md +25 -54
  85. package/skills/using-vvicat-ai-short-studio-cli/references/b-roll-and-screen.md +9 -0
  86. package/skills/using-vvicat-ai-short-studio-cli/references/caption-and-graphics-direction.md +11 -0
  87. package/skills/using-vvicat-ai-short-studio-cli/references/commands/canvas.md +3 -1
  88. package/skills/using-vvicat-ai-short-studio-cli/references/commands.md +1 -1
  89. package/skills/using-vvicat-ai-short-studio-cli/references/composition-and-shot-review.md +17 -0
  90. package/skills/using-vvicat-ai-short-studio-cli/references/craft-index.md +20 -0
  91. package/skills/using-vvicat-ai-short-studio-cli/references/drama-canvas.md +19 -0
  92. package/skills/using-vvicat-ai-short-studio-cli/references/editing-delivery.md +1 -1
  93. package/skills/using-vvicat-ai-short-studio-cli/references/format-relationships.md +15 -0
  94. package/skills/using-vvicat-ai-short-studio-cli/references/h3-prompts.md +7 -0
  95. package/skills/using-vvicat-ai-short-studio-cli/references/h3-video-delivery.md +1 -1
  96. package/skills/using-vvicat-ai-short-studio-cli/references/image-and-reference-direction.md +9 -0
  97. package/skills/using-vvicat-ai-short-studio-cli/references/licensed-music.md +9 -0
  98. package/skills/using-vvicat-ai-short-studio-cli/references/licenses/oh-my-short-drama-MIT.txt +21 -0
  99. package/skills/using-vvicat-ai-short-studio-cli/references/media-provenance-and-review.md +22 -0
  100. package/skills/using-vvicat-ai-short-studio-cli/references/paid-plan.md +7 -0
  101. package/skills/using-vvicat-ai-short-studio-cli/references/production-specialties.md +25 -0
  102. package/skills/using-vvicat-ai-short-studio-cli/references/recreation.md +21 -0
  103. package/skills/using-vvicat-ai-short-studio-cli/references/reference-analysis-and-timing.md +19 -0
  104. package/skills/using-vvicat-ai-short-studio-cli/references/remotion-editing.md +18 -0
  105. package/skills/using-vvicat-ai-short-studio-cli/references/shot-production.md +9 -0
  106. package/skills/using-vvicat-ai-short-studio-cli/references/sound-and-captions.md +7 -0
  107. package/skills/using-vvicat-ai-short-studio-cli/references/sources.md +6 -0
  108. package/skills/using-vvicat-ai-short-studio-cli/references/special-workflows.md +2 -2
  109. package/skills/using-vvicat-ai-short-studio-cli/references/story-craft/canvas-production.md +9 -0
  110. package/skills/using-vvicat-ai-short-studio-cli/references/story-craft/guide.md +7 -0
  111. package/skills/using-vvicat-ai-short-studio-cli/references/story-craft/index.md +13 -0
  112. package/skills/using-vvicat-ai-short-studio-cli/references/video-and-performance-direction.md +9 -0
  113. package/skills/using-vvicat-ai-short-studio-cli/references/voice-and-sound-direction.md +9 -0
  114. package/skills/using-vvicat-ai-short-studio-cli/references/writing-polish.md +15 -0
  115. package/skills/humanizer/SKILL.md +0 -412
  116. package/skills/short-drama/README.md +0 -508
  117. package/skills/short-drama/SKILL.md +0 -525
  118. /package/skills/{humanizer/LICENSE → using-vvicat-ai-short-studio-cli/references/licenses/humanizer-MIT.txt} +0 -0
  119. /package/skills/{short-drama/LICENSE → using-vvicat-ai-short-studio-cli/references/licenses/short-drama-MIT.txt} +0 -0
  120. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/compliance-checklist.md +0 -0
  121. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/genre-guide.md +0 -0
  122. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/hook-design.md +0 -0
  123. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/opening-rules.md +0 -0
  124. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/paywall-design.md +0 -0
  125. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/rhythm-curve.md +0 -0
  126. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/satisfaction-matrix.md +0 -0
  127. /package/skills/{short-drama/references → using-vvicat-ai-short-studio-cli/references/story-craft}/villain-design.md +0 -0
@@ -1,3 +1,23 @@
1
+ import { writeFile } from 'node:fs/promises';
1
2
  export function formatJson(value) {
2
3
  return JSON.stringify(value, null, 2);
3
4
  }
5
+ /**
6
+ * P0-4(应用层旁路):Windows 控制台 chcp=437 时,进程启动那一刻 stdout 句柄
7
+ * 已按旧代码页打开,进程内改 Console.OutputEncoding 无效——UTF-8 字节被按
8
+ * CP437 解码,中文变希腊字母。环境层只能靠 chcp 65001(须在启动 Node 前设置),
9
+ * 应用层可靠的做法是绕开终端句柄:由 Node 直接以 utf-8 写文件。
10
+ *
11
+ * 优先级:--output flag > CLI_OUTPUT_FILE 环境变量 > 不启用(保持原有 stdout 行为)。
12
+ */
13
+ export function resolveOutputFile(flagValue, env = process.env) {
14
+ const flag = flagValue?.trim();
15
+ if (flag)
16
+ return flag;
17
+ const fromEnv = env.CLI_OUTPUT_FILE?.trim();
18
+ return fromEnv || undefined;
19
+ }
20
+ /** 以 utf-8 无 BOM 落盘,保证字节级可核对(中文不被转义、不受终端代码页影响)。 */
21
+ export async function writeCliOutput(file, value) {
22
+ await writeFile(file, formatJson(value), { encoding: 'utf8' });
23
+ }
@@ -1,14 +1,17 @@
1
1
  import { Flags } from '@oclif/core';
2
- import { BUNDLED_SKILL_NAMES, defaultSkillsRoot, normalizeSkillsRoot, } from './skill-manager.js';
2
+ import { BUNDLED_SKILL_NAMES, LEGACY_SKILL_NAMES, defaultSkillsRoot, normalizeSkillsRoot, } from './skill-manager.js';
3
3
  export const skillFlags = {
4
4
  target: Flags.string({ options: ['codex', 'agents'], default: 'codex', description: 'Agent 类型' }),
5
5
  directory: Flags.string({ description: '兼容旧版:主 Skill 的精确安装目录', exclusive: ['root'] }),
6
- root: Flags.string({ description: '全部内置 Skills 的根目录', exclusive: ['directory'] }),
7
- skill: Flags.string({ options: ['all', ...BUNDLED_SKILL_NAMES], default: 'all', description: '要管理的内置 Skill' }),
6
+ root: Flags.string({ description: '主 Skill 的根目录', exclusive: ['directory'] }),
7
+ skill: Flags.string({ options: ['all', ...BUNDLED_SKILL_NAMES, ...LEGACY_SKILL_NAMES], default: 'all', description: '要管理的 Skill;short-drama/humanizer 为弃用别名' }),
8
8
  };
9
9
  export function resolveSkillsRoot(flags) {
10
10
  return normalizeSkillsRoot(flags.root || defaultSkillsRoot(flags.target));
11
11
  }
12
12
  export function selectedSkillNames(skill) {
13
- return skill === 'all' ? BUNDLED_SKILL_NAMES : [skill];
13
+ if (LEGACY_SKILL_NAMES.includes(skill)) {
14
+ process.stderr.write(`--skill ${skill} 已弃用;改为使用 ${BUNDLED_SKILL_NAMES[0]}。\n`);
15
+ }
16
+ return BUNDLED_SKILL_NAMES;
14
17
  }
@@ -1,4 +1,5 @@
1
- export declare const BUNDLED_SKILL_NAMES: readonly ["using-vvicat-ai-short-studio-cli", "short-drama", "humanizer"];
1
+ export declare const BUNDLED_SKILL_NAMES: readonly ["using-vvicat-ai-short-studio-cli"];
2
+ export declare const LEGACY_SKILL_NAMES: readonly ["short-drama", "humanizer"];
2
3
  export type BundledSkillName = typeof BUNDLED_SKILL_NAMES[number];
3
4
  export type SkillTarget = 'codex' | 'agents';
4
5
  export declare function bundledSkillPath(name?: BundledSkillName): string;
@@ -6,7 +7,16 @@ export declare function defaultSkillsRoot(target: SkillTarget): string;
6
7
  export declare function defaultSkillTarget(target: SkillTarget): string;
7
8
  export declare function normalizeSkillsRoot(path: string): string;
8
9
  export declare function skillStatus(target: string): Promise<{
9
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
10
+ legacySkills: {
11
+ name: "short-drama" | "humanizer";
12
+ target: string;
13
+ installed: boolean;
14
+ managed: boolean;
15
+ modified: boolean;
16
+ action: string;
17
+ requiresAttention: boolean;
18
+ }[];
19
+ name: "using-vvicat-ai-short-studio-cli";
10
20
  target: string;
11
21
  installed: boolean;
12
22
  managed: boolean;
@@ -24,7 +34,7 @@ export declare function installSkills(input: {
24
34
  current: boolean;
25
35
  skills: ({
26
36
  action: "unchanged";
27
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
37
+ name: "using-vvicat-ai-short-studio-cli";
28
38
  target: string;
29
39
  installed: boolean;
30
40
  managed: boolean;
@@ -34,7 +44,7 @@ export declare function installSkills(input: {
34
44
  modified: boolean;
35
45
  } | {
36
46
  action: "skipped";
37
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
47
+ name: "using-vvicat-ai-short-studio-cli";
38
48
  target: string;
39
49
  installed: boolean;
40
50
  managed: boolean;
@@ -44,7 +54,7 @@ export declare function installSkills(input: {
44
54
  modified: boolean;
45
55
  } | {
46
56
  action: "updated" | "installed";
47
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
57
+ name: "using-vvicat-ai-short-studio-cli";
48
58
  target: string;
49
59
  installed: boolean;
50
60
  managed: boolean;
@@ -53,13 +63,22 @@ export declare function installSkills(input: {
53
63
  installedVersion: string | null;
54
64
  modified: boolean;
55
65
  })[];
66
+ legacySkills: {
67
+ name: "short-drama" | "humanizer";
68
+ target: string;
69
+ installed: boolean;
70
+ managed: boolean;
71
+ modified: boolean;
72
+ action: string;
73
+ requiresAttention: boolean;
74
+ }[];
56
75
  }>;
57
76
  export declare function autoInstallSkills(root: string): Promise<{
58
77
  root: string;
59
78
  current: boolean;
60
79
  skills: ({
61
80
  action: "unchanged";
62
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
81
+ name: "using-vvicat-ai-short-studio-cli";
63
82
  target: string;
64
83
  installed: boolean;
65
84
  managed: boolean;
@@ -69,7 +88,7 @@ export declare function autoInstallSkills(root: string): Promise<{
69
88
  modified: boolean;
70
89
  } | {
71
90
  action: "skipped";
72
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
91
+ name: "using-vvicat-ai-short-studio-cli";
73
92
  target: string;
74
93
  installed: boolean;
75
94
  managed: boolean;
@@ -79,7 +98,7 @@ export declare function autoInstallSkills(root: string): Promise<{
79
98
  modified: boolean;
80
99
  } | {
81
100
  action: "updated" | "installed";
82
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
101
+ name: "using-vvicat-ai-short-studio-cli";
83
102
  target: string;
84
103
  installed: boolean;
85
104
  managed: boolean;
@@ -88,12 +107,21 @@ export declare function autoInstallSkills(root: string): Promise<{
88
107
  installedVersion: string | null;
89
108
  modified: boolean;
90
109
  })[];
110
+ legacySkills: {
111
+ name: "short-drama" | "humanizer";
112
+ target: string;
113
+ installed: boolean;
114
+ managed: boolean;
115
+ modified: boolean;
116
+ action: string;
117
+ requiresAttention: boolean;
118
+ }[];
91
119
  }>;
92
120
  export declare function skillStatuses(rootInput: string): Promise<{
93
121
  root: string;
94
122
  current: boolean;
95
123
  skills: {
96
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
124
+ name: "using-vvicat-ai-short-studio-cli";
97
125
  target: string;
98
126
  installed: boolean;
99
127
  managed: boolean;
@@ -102,12 +130,30 @@ export declare function skillStatuses(rootInput: string): Promise<{
102
130
  installedVersion: string | null;
103
131
  modified: boolean;
104
132
  }[];
133
+ legacySkills: {
134
+ name: "short-drama" | "humanizer";
135
+ target: string;
136
+ installed: boolean;
137
+ managed: boolean;
138
+ modified: boolean;
139
+ action: string;
140
+ requiresAttention: boolean;
141
+ }[];
105
142
  }>;
106
143
  export declare function installSkill(input: {
107
144
  target: string;
108
145
  force?: boolean;
109
146
  }): Promise<{
110
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
147
+ legacySkills: {
148
+ name: "short-drama" | "humanizer";
149
+ target: string;
150
+ installed: boolean;
151
+ managed: boolean;
152
+ modified: boolean;
153
+ action: string;
154
+ requiresAttention: boolean;
155
+ }[];
156
+ name: "using-vvicat-ai-short-studio-cli";
111
157
  target: string;
112
158
  installed: boolean;
113
159
  managed: boolean;
@@ -117,7 +163,16 @@ export declare function installSkill(input: {
117
163
  modified: boolean;
118
164
  }>;
119
165
  export declare function autoInstallSkill(target: string): Promise<{
120
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
166
+ legacySkills: {
167
+ name: "short-drama" | "humanizer";
168
+ target: string;
169
+ installed: boolean;
170
+ managed: boolean;
171
+ modified: boolean;
172
+ action: string;
173
+ requiresAttention: boolean;
174
+ }[];
175
+ name: "using-vvicat-ai-short-studio-cli";
121
176
  target: string;
122
177
  installed: boolean;
123
178
  managed: boolean;
@@ -125,9 +180,18 @@ export declare function autoInstallSkill(target: string): Promise<{
125
180
  packageVersion: string;
126
181
  installedVersion: string | null;
127
182
  modified: boolean;
128
- action: "unchanged";
183
+ action: "skipped";
129
184
  } | {
130
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
185
+ legacySkills: {
186
+ name: "short-drama" | "humanizer";
187
+ target: string;
188
+ installed: boolean;
189
+ managed: boolean;
190
+ modified: boolean;
191
+ action: string;
192
+ requiresAttention: boolean;
193
+ }[];
194
+ name: "using-vvicat-ai-short-studio-cli";
131
195
  target: string;
132
196
  installed: boolean;
133
197
  managed: boolean;
@@ -135,9 +199,18 @@ export declare function autoInstallSkill(target: string): Promise<{
135
199
  packageVersion: string;
136
200
  installedVersion: string | null;
137
201
  modified: boolean;
138
- action: "skipped";
202
+ action: "unchanged";
139
203
  } | {
140
- name: "using-vvicat-ai-short-studio-cli" | "short-drama" | "humanizer";
204
+ legacySkills: {
205
+ name: "short-drama" | "humanizer";
206
+ target: string;
207
+ installed: boolean;
208
+ managed: boolean;
209
+ modified: boolean;
210
+ action: string;
211
+ requiresAttention: boolean;
212
+ }[];
213
+ name: "using-vvicat-ai-short-studio-cli";
141
214
  target: string;
142
215
  installed: boolean;
143
216
  managed: boolean;
@@ -1,9 +1,10 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { cp, mkdir, readFile, readdir, rename, rm, stat, writeFile } from 'node:fs/promises';
2
+ import { cp, lstat, mkdir, readFile, readdir, rename, rm, writeFile } from 'node:fs/promises';
3
3
  import { homedir } from 'node:os';
4
4
  import { basename, dirname, join, relative } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- export const BUNDLED_SKILL_NAMES = ['using-vvicat-ai-short-studio-cli', 'short-drama', 'humanizer'];
6
+ export const BUNDLED_SKILL_NAMES = ['using-vvicat-ai-short-studio-cli'];
7
+ export const LEGACY_SKILL_NAMES = ['short-drama', 'humanizer'];
7
8
  const PACKAGE_JSON = fileURLToPath(new URL('../../package.json', import.meta.url));
8
9
  const MANIFEST = '.vvicat-skill.json';
9
10
  export function bundledSkillPath(name = BUNDLED_SKILL_NAMES[0]) {
@@ -22,17 +23,60 @@ async function statusFor(name, target) {
22
23
  const packageVersion = await readPackageVersion();
23
24
  const bundledChecksum = await directoryChecksum(bundledSkillPath(name));
24
25
  const installed = await exists(target);
25
- const manifest = installed ? await readManifest(target) : null;
26
- const installedChecksum = installed ? await directoryChecksum(target) : null;
26
+ const directDirectory = installed && (await lstat(target)).isDirectory();
27
+ const manifest = directDirectory ? await readManifest(target) : null;
28
+ const installedChecksum = directDirectory ? await directoryChecksum(target) : null;
29
+ const extraEntries = directDirectory ? await hasUnhashedEntries(target) : false;
27
30
  return {
28
31
  name, target, installed, managed: Boolean(manifest),
29
- current: Boolean(manifest && manifest.packageVersion === packageVersion && installedChecksum === bundledChecksum),
32
+ current: Boolean(manifest && !extraEntries && manifest.packageVersion === packageVersion
33
+ && installedChecksum === manifest.checksum && installedChecksum === bundledChecksum),
30
34
  packageVersion, installedVersion: manifest?.packageVersion ?? null,
31
- modified: Boolean(manifest && installedChecksum !== manifest.checksum),
35
+ modified: Boolean(manifest && (extraEntries || installedChecksum !== manifest.checksum)),
32
36
  };
33
37
  }
38
+ async function legacyStatuses(root, cleanup) {
39
+ const results = [];
40
+ for (const name of LEGACY_SKILL_NAMES) {
41
+ const target = join(root, name);
42
+ const installed = await exists(target);
43
+ const manifest = installed ? await readManifest(target) : null;
44
+ const skillHeader = installed ? await readFile(join(target, 'SKILL.md'), 'utf8').catch(() => '') : '';
45
+ const directDirectory = installed && (await lstat(target)).isDirectory();
46
+ const managed = Boolean(directDirectory && manifest?.checksum && manifest?.packageVersion && skillHeader.startsWith(`---\nname: ${name}\n`));
47
+ let modified = installed;
48
+ if (managed && manifest) {
49
+ try {
50
+ modified = await directoryChecksum(target) !== manifest.checksum || await hasUnhashedEntries(target);
51
+ }
52
+ catch {
53
+ // 无法完整读取旧目录时,按用户修改处理,避免安装主 Skill 后误删。
54
+ modified = true;
55
+ }
56
+ }
57
+ const removed = cleanup && installed && managed && !modified;
58
+ if (removed)
59
+ await rm(target, { recursive: true });
60
+ results.push({ name, target, installed: installed && !removed, managed, modified, action: removed ? 'removed' : installed ? 'retained' : 'absent', requiresAttention: installed && !removed });
61
+ }
62
+ return results;
63
+ }
64
+ async function hasUnhashedEntries(root) {
65
+ // 旧版校验值只计普通文件;额外的空目录或符号链接也可能是用户修改,不能清理。
66
+ const entries = await readdir(root, { withFileTypes: true });
67
+ for (const entry of entries) {
68
+ if (entry.isDirectory()) {
69
+ const nested = join(root, entry.name);
70
+ if ((await readdir(nested)).length === 0 || await hasUnhashedEntries(nested))
71
+ return true;
72
+ }
73
+ else if (!entry.isFile())
74
+ return true;
75
+ }
76
+ return false;
77
+ }
34
78
  export async function skillStatus(target) {
35
- return statusFor(BUNDLED_SKILL_NAMES[0], target);
79
+ return { ...await statusFor(BUNDLED_SKILL_NAMES[0], target), legacySkills: await legacyStatuses(dirname(target), false) };
36
80
  }
37
81
  async function installOne(name, target) {
38
82
  const checksum = await directoryChecksum(bundledSkillPath(name));
@@ -75,7 +119,8 @@ export async function installSkills(input) {
75
119
  }
76
120
  results.push({ ...await installOne(name, target), action: current.installed ? 'updated' : 'installed' });
77
121
  }
78
- return { root, current: results.every((item) => item.current), skills: results };
122
+ const current = results.every((item) => item.current);
123
+ return { root, current, skills: results, legacySkills: await legacyStatuses(root, current) };
79
124
  }
80
125
  export function autoInstallSkills(root) {
81
126
  return installSkills({ root });
@@ -83,7 +128,7 @@ export function autoInstallSkills(root) {
83
128
  export async function skillStatuses(rootInput) {
84
129
  const root = normalizeSkillsRoot(rootInput);
85
130
  const skills = await Promise.all(BUNDLED_SKILL_NAMES.map((name) => statusFor(name, join(root, name))));
86
- return { root, current: skills.every((item) => item.current), skills };
131
+ return { root, current: skills.every((item) => item.current), skills, legacySkills: await legacyStatuses(root, false) };
87
132
  }
88
133
  export async function installSkill(input) {
89
134
  const name = BUNDLED_SKILL_NAMES[0];
@@ -94,19 +139,19 @@ export async function installSkill(input) {
94
139
  if (current.modified)
95
140
  throw new Error('已安装 Skill 已被修改;使用 --force 才能替换');
96
141
  }
97
- return installOne(name, input.target);
142
+ const result = await installOne(name, input.target);
143
+ return { ...result, legacySkills: await legacyStatuses(dirname(input.target), result.current) };
98
144
  }
99
145
  export async function autoInstallSkill(target) {
100
146
  const name = BUNDLED_SKILL_NAMES[0];
101
147
  const current = await statusFor(name, target);
102
- if (current.current)
103
- return { action: 'unchanged', ...current };
104
148
  if (current.installed && (!current.managed || current.modified))
105
- return { action: 'skipped', ...current };
106
- return { action: current.installed ? 'updated' : 'installed', ...await installOne(name, target) };
149
+ return { action: 'skipped', ...current, legacySkills: await legacyStatuses(dirname(target), false) };
150
+ const result = current.current ? { action: 'unchanged', ...current } : { action: current.installed ? 'updated' : 'installed', ...await installOne(name, target) };
151
+ return { ...result, legacySkills: await legacyStatuses(dirname(target), result.current) };
107
152
  }
108
153
  export function withLegacySkillFields(result) {
109
- return { ...result.skills[0], root: result.root, allCurrent: result.current, skills: result.skills };
154
+ return { ...result.skills[0], ...result, allCurrent: result.current };
110
155
  }
111
156
  async function directoryChecksum(root) {
112
157
  const files = await filesBelow(root);
@@ -144,5 +189,5 @@ async function readPackageVersion() {
144
189
  return JSON.parse(await readFile(PACKAGE_JSON, 'utf8')).version;
145
190
  }
146
191
  async function exists(path) {
147
- return stat(path).then(() => true, () => false);
192
+ return lstat(path).then(() => true, () => false);
148
193
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aswless_854771076/ai_short_studio_cli",
3
- "version": "0.1.95",
3
+ "version": "0.1.97",
4
4
  "description": "VVICAT 无限画布项目命令行工具",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -1,4 +1,4 @@
1
- import { dirname, resolve } from 'node:path'
1
+ import { resolve } from 'node:path'
2
2
  import { fileURLToPath } from 'node:url'
3
3
 
4
4
  const packageRoot = fileURLToPath(new URL('../', import.meta.url))
@@ -8,24 +8,19 @@ export function shouldAutoInstallSkill(environment = process.env) {
8
8
  return !environment.INIT_CWD || resolve(environment.INIT_CWD) !== resolve(packageRoot)
9
9
  }
10
10
 
11
- export async function main(environment = process.env) {
11
+ export async function main(environment = process.env, loadManager = () => import('../dist/skill/skill-manager.js')) {
12
12
  if (!shouldAutoInstallSkill(environment)) return
13
13
  try {
14
- const { BUNDLED_SKILL_NAMES, autoInstallSkill, autoInstallSkills, defaultSkillsRoot, installSkills } = await import('../dist/skill/skill-manager.js')
14
+ const { autoInstallSkill, autoInstallSkills, defaultSkillsRoot } = await loadManager()
15
15
  const result = environment.VVICAT_SKILL_DIRECTORY
16
- ? {
17
- skills: [
18
- await autoInstallSkill(environment.VVICAT_SKILL_DIRECTORY),
19
- ...(await installSkills({
20
- root: dirname(environment.VVICAT_SKILL_DIRECTORY),
21
- skills: BUNDLED_SKILL_NAMES.slice(1),
22
- })).skills,
23
- ],
24
- }
16
+ ? { skills: [await autoInstallSkill(environment.VVICAT_SKILL_DIRECTORY)] }
25
17
  : await autoInstallSkills(environment.VVICAT_SKILLS_ROOT || defaultSkillsRoot('codex'))
26
18
  for (const skill of result.skills) {
27
19
  process.stdout.write(`VVICAT Skill ${skill.name} ${skill.action}: ${skill.target}\n`)
28
20
  }
21
+ for (const legacy of result.legacySkills || result.skills[0]?.legacySkills || []) {
22
+ if (legacy.requiresAttention) process.stderr.write(`旧 Skill ${legacy.name} 已保留,Codex 仍可能识别:${legacy.target}\n`)
23
+ }
29
24
  } catch (error) {
30
25
  process.stderr.write(`VVICAT CLI 已安装,Skill 自动安装已跳过:${error instanceof Error ? error.message : String(error)}\n`)
31
26
  }
@@ -1,64 +1,35 @@
1
1
  ---
2
2
  name: using-vvicat-ai-short-studio-cli
3
- description: Use when an agent needs to inspect or operate VVICAT infinite-canvas projects through the ai-short-studio CLI, including assets, storyboard-to-video rolling generation, task recovery, editing, captions, audio balancing, and final delivery.
3
+ description: 使用 VVICAT CLI 和原生无限画布创作短剧或逐镜复刻参考片,管理资产选版、审片、付费门禁和交付。
4
4
  ---
5
5
 
6
- <!-- 🛡️ 2026-09-05 asset-ssot Phase 1.1 sync note: 5 trivial `/api/asset-hub/*` routes deleted
7
- (generate-image / modify-image / select-image / undo-image / update-asset-label) and 5 GET
8
- handlers removed from multi-method routes. CLI does not call these endpoints directly.
9
- `/api/asset-hub/creative-assets/upload` (route #25, complex) is preserved and still used by
10
- `uploadAsset` in `src/domain/vvicat-api.ts`. See `docs/asset-management-api-matrix.md` Phase 1.1. -->
6
+ # VVICAT CLI 创作入口
11
7
 
8
+ 画布节点、边、资源选版和任务回执是唯一项目状态。每轮先运行 `ai-short-studio preflight --json` 与 `canvas inspect --project <id> --json`,按实时 `canvas node types` 取得节点参数;不得另建 `.drama-state.json`。已有剧本直接复用。未知的权利、创作目标、画幅、语言、模型或付费范围只做只读检查,向用户合并询问必要问题。
12
9
 
13
- <!-- 🛡️ 2026-09-06 cli-backend-sync Layer 1+2: 服务器 bootstrap 现暴露
14
- data.cliDrift 字段;packages/ai_short_studio_cli/package.json 的 name 字段被
15
- pre-commit guard 锁定为 @aswless_854771076/ai_short_studio_cli。Agent 升级 CLI
16
- 前请跑 `config ai-provider capabilities` 看是否有 cliDrift;如有,按
17
- data.cliDrift.reason (PACKAGE_NAME_DRIFT / VERSION_DRIFT) 走对应修复路径。
18
- 详见本仓库 .agents/skills/vvicat-cli-adapter/SKILL.md §3.1。 -->
10
+ ## 路由与参考索引
19
11
 
20
- # 使用 VVICAT AI Short Studio CLI
12
+ | 触发 | 读取 | 输入 → 产物 → 下一步 |
13
+ | --- | --- | --- |
14
+ | 原创或改编短剧 | [drama-canvas.md](references/drama-canvas.md)、[story-craft/guide.md](references/story-craft/guide.md) | 来源/简报 → 剧本与导演本选版 → 资产与分镜 |
15
+ | 题材、开场、节奏、钩子、反派、付费卡点、审校 | [story-craft/index.md](references/story-craft/index.md) | 目标集与故事事实 → 创作/审校依据 → 剧本选版 |
16
+ | 润色剧本或台词 | [writing-polish.md](references/writing-polish.md) | 已选剧本 → 新资源版本 → 重跑审校 |
17
+ | 参考片复刻 | [recreation.md](references/recreation.md) | 授权来源 → 连续切镜/资产/处理/逐镜审片 → 时间线 |
18
+ | 原片理解、目标简报、对白读法与语义切点 | [reference-analysis-and-timing.md](references/reference-analysis-and-timing.md) | 来源与授权 → 保留/替换关系及语义事件 → 剧本/镜头 |
19
+ | 逐镜资产和分镜 | [assets-and-storyboard.md](references/assets-and-storyboard.md)、[shot-production.md](references/shot-production.md) | 剧本/镜头清单 → 已选资产和逐镜合同 → 视频提示词 |
20
+ | 镜头、人物、打斗、预演、声音、字幕等专业导演问题 | [craft-index.md](references/craft-index.md) | 当前镜头问题 → 对应专业参考 → 可审画布版本 |
21
+ | H3 提示词与视频 | [h3-video-delivery.md](references/h3-video-delivery.md)、[h3-prompts.md](references/h3-prompts.md) | 已选资产/参考 → 模型适配提示词 → 逐镜生成 |
22
+ | 异步付费生成或失败恢复 | [async-pipeline.md](references/async-pipeline.md)、[paid-plan.md](references/paid-plan.md) | 画布/模型/价格 → 只读执行计划与任务回执 → 审片 |
23
+ | 字幕、配音、音轨、剪辑交付 | [editing-delivery.md](references/editing-delivery.md)、[sound-and-captions.md](references/sound-and-captions.md);本地 Remotion 工程再读 [remotion-editing.md](references/remotion-editing.md) | 已审镜头 → 时间线/字幕/音频/清单 → 完整观片 |
24
+ | 媒体导入、抽帧、派生、版权音乐和审片证据 | [media-provenance-and-review.md](references/media-provenance-and-review.md);外部曲目再读 [licensed-music.md](references/licensed-music.md) | 原件及 selected 版本 → 可追溯候选与权利凭证 → 审片/时间线 |
25
+ | Meme、独立配音等专项任务 | [special-workflows.md](references/special-workflows.md) | 已确认任务类型 → 专用画布链路 → 对应审片 |
26
+ | CLI 命令、项目设置、异常 | [commands.md](references/commands.md)、[project-and-canvas.md](references/project-and-canvas.md)、[troubleshooting.md](references/troubleshooting.md) | 实时 CLI 输出 → 精确操作或恢复 |
21
27
 
22
- ## 按需读取
28
+ ## 控制循环
23
29
 
24
- `SKILL.md` 只保留路由和主流程。按实际进度逐阶段读取命中的资料;长任务可以先后读取多份,但不要在开始时一次加载全部:
30
+ 1. 读取当前画布和所选版本,只推进当前阶段。按参考合同补齐节点和连线;`canvas apply` 带 `expectedVersion`,409 后重读。
31
+ 2. 付费前按 [paid-plan.md](references/paid-plan.md) 核对模型、参数、请求数、管理员发布价格和用户确认;无价格或结果未知时停止。禁止对结果未知任务自动重试。
32
+ 3. 节点完成后回读任务、画布、输入指纹和产物预览;只把已选且审过的版本送入下游。上游换版使受影响的审校、逐镜绑定和成片结论失效。
33
+ 4. P0/P1 清零、人工授权、剧本审校、逐镜画面与完整音轨审片、成片从头到尾验收均为门禁;成功任务本身不等于创作合格。
25
34
 
26
- - 项目创建、settings、批处理、目录或安全边界:[references/project-and-canvas.md](references/project-and-canvas.md)
27
- - 人物/场景/道具、拆镜、分镜图和生成前审计:[references/assets-and-storyboard.md](references/assets-and-storyboard.md)
28
- - MiniMax H3 视频、原生音频、遮挡转场、单镜与成片验收:[references/h3-video-delivery.md](references/h3-video-delivery.md)
29
- - 资产、分镜图、视频提示词和视频的滚动异步调度:[references/async-pipeline.md](references/async-pipeline.md)
30
- - 字幕、镜头衔接、声音平衡、时间线、渲染与剪辑验收:[references/editing-delivery.md](references/editing-delivery.md)
31
- - 专业剧本、TTS 独立音频、Meme、Remotion 等专项流程:[references/special-workflows.md](references/special-workflows.md)
32
- - 精确参数和命令示例:[references/commands.md](references/commands.md) 或 `ai-short-studio <topic> --help`
33
- - 已知错误、恢复边界和历史兼容问题:[references/troubleshooting.md](references/troubleshooting.md)
34
-
35
- ## 核心原则
36
-
37
- 把 `ai-short-studio` 当作机器可读的远程控制接口。目标、素材含义、平台、语言、画幅、时长、风格、人物、场景、道具、模型、实时 schema、候选版本、付费范围或覆盖影响存在未知、缺失或矛盾时,合并询问最少必要问题;得到答复前只做只读检查。不得用示例值替用户作创作决定。
38
-
39
- 优先使用一次 `canvas inspect` 聚合查询。同阶段可合并的 settings、节点和连线分别使用 `canvas settings set --file`、`canvas apply`、`canvas node update-many`。生成链使用单个调度循环滚动提交,不用 `--wait` 把整批阶段串行化:节点一完成就刷新画布并立即提交它已就绪的下游节点。默认每类并发 4、总并发上限 16;同一节点同一输入指纹只允许一个在途任务,不并发启动多个相互竞争的调度器。409 重读并确认,401/403、schema、业务门禁和付费错误必须停下修正;`MODEL_PRICING_NOT_CONFIGURED` 表示当前能力参数没有已发布价格档位,不能盲目重试。
40
-
41
- ## 主流程
42
-
43
- 1. **预检**:运行 `preflight --json`;更新后重跑。再读取 `config get`、实时 `canvas node types` 和目标项目。`skill status --target codex` 非 current 时先询问再更新,禁止强制覆盖用户修改。
44
- 2. **项目配置门禁**:读取 `canvas settings get`,展示 `currentValue`、`effectiveValue`、`source`、建议值和实时 options。确认 `aspectRatio`、`artStyle`、`visualBible`、`storyboardImageType`、`outputLanguage`、`analysisModel`、`imageModel`、`imageResolution`、`imageQuality` 和 `videoResolution`,以及 `editModel`、`videoModel`、`videoPromptType`、`audioModel`;`$ART_STYLE_ID` 必须解析实时 `option.value`。标准创作门禁使用 `--file settings.json` 原子写入并携带 `expectedVersion`,回读一致前不得创建或运行付费节点。节点确需不同参数时才按实时能力覆盖。
45
- 3. **剧本和目标集**:已有专业剧本时直接复用,不得重复执行 `novel-to-script`。需要创作微短剧时,**REQUIRED SUB-SKILL: Use short-drama**,随后 **REQUIRED SUB-SKILL: Use humanizer** 去除 AI 味;不得改动已经确认的剧情事实,润色后回到 short-drama 复核。确认目标集数后,只生产该集实际需要的资产。
46
- 4. **资产门禁**:先完成人物、场景、道具及其派生版本,展示真实预览并确认 selected version。执行资产—剧情匹配审计;缺失、多余、错版、stale、错边未清零时拆镜前门禁失败。
47
- 5. **拆镜和滚动生成**:拆镜前按 [语言匹配门禁](references/assets-and-storyboard.md#拆镜语言匹配门禁) 核对源剧本语言、交付语言、项目 `outputLanguage` 和节点 `targetLanguage`,不一致时禁止执行。把该集剧本与全部确认资产连接到 `storyboard-breakdown`,复用服务端自动创建的 `storyboard-shot` / `storyboard-image`。完成后逐镜执行语言与镜头质量审计且不抽样;P0/P1 未清零不得继续。分镜图生成前按实时 schema 确认画幅、布局、格数和费用。进入付费生成后读取 [滚动异步调度](references/async-pipeline.md):分镜图完成一镜即审计并提交该镜视频提示词,提示词完成即审计、显式写入视频配置并提交该镜视频;不得等待同阶段全部镜头完成。单镜可恢复失败只标记并重试该镜,不阻塞其他已就绪镜头。
48
- 6. **视频规划**:视频生成前依次完成素材连线、镜头逻辑和上下镜连续性审计。读取实时 `durationOptions`;只有选项恰为 5、10、15 秒时才按这三档向上取整。RunningHub MiniMax H3 支持纯文本和 0–9 张参考图、0–2 个参考视频、0–2 个参考音频,5–15 秒逐秒可选,1–4 秒归一为 5 秒;参考视频只传画面。画幅为 16:9 / 9:16,分辨率为 480p / 720p / 1K / 2K。
49
- 7. **H3 生成门禁**:`videoModel` 为 `comfly::minimax-h3` 或 `runninghub::minimax-h3-reference-to-video` 时,同次设置 `videoPromptType: "h3"`。仅有效的节点 `promptType` 可覆盖项目值;旧 `seedance` 覆盖必须清除并回读,否则不得执行视频提示词或视频生成。编写或改写 H3 提示词时,**REQUIRED SUB-SKILL: Use h3-prompt-writing**;进入生成前必须读取 [H3 成片规则](references/h3-video-delivery.md)。
50
- 8. **逐镜后验**:每个视频镜头完成后实际看画面、听完整音轨,并完成视觉、音频、转场、字幕四项验收。任一 P0/P1 失败只修复并重跑受影响镜头;未通过不得进入成片剪辑。
51
- 9. **成片和交付**:剪辑只使用通过验收的 selected 版本,并完整执行 [剪辑与交付](references/editing-delivery.md)。默认对白/旁白字幕进入成片,人物与场景介绍仅在首次出现时显示短暂角标,同时输出独立 SRT/ASS。根据叙事动机选择硬切、动作切、J/L-cut、遮挡匹配或短淡化,不给所有镜头机械套淡入淡出。完成节奏裁切、声音平衡、画面统一、字幕和必要技术处理后,从头到尾观看成片并执行完整成片验收;未通过不得声称完成。原始视频按 `selectedOutputVersionId` 下载,字幕独立保存,`manifest` 记录镜号、节点、任务、资产、版本、时间线和文件名。
52
-
53
- ## 强制分流
54
-
55
- - H3 成片中的人物说话、旁白、环境音和 BGM 全部由 H3 原生生成;不得用 `voice-design`、`voice-clone`、`tts` 或其他外部 TTS 覆盖。独立音频交付才读取专项 TTS 流程。
56
- - Meme 默认且标准链路是 `meme-scene-analysis → meme-material-search → cat-meme-video`,禁止静默切换通用视频生成;`CAT_MEME_REQUIRED_PROP_MISSING` 必须返回素材检索修复。
57
- - 涉及剪辑、字幕、转场、音频编排或渲染时,**REQUIRED SUB-SKILL: Use remotion-best-practices**。
58
- - 编辑镜头用 `canvas node edit-shot`,编辑结构化资源用 `canvas node edit-resource`,均追加不可变版本。`outputLanguage` 变化只标记下游 stale;手工首尾帧使用 `video-frame-extract`。
59
-
60
- ## 安全与完成条件
61
-
62
- 不把密码、Token 或 API Key 写入参数、日志、Skill 或仓库。删除项目、覆盖画布、切换模型、重新付费生成或覆盖用户选版前,必须明确目标和影响并取得授权。临时项目在所有结束路径清理;既有项目不得擅自删除。
63
-
64
- 任务成功只代表服务端完成,不代表创作合格。每批产物都要回读任务、画布、连线、资产和最终配置并查看真实预览;只有阶段门禁、逐镜四项验收和最终成片验收全部通过,才可交付。
35
+ 视频、字幕、声音和时间线始终使用 VVICAT 画布产物。第三方方法仅作随包参考;来源和许可见 [sources.md](references/sources.md)。
@@ -0,0 +1,9 @@
1
+ # B-roll 与屏幕演示
2
+
3
+ 当画面需要为对白、旁白或讲解提供证据时读取。输入是已确认的论点、目标观众、真实素材和声音时间线;产物是每段画面的叙事职责、素材版本、图形说明与可审切点。
4
+
5
+ 每段补充画面先选一个角色:真实证据、动作过程、空间建立、情绪反应、对比或视觉比喻。真实证据必须能追溯到原文件、实际界面或已确认事实;生成图像仅能表达概念或重建经授权的场景,不能冒充录屏、产品效果或新闻现场。画面不必永远全屏;需要保留说话者表演时,可用并置或短插入,但要检查两处视觉重音是否互相争抢。
6
+
7
+ 屏幕演示先列用户要看懂的状态变化:起始界面、执行的操作、可见反馈和结果。只捕获必要窗口与时间段,遮住凭据、个人信息和无关通知;编辑器、终端或网页的输入内容以真实可复现状态为准。后期箭头、放大、角标与字幕是解释层,不能制造软件并未产生的结果。
8
+
9
+ 在时间线上把 B-roll 的进入、关键可见事件与退出对齐到实际语音或动作。画面持续到观众足以识别证据,再回到承担情绪或判断的人物;切点审查声音主线是否连续。观看最终导出时检查缩小画幅下的 UI 字号、鼠标/手势是否可辨,以及图形是否遮挡真实结果。
@@ -0,0 +1,11 @@
1
+ # 字幕与画面图形
2
+
3
+ 适用于对白字幕、人物头顶跟随文字、榜单、动态图形和屏幕演示。输入是人工校对的原语与译文、镜头选版、画面安全区和时间线;产物是独立字幕/图形层及成片审片记录。
4
+
5
+ 字幕按听到的语义和语言阅读单位切分,先定 Cue 起止,再定分行、强调与颜色。短语、停顿和情绪决定节奏;不能仅按等长字符切块。核对字幕与说话人、画面对象和语言一致,避免遮挡脸、手、关键道具或平台 UI 安全区。原片烧录字幕与新片字幕分开处理;转写只作待审文本,不自动视为已对齐字幕。
6
+
7
+ 需要文字跟随人物头部时,先核对每个镜头的说话人、脸部位置和可见时间,再把字幕锚到该人物;遮挡、出画或双人同框时改用稳定的安全区位置。屏幕演示应区分真实界面证据、录屏交互与后期解释图形,不伪造未经展示的产品状态。图形与素材共同构图:留出信息层级、阅读时间和动线;运动应表达状态变化、揭示或承接,不只做重复入场动画。
8
+
9
+ 在时间线中逐帧检查字幕可读性、图形与镜头交接、音画同步及最终导出。可编辑字幕文件与成片版本一起交付;模型生成画面中出现的水印、标题、Logo 或意外文字按镜退回修复。
10
+
11
+ 动态图形先定义它所表示的状态和持续时间,再决定进入、保持、更新与退出;同一个比较板或计数器在镜头切换时应保留身份和数值连续性。动画需给阅读留停顿,不要让文字每次出现都重新飞入。图形若跨越真人表演与 B-roll,应在切点前后检查位置、大小、颜色和层级是否保持可追踪。
@@ -15,15 +15,17 @@ ai-short-studio canvas node types --json
15
15
  ```bash
16
16
  ai-short-studio canvas node list --project <projectId> --json
17
17
  ai-short-studio canvas node get <nodeId> --project <projectId> --json
18
+ ai-short-studio canvas node quote <nodeId> --project <projectId> --json
18
19
  ai-short-studio canvas node add <kind> --project <projectId> --config node.json --json
19
20
  ai-short-studio canvas node update <nodeId> --project <projectId> --set 'gridSize=9' --json
20
21
  ai-short-studio canvas node update-many --project <projectId> --file updates.json --json
21
22
  ai-short-studio canvas node edit-shot <nodeId> --project <projectId> --file shot.json --json
22
23
  ai-short-studio canvas node edit-resource <nodeId> --project <projectId> --file resource.json --json
24
+ ai-short-studio canvas node edit-resource <screenplayResourceNodeId> --project <projectId> --markdown-file episode.md --json
23
25
  ai-short-studio canvas node delete <nodeId> --project <projectId> --yes --json
24
26
  ```
25
27
 
26
- `update` 只改普通 config、标题和坐标;结构化 artifact 必须用 `edit-shot` / `edit-resource` 追加版本。身份字段不可修改,409 后重新读取当前选版。
28
+ `update` 只改普通 config、标题和坐标;资源 artifact 必须用 `edit-shot` / `edit-resource` 追加版本。`--markdown-file` 只用于 `drama-episode` 生成的剧本资源;其他资源继续传 JSON。身份字段不可修改,409 后重新读取当前选版。
27
29
 
28
30
  H3 批量启用原生音频的 `updates.json`:
29
31
 
@@ -16,6 +16,6 @@
16
16
 
17
17
  最终 `videoModel.effectiveValue` 为 `comfly::minimax-h3` 或 `runninghub::minimax-h3-reference-to-video` 时,必须写入 `videoPromptType: "h3"`;仅有效的节点 `promptType` 可覆盖项目值,旧 `seedance` 必须清除并回读,否则不得执行视频提示词或视频生成。实时能力 `generateAudioOptions` 必须包含 `true`,H3 节点显式设置并回读 `generateAudio: true`。
18
18
 
19
- 视频时长读取实时 `durationOptions`。只有实时选项恰为 5、10、15 秒时才按这三档向上取整。RunningHub MiniMax H3 支持纯文本,5–15 秒逐秒可选,1–4 秒归一为 5 秒;画幅为 16:9 / 9:16,分辨率为 480p / 720p / 1K / 2K。
19
+ 视频时长读取实时 `durationOptions`。只有实时选项恰为 5、10、15 秒时才按这三档向上取整。RunningHub MiniMax H3 支持纯文本、0–9 张参考图、0–2 段参考视频与 0–2 段参考音频,参考视频只传画面;5–15 秒逐秒可选,1–4 秒归一为 5 秒;画幅为 16:9 / 9:16,分辨率为 480p / 720p / 1K / 2K。
20
20
 
21
21
  结构化资源使用 `canvas node edit-resource`,镜头使用 `canvas node edit-shot`。Meme 使用 `meme-scene-analysis → meme-material-search → cat-meme-video`,禁止静默切换通用视频;`CAT_MEME_REQUIRED_PROP_MISSING` 返回素材检索修复。
@@ -0,0 +1,17 @@
1
+ # 构图与镜头语言专项
2
+
3
+ 用于人物关系、动作空间、画风或竖屏安全区使逐镜画面难读时。输入为已选剧本、导演本、资产和相邻镜头;产物是分镜中的可见构图约束与逐镜审片观察。构图名称只是沟通工具,必须说明它让观众先看到什么、如何保持或改变关系。
4
+
5
+ | 叙事任务 | 可选画面关系 | 容易失败的地方 |
6
+ | --- | --- | --- |
7
+ | 明确第一眼信息 | 单点聚焦、三分落点、框景或有目的的留白 | 第二视觉重音抢走注意;主体被字幕、裁切或图形挡住 |
8
+ | 交代空间与行动路线 | 前中后景、引导线、走廊透视、人物与出口的相对位置 | 模型改写门窗与道具位置;相邻镜屏幕方向或轴线不连续 |
9
+ | 表达人物权力与亲疏 | 双人同框、距离、遮挡、镜内分区、反打视线 | 三人以上左右顺序漂移;听者没有独立反应;无缘由地看镜头 |
10
+ | 强化动作因果 | 起势方向、接触点、受力结果、可辨的遮挡前后状态 | 武器或手的归属错误;慢动作、拖影、烟尘掩盖关键接触 |
11
+ | 建立氛围或情绪 | 有来源的窗光、局部高光、冷暖差、空镜或静止等待 | 无剧本或资产依据的雨、雾、霓虹、镜面、烟尘抢戏 |
12
+
13
+ 选择景别、焦点与机位运动时,先问哪个事件需要被看清。人物表情是主信息时,不能因“电影感”把脸藏在剪影、虚焦或过亮反射中;动作路线是主信息时,不能用连续特写代替空间关系。固定机位能完成任务时无需强行推轨、环绕或拉焦。竖屏还要核对脸、手、关键道具和字幕安全区。
14
+
15
+ 审片依次记录五件事:首要信息是否第一眼可读;分镜约定的主体位置、视线、层次、光源与机位是否真的实现;雨雾、镜面、武器或群体等高风险元素是否有来源;相邻镜轴线与出入画是否连续;交付画幅和字幕是否遮挡关键内容。影响剧情因果或人物关系的缺陷记为 P1 并退回相关镜头;仅氛围强弱偏差可作为 P2。观察必须写真实画面证据,而非复述提示词。
16
+
17
+ 来源:MIT 许可短剧模块的导演构图参考与受控词表,见 [许可](licenses/oh-my-short-drama-MIT.txt)。
@@ -0,0 +1,20 @@
1
+ # 专业创作参考索引
2
+
3
+ 仅在镜头或交付出现对应问题时读取;一部作品可组合多项方法。参考提供导演判断,模型与节点参数以 VVICAT 实时目录为准。每次选择都写回画布节点、资源版本、逐镜审片记录或时间线,不另存第二套项目状态。
4
+
5
+ | 触发问题 | 读取 | 输入 → 产物 → 下一步 |
6
+ | --- | --- | --- |
7
+ | 人物、场景、道具图过于泛化或跨镜不一致 | [image-and-reference-direction.md](image-and-reference-direction.md) | 已选剧本/资产与参考角色 → 单张图的视觉职责及参考来源 → 图片预览、选版 |
8
+ | 构图、机位、光源或竖屏画面让剧情信息难读 | [composition-and-shot-review.md](composition-and-shot-review.md) | 剧本/导演本与相邻镜 → 可见的画面关系和风险 → 分镜/逐镜审片 |
9
+ | 动作、对白表演、机位或参考视频用途不清 | [video-and-performance-direction.md](video-and-performance-direction.md) | 逐镜合同与上游版本 → 动作因果、表演、首尾状态和模型输入分工 → 视频提示词及生成 |
10
+ | 配音选角、原音保留、音乐、环境声或声音跨切点冲突 | [voice-and-sound-direction.md](voice-and-sound-direction.md) | 角色、对白、原片音轨 → 声音身份、场级声场与混音决定 → 音频版本、完整试听 |
11
+ | 字幕、图形、运动、屏幕演示或人物头顶跟随文字 | [caption-and-graphics-direction.md](caption-and-graphics-direction.md) | 已校对白、画面占位与时间线 → 字幕节奏、图形布局及运动事件 → 成片逐帧审片 |
12
+ | 讲解、旁白或口播需要证据画面、录屏或补充镜头 | [b-roll-and-screen.md](b-roll-and-screen.md) | 已确认论点与真实素材 → B-roll 职责及界面状态 → 时间线和成片审查 |
13
+ | 短剧、口播、双人对话、街访、榜单、讲解或旁白结构 | [format-relationships.md](format-relationships.md) | 参考片和目标简报 → 表演、画面与揭示关系 → 分镜/时间线 |
14
+ | 逐镜资产清单、有序参考和付费生成门禁 | [shot-production.md](shot-production.md)、[paid-plan.md](paid-plan.md) | 镜头需求 → 选版引用与只读报价 → 逐镜生成 |
15
+ | 角色形象、打斗、复杂运动预演、跨镜连续性或市场立项 | [production-specialties.md](production-specialties.md) | 剧本/导演本与真实镜头风险 → 专项导演决策 → 资产、分镜或审片版本 |
16
+ | 本地 Remotion 剪辑实现 | [remotion-editing.md](remotion-editing.md) | 已审 selected 镜头与时间线 → 确定性工程、字幕与审片版 → 交付验收 |
17
+ | 参考片意图、对白读法或切点依赖 | [reference-analysis-and-timing.md](reference-analysis-and-timing.md) | 原片/剧本/授权 → 保留与替换关系、语义事件 → 逐镜合同 |
18
+ | 媒体派生、全片审片或独立音乐授权 | [media-provenance-and-review.md](media-provenance-and-review.md)、[licensed-music.md](licensed-music.md) | 原件/选版/许可 → 可追溯派生和审片证据 → 时间线 |
19
+
20
+ 图片、视频、声音、图形和形式资料按 Hypit 的专业 Craft 与 Format 方法重新整理;短剧专项与 Remotion 资料来自 MIT 许可的开源短剧模块。均按问题加载,来源说明见 [sources.md](sources.md)。短剧编剧的题材、钩子、节奏和审校另见 [story-craft/index.md](story-craft/index.md)。