hyper-animator-codex 0.6.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Install the `hyper-animator-codex` Codex skill from npm.
4
4
 
5
- The skill guides Codex through a HyperFrames animation workflow: natural-language brief, clarification questions, catalog-map intent matching, style and motion selection, HTML animation authoring, validation, user approval, and render handoff.
5
+ The skill guides Codex through a HyperFrames animation workflow: natural-language brief, outline/narration detection, clarification questions, catalog-map intent matching, style and motion selection, HTML animation authoring, validation, user approval, and render handoff.
6
6
 
7
7
  ## Install
8
8
 
@@ -22,19 +22,31 @@ Or install the CLI globally:
22
22
 
23
23
  ```bash
24
24
  npm install -g hyper-animator-codex
25
- hyper-animator-codex install --force
26
25
  ```
27
26
 
28
- By default the installer copies the skill to:
27
+ Global install runs a postinstall step that copies the skill to both Codex and Claude Code:
29
28
 
30
29
  ```text
31
30
  ${CODEX_HOME:-$HOME/.codex}/skills/hyper-animator-codex
31
+ ${CLAUDE_HOME:-$HOME/.claude}/skills/hyper-animator-codex
32
+ ```
33
+
34
+ To skip automatic skill installation during npm install:
35
+
36
+ ```bash
37
+ HYPER_ANIMATOR_CODEX_SKIP_POSTINSTALL=1 npm install -g hyper-animator-codex
38
+ ```
39
+
40
+ You can also run or re-run installation manually:
41
+
42
+ ```bash
43
+ hyper-animator-codex install --force
32
44
  ```
33
45
 
34
- Use a custom Codex skills directory:
46
+ Use one custom skills directory:
35
47
 
36
48
  ```bash
37
- npx hyper-animator-codex install --target /path/to/codex/skills
49
+ npx hyper-animator-codex install --target /path/to/skills
38
50
  ```
39
51
 
40
52
  ## Git Checkpoints
@@ -52,9 +64,9 @@ node skills/hyper-animator-codex/scripts/git_checkpoint.mjs \
52
64
 
53
65
  Existing Git repositories with uncommitted changes are protected: the helper stops instead of mixing user edits into automatic commits. Large video and audio files above 10 MB are skipped by default and recorded in a manifest under `hyper-animator-output/git-checkpoints/`.
54
66
 
55
- ## MiniMax Music Configuration
67
+ ## MiniMax Audio Configuration
56
68
 
57
- MiniMax is the preferred generated-background-music provider. Configure it during install so the copied Codex skill can generate music without asking for credentials later:
69
+ MiniMax is the preferred generated-background-music provider and the required narration TTS provider. Configure it during install so the copied Codex skill can generate music and voiceover without asking for credentials later:
58
70
 
59
71
  ```bash
60
72
  npx hyper-animator-codex install --force \
@@ -104,10 +116,44 @@ When rendering video, hide preview controls with any one of:
104
116
  <html data-render-mode="video">
105
117
  ```
106
118
 
119
+ ## Narration And Subtitles
120
+
121
+ Detect outline and narration files in a project directory:
122
+
123
+ ```bash
124
+ node skills/hyper-animator-codex/scripts/detect_project_files.mjs --dir . --pretty
125
+ ```
126
+
127
+ List MiniMax voices, cloned voices first:
128
+
129
+ ```bash
130
+ node skills/hyper-animator-codex/scripts/generate_minimax_tts.mjs --list-voices --voice-type all
131
+ ```
132
+
133
+ Generate narration audio from `narration.json`:
134
+
135
+ ```bash
136
+ node skills/hyper-animator-codex/scripts/generate_minimax_tts.mjs \
137
+ --narration-json narration.json \
138
+ --output-dir hyper-animator-output/voice
139
+ ```
140
+
141
+ Generate timed subtitle JSON and SRT:
142
+
143
+ ```bash
144
+ node skills/hyper-animator-codex/scripts/generate_subtitles.mjs \
145
+ --narration narration.json \
146
+ --output-dir hyper-animator-output/subtitles \
147
+ --style clean
148
+ ```
149
+
107
150
  ## Contents
108
151
 
109
152
  - `skills/hyper-animator-codex/SKILL.md`: Codex skill instructions.
110
153
  - `skills/hyper-animator-codex/references/`: HyperFrames catalog map, workflow guide, pseudocode, and request examples.
154
+ - `skills/hyper-animator-codex/scripts/detect_project_files.mjs`: top-level outline and narration detector for first-run/restart workflows.
155
+ - `skills/hyper-animator-codex/scripts/generate_minimax_tts.mjs`: MiniMax voice listing and narration TTS helper.
156
+ - `skills/hyper-animator-codex/scripts/generate_subtitles.mjs`: timed subtitle JSON/SRT generator.
111
157
  - `skills/hyper-animator-codex/scripts/git_checkpoint.mjs`: Git initialization and stage-commit helper for `/hyper-animator` runs.
112
158
  - `skills/hyper-animator-codex/scripts/validate_hyperframes_html.py`: static pre-render HTML quality gate.
113
159
 
@@ -1,21 +1,29 @@
1
1
  #!/usr/bin/env node
2
- import { installSkill, resolveCodexSkillsRoot, SKILL_NAME } from "../lib/install-skill.mjs";
2
+ import {
3
+ installSkillToTargets,
4
+ resolveClaudeCodeSkillsRoot,
5
+ resolveCodexSkillsRoot,
6
+ SKILL_NAME,
7
+ } from "../lib/install-skill.mjs";
3
8
  import { parseInstallArgs } from "../lib/install-options.mjs";
4
9
 
5
10
  function printHelp() {
6
11
  console.log(`Usage:
7
12
  hyper-animator-codex install [--force] [--target <skills-dir>] [MiniMax options]
8
13
  hyper-animator-codex path
14
+ hyper-animator-codex paths
9
15
  hyper-animator-codex help
10
16
 
11
17
  Commands:
12
- install Install the packaged ${SKILL_NAME} skill into Codex
18
+ install Install the packaged ${SKILL_NAME} skill into Codex and Claude Code
13
19
  path Print the default Codex skills directory
20
+ paths Print default Codex and Claude Code skills directories
14
21
  help Show this help
15
22
 
16
23
  Options:
17
24
  --force Replace an existing installed skill
18
- --target <dir> Install into a specific Codex skills directory
25
+ --quiet Suppress successful install output
26
+ --target <dir> Install into one specific skills directory
19
27
  --minimax-api-key <key> Save MiniMax API key into installed skill config
20
28
  --minimax-group-id <group_id> Save MiniMax group_id into installed skill config
21
29
  --minimax-model <model> MiniMax text music model: music-2.6 or music-2.6-free
@@ -39,20 +47,34 @@ async function main() {
39
47
  return;
40
48
  }
41
49
 
50
+ if (command === "paths") {
51
+ console.log(`Codex: ${resolveCodexSkillsRoot()}`);
52
+ console.log(`Claude Code: ${resolveClaudeCodeSkillsRoot()}`);
53
+ return;
54
+ }
55
+
42
56
  if (command !== "install") {
43
57
  throw new Error(`Unknown command: ${command}`);
44
58
  }
45
59
 
46
60
  const options = parseInstallArgs(args);
47
- const result = await installSkill(options);
61
+ const result = await installSkillToTargets(options);
62
+
63
+ if (options.quiet) {
64
+ return;
65
+ }
48
66
 
49
67
  console.log(`Installed ${result.skillName}`);
50
68
  console.log(`Source: ${result.sourcePath}`);
51
- console.log(`Target: ${result.installedPath}`);
52
69
 
53
- if (result.minimaxConfigPath) {
54
- console.log(`MiniMax config: ${result.minimaxConfigPath}`);
55
- console.log(`MiniMax model: ${result.minimaxConfig.model}`);
70
+ for (const install of result.installs) {
71
+ console.log(`${install.tool}: ${install.installedPath}`);
72
+ if (install.minimaxConfigPath) {
73
+ console.log(`${install.tool} MiniMax config: ${install.minimaxConfigPath}`);
74
+ if (install.minimaxConfig) {
75
+ console.log(`${install.tool} MiniMax model: ${install.minimaxConfig.model}`);
76
+ }
77
+ }
56
78
  }
57
79
  }
58
80
 
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env node
2
+ import { installSkillToTargets } from "../lib/install-skill.mjs";
3
+
4
+ if (process.env.HYPER_ANIMATOR_CODEX_SKIP_POSTINSTALL === "1") {
5
+ process.exit(0);
6
+ }
7
+
8
+ try {
9
+ const result = await installSkillToTargets({
10
+ force: true,
11
+ quiet: true,
12
+ env: process.env,
13
+ });
14
+
15
+ const targets = result.installs.map((install) => `${install.tool}:${install.installedPath}`).join(", ");
16
+ console.log(`hyper-animator-codex installed skills: ${targets}`);
17
+ } catch (error) {
18
+ console.warn(`hyper-animator-codex postinstall skipped: ${error.message}`);
19
+ }
@@ -11,6 +11,7 @@ function requireValue(args, index, flag) {
11
11
  export function parseInstallArgs(args) {
12
12
  const parsed = {
13
13
  force: false,
14
+ quiet: false,
14
15
  targetRoot: undefined,
15
16
  minimaxConfig: {},
16
17
  };
@@ -20,6 +21,8 @@ export function parseInstallArgs(args) {
20
21
 
21
22
  if (arg === "--force") {
22
23
  parsed.force = true;
24
+ } else if (arg === "--quiet") {
25
+ parsed.quiet = true;
23
26
  } else if (arg === "--target") {
24
27
  parsed.targetRoot = requireValue(args, index, arg);
25
28
  index += 1;
@@ -1,9 +1,10 @@
1
- import { cp, mkdir, rm, stat } from "node:fs/promises";
1
+ import { chmod, cp, mkdir, readFile, rm, stat, writeFile } from "node:fs/promises";
2
2
  import { homedir } from "node:os";
3
3
  import { basename, dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
6
  import {
7
+ MINIMAX_CONFIG_RELATIVE_PATH,
7
8
  redactMinimaxConfig,
8
9
  resolveInstallMinimaxConfig,
9
10
  writeMinimaxConfig,
@@ -43,6 +44,44 @@ export function resolveCodexSkillsRoot(env = process.env) {
43
44
  return join(home, ".codex", "skills");
44
45
  }
45
46
 
47
+ export function resolveClaudeCodeSkillsRoot(env = process.env) {
48
+ if (env.CLAUDE_HOME) {
49
+ return join(env.CLAUDE_HOME, "skills");
50
+ }
51
+
52
+ const home = env.HOME || homedir();
53
+ if (!home) {
54
+ throw new Error("Cannot resolve Claude Code skills directory: HOME is not set");
55
+ }
56
+
57
+ return join(home, ".claude", "skills");
58
+ }
59
+
60
+ async function readExistingMinimaxConfig(installedPath) {
61
+ const configPath = join(installedPath, MINIMAX_CONFIG_RELATIVE_PATH);
62
+
63
+ if (!(await pathExists(configPath))) {
64
+ return null;
65
+ }
66
+
67
+ return {
68
+ configPath,
69
+ body: await readFile(configPath, "utf8"),
70
+ };
71
+ }
72
+
73
+ async function restoreExistingMinimaxConfig(installedPath, existingConfig) {
74
+ if (!existingConfig) {
75
+ return null;
76
+ }
77
+
78
+ const configPath = join(installedPath, MINIMAX_CONFIG_RELATIVE_PATH);
79
+ await mkdir(dirname(configPath), { recursive: true });
80
+ await writeFile(configPath, existingConfig.body, { encoding: "utf8", mode: 0o600 });
81
+ await chmod(configPath, 0o600);
82
+ return configPath;
83
+ }
84
+
46
85
  export async function installSkill(options = {}) {
47
86
  const targetRoot = options.targetRoot || resolveCodexSkillsRoot(options.env);
48
87
  const sourcePath = options.sourcePath || join(packageRoot, "skills", SKILL_NAME);
@@ -55,6 +94,8 @@ export async function installSkill(options = {}) {
55
94
 
56
95
  await mkdir(targetRoot, { recursive: true });
57
96
 
97
+ const existingMinimaxConfig = force ? await readExistingMinimaxConfig(installedPath) : null;
98
+
58
99
  if (await pathExists(installedPath)) {
59
100
  if (!force) {
60
101
  throw new Error(`${SKILL_NAME} already exists at ${installedPath}; rerun with --force to replace it`);
@@ -74,12 +115,53 @@ export async function installSkill(options = {}) {
74
115
  const minimaxWrite = resolvedMinimaxConfig
75
116
  ? await writeMinimaxConfig(installedPath, resolvedMinimaxConfig)
76
117
  : null;
118
+ const restoredMinimaxConfigPath = minimaxWrite ? null : await restoreExistingMinimaxConfig(installedPath, existingMinimaxConfig);
77
119
 
78
120
  return {
79
121
  skillName: SKILL_NAME,
122
+ tool: options.tool || "codex",
80
123
  sourcePath,
81
124
  installedPath,
82
- minimaxConfigPath: minimaxWrite ? minimaxWrite.configPath : null,
125
+ minimaxConfigPath: minimaxWrite ? minimaxWrite.configPath : restoredMinimaxConfigPath,
83
126
  minimaxConfig: minimaxWrite ? redactMinimaxConfig(minimaxWrite.config) : null,
84
127
  };
85
128
  }
129
+
130
+ export async function installSkillToTargets(options = {}) {
131
+ const sourcePath = options.sourcePath || join(packageRoot, "skills", SKILL_NAME);
132
+
133
+ if (options.targetRoot) {
134
+ const install = await installSkill({
135
+ ...options,
136
+ sourcePath,
137
+ tool: "custom",
138
+ });
139
+ return {
140
+ skillName: SKILL_NAME,
141
+ sourcePath,
142
+ installs: [install],
143
+ };
144
+ }
145
+
146
+ const env = options.env || process.env;
147
+ const installs = [];
148
+
149
+ for (const target of [
150
+ { tool: "codex", targetRoot: resolveCodexSkillsRoot(env) },
151
+ { tool: "claude-code", targetRoot: resolveClaudeCodeSkillsRoot(env) },
152
+ ]) {
153
+ installs.push(await installSkill({
154
+ ...options,
155
+ env,
156
+ sourcePath,
157
+ targetRoot: target.targetRoot,
158
+ tool: target.tool,
159
+ }));
160
+ }
161
+
162
+ return {
163
+ skillName: SKILL_NAME,
164
+ sourcePath,
165
+ installs,
166
+ };
167
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "hyper-animator-codex",
3
- "version": "0.6.0",
4
- "description": "Install the Hyper Animator Codex skill for Codex.",
3
+ "version": "0.7.1",
4
+ "description": "Install the Hyper Animator skill for Codex and Claude Code.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "hyper-animator-codex": "bin/hyper-animator-codex.mjs"
8
8
  },
9
9
  "scripts": {
10
+ "postinstall": "node bin/postinstall.mjs",
10
11
  "test": "node --test",
11
12
  "pack:check": "npm pack --dry-run"
12
13
  },
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: hyper-animator-codex
3
- description: Use when a user asks Codex to create, plan, author, customize, validate, preview, or render a HyperFrames, HTML, or GSAP animation/video from natural-language requirements, including product demos, code demos, data videos, podcast captions, social shorts, catalog assembly, new HyperFrames HTML, sound effects, background music, rhythm, BPM, or beat-synced transitions.
3
+ description: Use when a user asks Codex to create, plan, author, customize, validate, preview, or render a HyperFrames, HTML, or GSAP animation/video from natural-language requirements, including product demos, code demos, data videos, podcast captions, social shorts, catalog assembly, new HyperFrames HTML, outline/narration detection, voiceover, subtitles, sound effects, background music, rhythm, BPM, final mixing, or beat-synced transitions.
4
4
  ---
5
5
 
6
6
  # Hyper Animator Codex
@@ -12,23 +12,25 @@ Turn a natural-language animation or video brief into a validated HyperFrames HT
12
12
  ## Required Flow
13
13
 
14
14
  1. Read `references/git-checkpoint-workflow.md` and run `scripts/git_checkpoint.mjs --phase init --message "chore: initialize hyper animator workspace" --allow-empty` before requirement capture. If the helper reports an existing dirty repository or linked worktree, stop and ask the user to resolve it.
15
- 2. Capture the raw request and extract an initial intent profile: purpose, format, duration, content inputs, style tags, motion tags, and needed roles.
16
- 3. If purpose, format, or core content is unclear, ask the first clarification round before choosing catalog items.
17
- 4. Read `references/hyperframes-catalog-map.json` and score candidates by keyword, intent domain, format, role, style, motion, and constraints.
18
- 5. Pick candidate blocks/components for main scene, caption, effects, transitions, and outro.
19
- 6. Decide generation mode:
15
+ 2. When starting in a new directory, restarting, or handling outline/narration/voiceover/subtitle/full-mix work, read `references/narration-audio-workflow.md` and run `scripts/detect_project_files.mjs --dir . --pretty` before visual planning.
16
+ 3. Capture the raw request and extract an initial intent profile: purpose, format, duration, content inputs, style tags, motion tags, and needed roles.
17
+ 4. If purpose, format, or core content is unclear, ask the first clarification round before choosing catalog items.
18
+ 5. Read `references/hyperframes-catalog-map.json` and score candidates by keyword, intent domain, format, role, style, motion, and constraints.
19
+ 6. Pick candidate blocks/components for main scene, caption, effects, transitions, and outro.
20
+ 7. Decide generation mode:
20
21
  - `generate_new_hyperframes_html` when the user asks to write HTML, create a new effect, customize style, match a brand, use complex animation, or when component snippets are only paste placeholders.
21
22
  - `assemble_existing_catalog_items` only when the user asks to use existing catalog items or quickly compose installed blocks/components.
22
- 7. Clarify audio: ask whether to add animation/transition sound effects and whether to add background music.
23
- 8. If background music is requested or undecided, read `references/minimax-music-workflow.md` and use `scripts/generate_minimax_music.mjs` for every MiniMax request. Do not construct MiniMax HTTP calls directly. If MiniMax is unavailable or declined, ask for a local audio path or continue without BGM.
24
- 9. If background music is used, read `references/beat-sync-workflow.md`, generate or obtain the audio, and run `scripts/analyze_music_beats.py` when the file is available.
25
- 10. Ask the second clarification round with candidate context: visual direction, motion rhythm, generation mode, audio choices, music prompt/model when MiniMax is used, and beat-sync assumptions when background music is present.
26
- 11. Write or assemble HTML. When beat-sync is enabled, align major reveals, cuts, transitions, camera moves, and visual accents to the beat map instead of arbitrary timestamps.
27
- 12. After each file-writing stage, run `scripts/git_checkpoint.mjs` with the phase-specific allowlist from `references/git-checkpoint-workflow.md`. Do not create, switch, or recommend Git branches or worktrees.
28
- 13. Run pre-render quality gates on the base HTML.
29
- 14. Read `references/preview-controls-workflow.md`, run `scripts/inject_preview_controls.mjs` to create a preview HTML copy, and run `scripts/validate_hyperframes_html.py preview.html --preview-controls`.
30
- 15. Show a concise plan summary and preview path or HTML file to the user. Ask for confirmation before video render.
31
- 16. Render only after user confirmation. Prefer rendering the base HTML; if rendering the preview copy, use `?render=1`, `?preview=0`, or `<html data-render-mode="video">` so preview controls are hidden. Then report output path and any caveats.
23
+ 8. Clarify audio: ask whether to use narration, subtitles, animation/transition sound effects, and background music.
24
+ 9. If narration is used, use `scripts/generate_minimax_tts.mjs` for voice listing and TTS. Do not construct MiniMax TTS HTTP calls directly. If subtitles are requested, run `scripts/generate_subtitles.mjs`.
25
+ 10. If background music is requested or undecided, read `references/minimax-music-workflow.md` and use `scripts/generate_minimax_music.mjs` for every MiniMax music request. Do not construct MiniMax music HTTP calls directly. If MiniMax is unavailable or declined, ask for a local audio path or continue without BGM.
26
+ 11. If background music is used, read `references/beat-sync-workflow.md`, generate or obtain the audio, and run `scripts/analyze_music_beats.py` when the file is available.
27
+ 12. Ask the second clarification round with candidate context: visual direction, motion rhythm, generation mode, narration/subtitle/audio choices, music prompt/model when MiniMax is used, and beat-sync assumptions when background music is present.
28
+ 13. Write or assemble HTML. When beat-sync is enabled, align major reveals, cuts, transitions, camera moves, narration beats, subtitle cues, and visual accents to the beat map instead of arbitrary timestamps.
29
+ 14. After each file-writing stage, run `scripts/git_checkpoint.mjs` with the phase-specific allowlist from `references/git-checkpoint-workflow.md`. Do not create, switch, or recommend Git branches or worktrees.
30
+ 15. Run pre-render quality gates on the base HTML.
31
+ 16. Read `references/preview-controls-workflow.md`, run `scripts/inject_preview_controls.mjs` to create a preview HTML copy, and run `scripts/validate_hyperframes_html.py preview.html --preview-controls`.
32
+ 17. Show a concise plan summary and preview path or HTML file to the user. Ask for confirmation before video render.
33
+ 18. Render only after user confirmation. Prefer rendering the base HTML; if rendering the preview copy, use `?render=1`, `?preview=0`, or `<html data-render-mode="video">` so preview controls are hidden. Then report output path and any caveats.
32
34
 
33
35
  ## Interactive Questions
34
36
 
@@ -36,14 +38,15 @@ Prefer Codex interactive question tools such as `AskUserQuestion` or `request_us
36
38
 
37
39
  Use two rounds:
38
40
 
39
- - Round 1: purpose, format, duration, platform, required content, brand assets, sound effects, background music, and whether video render is expected in this turn.
40
- - Round 2: after catalog scoring, ask about style, motion, candidate selection, generation mode, and beat-sync assumptions when background music is present.
41
+ - Round 1: purpose, format, duration, platform, required content, outline/narration status, narration/subtitles, brand assets, sound effects, background music, and whether video render is expected in this turn.
42
+ - Round 2: after catalog scoring, ask about style, motion, candidate selection, generation mode, voice/emotion/subtitle style, and beat-sync assumptions when background music is present.
41
43
 
42
44
  Do not ask everything upfront when the brief is already specific. Ask only for missing or decision-changing information.
43
45
 
44
46
  ## References
45
47
 
46
48
  - Read `references/git-checkpoint-workflow.md` before any `/hyper-animator` run writes files or initializes output.
49
+ - Read `references/narration-audio-workflow.md` when first-run detection, restart, outline, narration, voiceover, subtitle, BGM timing, or final audio/video mixing is mentioned.
47
50
  - Read `references/hyperframes-intent-workflow.md` for the full AskUserQuestion workflow, generation-mode rules, scoring model, and render confirmation requirements.
48
51
  - Read `references/hyperframes-catalog-map.json` whenever selecting catalog candidates or determining visual references.
49
52
  - Read `references/hyperframes-agent-pseudocode.ts` when implementing the end-to-end loop or when the correct sequence is ambiguous.
@@ -94,7 +97,9 @@ If the validator fails, fix the HTML before asking the user to approve render.
94
97
  Before rendering, summarize:
95
98
 
96
99
  - generation mode;
100
+ - detected outline/narration paths, ambiguity status, and any outline/narration generation assumptions;
97
101
  - selected or referenced catalog items;
102
+ - narration usage, selected voice, emotion, voice audio path, subtitle style, subtitle JSON/SRT paths, and voice/BGM/SFX volume values when applicable;
98
103
  - sound effects and background music choices;
99
104
  - MiniMax provider status, model, generated audio path, metadata path, and redacted config source when MiniMax is used;
100
105
  - beat map path, BPM, duration, and timing assumptions when beat-sync is enabled;
@@ -0,0 +1,110 @@
1
+ # Narration Audio Workflow
2
+
3
+ Use this when a `/hyper-animator` run starts in a new directory, the user asks to restart, or the brief mentions outline, narration, voiceover, subtitles, BGM, beat sync, sound effects, or final audio/video mixing.
4
+
5
+ ## Start Or Restart
6
+
7
+ Run file detection before visual planning:
8
+
9
+ ```bash
10
+ node scripts/detect_project_files.mjs --dir . --pretty
11
+ ```
12
+
13
+ Rules:
14
+
15
+ - scan only the current directory top level;
16
+ - trust fixed names first: `outline.*` and `narration.*`;
17
+ - if multiple candidates are returned, ask the user to choose;
18
+ - if only outline exists, ask whether to generate narration from it;
19
+ - if only narration exists, ask whether to generate an outline from it;
20
+ - if narration is not used, do not generate voice audio or subtitles.
21
+
22
+ Codex writes or edits outline/narration content. Scripts only validate, convert, call MiniMax, and write artifacts.
23
+
24
+ ## Narration JSON
25
+
26
+ Use this shape:
27
+
28
+ ```json
29
+ {
30
+ "voice": "XiaoR_001",
31
+ "emotion": "calm",
32
+ "language": "zh",
33
+ "style": "casual",
34
+ "scenes": [
35
+ {
36
+ "scene": 1,
37
+ "title": "Scene title",
38
+ "sync_hint": "Visual sync hint",
39
+ "narration": "Spoken text",
40
+ "duration_estimate": 8
41
+ }
42
+ ]
43
+ }
44
+ ```
45
+
46
+ Before generating narration, ask language and style. Default language is `zh`.
47
+
48
+ ## MiniMax Voice
49
+
50
+ Do not hand-write MiniMax TTS HTTP calls. Use:
51
+
52
+ ```bash
53
+ node scripts/generate_minimax_tts.mjs --list-voices --voice-type all
54
+ ```
55
+
56
+ Prefer cloned voices, then generated voices, then system voices. Recommend three candidates by content tone and ask the user to choose. If listing fails, continue with `XiaoR_001` after telling the user.
57
+
58
+ Emotion labels:
59
+
60
+ ```text
61
+ happy, sad, angry, fearful, disgusted, surprised, calm, fluent, whipser
62
+ ```
63
+
64
+ Generate narration audio:
65
+
66
+ ```bash
67
+ node scripts/generate_minimax_tts.mjs \
68
+ --narration-json narration.json \
69
+ --output-dir hyper-animator-output/voice
70
+ ```
71
+
72
+ Use `--dry-run` before a real call when checking request shape or credentials.
73
+
74
+ ## Subtitles
75
+
76
+ Generate subtitles only when narration is used and the user wants subtitles:
77
+
78
+ ```bash
79
+ node scripts/generate_subtitles.mjs \
80
+ --narration narration.json \
81
+ --output-dir hyper-animator-output/subtitles \
82
+ --style clean
83
+ ```
84
+
85
+ Subtitle rules:
86
+
87
+ - short phrase cues;
88
+ - no trailing punctuation;
89
+ - one visible cue at a time;
90
+ - JSON and SRT outputs include timecodes.
91
+
92
+ ## BGM, Beat, And Schedule
93
+
94
+ BGM is the timing source when present:
95
+
96
+ 1. decide BGM generation, local import, or skip;
97
+ 2. if configured, generate BGM with `scripts/generate_minimax_music.mjs`;
98
+ 3. analyze the music with `scripts/analyze_music_beats.py`;
99
+ 4. schedule transitions, animation, narration, and subtitles against the beat map;
100
+ 5. generate HTML;
101
+ 6. preview and get user confirmation;
102
+ 7. render and mix final video/audio.
103
+
104
+ Default mix:
105
+
106
+ - voice: `1.0`;
107
+ - BGM: `0.3`;
108
+ - SFX: `0.3`.
109
+
110
+ If the user gives volume commands, adjust `voice`, `bgm`, and `sfx` independently in the final mix command or render handoff.