@agentmedia/schema 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.memory/cursor.json +3 -0
- package/.memory/memories.json +1 -0
- package/.memory/project.json +5 -0
- package/CLAUDE.md +7 -0
- package/dist/__tests__/character-pipeline.test.d.ts +2 -0
- package/dist/__tests__/character-pipeline.test.d.ts.map +1 -0
- package/dist/__tests__/character-pipeline.test.js +296 -0
- package/dist/__tests__/character-pipeline.test.js.map +1 -0
- package/dist/__tests__/text-to-video.test.d.ts +2 -0
- package/dist/__tests__/text-to-video.test.d.ts.map +1 -0
- package/dist/__tests__/text-to-video.test.js +67 -0
- package/dist/__tests__/text-to-video.test.js.map +1 -0
- package/dist/generators.d.ts +67 -0
- package/dist/generators.d.ts.map +1 -1
- package/dist/generators.js +11 -1
- package/dist/generators.js.map +1 -1
- package/dist/v2/character.d.ts +32 -0
- package/dist/v2/character.d.ts.map +1 -0
- package/dist/v2/character.js +31 -0
- package/dist/v2/character.js.map +1 -0
- package/dist/v2/generators.d.ts +69 -0
- package/dist/v2/generators.d.ts.map +1 -0
- package/dist/v2/generators.js +105 -0
- package/dist/v2/generators.js.map +1 -0
- package/dist/v2/index.d.ts +13 -0
- package/dist/v2/index.d.ts.map +1 -0
- package/dist/v2/index.js +14 -0
- package/dist/v2/index.js.map +1 -0
- package/dist/v2/selfie.d.ts +78 -0
- package/dist/v2/selfie.d.ts.map +1 -0
- package/dist/v2/selfie.js +98 -0
- package/dist/v2/selfie.js.map +1 -0
- package/dist/v2/subtitle.d.ts +31 -0
- package/dist/v2/subtitle.d.ts.map +1 -0
- package/dist/v2/subtitle.js +53 -0
- package/dist/v2/subtitle.js.map +1 -0
- package/dist/video.d.ts +171 -0
- package/dist/video.d.ts.map +1 -1
- package/dist/video.js +89 -0
- package/dist/video.js.map +1 -1
- package/package.json +6 -1
- package/scripts/generate-v2-docs.ts +548 -0
- package/src/__tests__/character-pipeline.test.ts +356 -0
- package/src/__tests__/text-to-video.test.ts +79 -0
- package/src/generators.ts +12 -0
- package/src/v2/character.ts +41 -0
- package/src/v2/generators.ts +186 -0
- package/src/v2/index.ts +15 -0
- package/src/v2/selfie.ts +115 -0
- package/src/v2/subtitle.ts +62 -0
- package/src/video.ts +164 -0
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-test.log +0 -14
- package/.turbo/turbo-typecheck.log +0 -4
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
// Copyright 2026 agent-media contributors. Apache-2.0 license.
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Generate v2 docs + SKILL.md from V2_GENERATORS.
|
|
6
|
+
*
|
|
7
|
+
* Reads packages/schema/src/v2/generators.ts and emits:
|
|
8
|
+
* - docs/v2/api-reference.md — Markdown reference for the v2 REST surface
|
|
9
|
+
* - skills/agent-media-v2/SKILL.md — Claude skill file pointing only at v2
|
|
10
|
+
*
|
|
11
|
+
* Both files are derived. Hand edits get blown away on the next run — the
|
|
12
|
+
* single source of truth is the registry.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
16
|
+
import { resolve, dirname } from 'node:path';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
19
|
+
import {
|
|
20
|
+
V2_GENERATORS,
|
|
21
|
+
V2_SHOT_PRESETS,
|
|
22
|
+
V2_VIBES,
|
|
23
|
+
quoteV2Credits,
|
|
24
|
+
type V2GeneratorRecord,
|
|
25
|
+
} from '../src/v2/index.js';
|
|
26
|
+
|
|
27
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
28
|
+
const repoRoot = resolve(__dirname, '../../..');
|
|
29
|
+
|
|
30
|
+
const DOCS_OUT = resolve(repoRoot, 'docs/v2/api-reference.md');
|
|
31
|
+
const SKILL_OUT = resolve(repoRoot, 'skills/agent-media-v2/SKILL.md');
|
|
32
|
+
|
|
33
|
+
const GENERATED_NOTE = `<!--
|
|
34
|
+
AUTO-GENERATED — do not hand-edit.
|
|
35
|
+
Source: packages/schema/src/v2/generators.ts
|
|
36
|
+
Regenerate: pnpm --filter @agentmedia/schema gen:v2-docs
|
|
37
|
+
-->`;
|
|
38
|
+
|
|
39
|
+
// ── Markdown helpers ───────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
function fmtInputSchema(def: V2GeneratorRecord): string {
|
|
42
|
+
const schema = zodToJsonSchema(def.inputSchema, {
|
|
43
|
+
name: `${def.id}_input`,
|
|
44
|
+
$refStrategy: 'none',
|
|
45
|
+
}) as {
|
|
46
|
+
definitions?: Record<string, { properties?: Record<string, unknown>; required?: string[] }>;
|
|
47
|
+
};
|
|
48
|
+
const body = schema.definitions?.[`${def.id}_input`] ?? (schema as any);
|
|
49
|
+
return '```json\n' + JSON.stringify(body, null, 2) + '\n```';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function fmtPricing(def: V2GeneratorRecord): string {
|
|
53
|
+
if (!def.pricing) return '_Pricing not declared on this generator._';
|
|
54
|
+
if (def.pricing.basis === 'one_shot') {
|
|
55
|
+
const c = def.pricing.baseCredits;
|
|
56
|
+
return `One-shot: **${c} credits** ($${(c / 100).toFixed(2)})`;
|
|
57
|
+
}
|
|
58
|
+
// per_clip — show 5/10/15
|
|
59
|
+
const rows = [5, 10, 15].map((s) => {
|
|
60
|
+
const c = quoteV2Credits(def.id as any, { durationSeconds: s });
|
|
61
|
+
return `| ${s}s | ${c} | $${(c / 100).toFixed(2)} |`;
|
|
62
|
+
});
|
|
63
|
+
return `Per-clip (base ${def.pricing.baseCredits} + ${def.pricing.perSecondCredits}/sec):\n\n| Duration | Credits | USD |\n|---|---:|---:|\n${rows.join('\n')}`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── docs/v2/api-reference.md ──────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
function renderApiReference(): string {
|
|
69
|
+
const generators = Object.values(V2_GENERATORS);
|
|
70
|
+
|
|
71
|
+
const toc = generators
|
|
72
|
+
.map((g) => `- [\`${g.rest?.method ?? ''} ${g.rest?.path ?? '(internal)'}\` — ${g.summary}](#${g.id})`)
|
|
73
|
+
.join('\n');
|
|
74
|
+
|
|
75
|
+
const sections = generators
|
|
76
|
+
.map((g) => {
|
|
77
|
+
const status = g.status === 'beta' ? ' · _beta_' : '';
|
|
78
|
+
return [
|
|
79
|
+
`## ${g.id}${status}`,
|
|
80
|
+
'',
|
|
81
|
+
`\`${g.rest?.method ?? ''} ${g.rest?.path ?? '(internal)'}\``,
|
|
82
|
+
'',
|
|
83
|
+
g.description,
|
|
84
|
+
'',
|
|
85
|
+
'### Pricing',
|
|
86
|
+
'',
|
|
87
|
+
fmtPricing(g),
|
|
88
|
+
'',
|
|
89
|
+
'### Request body',
|
|
90
|
+
'',
|
|
91
|
+
fmtInputSchema(g),
|
|
92
|
+
'',
|
|
93
|
+
'### Response',
|
|
94
|
+
'',
|
|
95
|
+
g.output === 'video_url'
|
|
96
|
+
? 'Returns a job submission. Poll `GET /v1/videos/{job_id}` until `status: "completed"`; the final row carries `video_url`.'
|
|
97
|
+
: 'Returns a job submission. Poll `GET /v1/videos/{job_id}` until `status: "completed"`; the final row carries the new `character_id` (`char_xxxxxxxxxx`).',
|
|
98
|
+
'',
|
|
99
|
+
'#### Submission (201)',
|
|
100
|
+
'',
|
|
101
|
+
'```json',
|
|
102
|
+
JSON.stringify(
|
|
103
|
+
{
|
|
104
|
+
job_id: '<uuid>',
|
|
105
|
+
status: 'submitted',
|
|
106
|
+
credits_deducted: g.pricing
|
|
107
|
+
? g.pricing.basis === 'one_shot'
|
|
108
|
+
? g.pricing.baseCredits
|
|
109
|
+
: g.pricing.baseCredits + g.pricing.perSecondCredits * 8
|
|
110
|
+
: 0,
|
|
111
|
+
generator: g.id,
|
|
112
|
+
},
|
|
113
|
+
null,
|
|
114
|
+
2,
|
|
115
|
+
),
|
|
116
|
+
'```',
|
|
117
|
+
'',
|
|
118
|
+
...(g.cli?.examples?.length
|
|
119
|
+
? [
|
|
120
|
+
'### CLI examples',
|
|
121
|
+
'',
|
|
122
|
+
'```bash',
|
|
123
|
+
...g.cli.examples,
|
|
124
|
+
'```',
|
|
125
|
+
'',
|
|
126
|
+
]
|
|
127
|
+
: []),
|
|
128
|
+
].join('\n');
|
|
129
|
+
})
|
|
130
|
+
.join('\n---\n\n');
|
|
131
|
+
|
|
132
|
+
return [
|
|
133
|
+
GENERATED_NOTE,
|
|
134
|
+
'',
|
|
135
|
+
'# agent-media v2 — API reference',
|
|
136
|
+
'',
|
|
137
|
+
'_Public REST surface for v2 generators (Selfie, Character). Auth is a Bearer API key (`ma_xxx`)._',
|
|
138
|
+
'',
|
|
139
|
+
'**Base URL:** `https://api.agent-media.ai`',
|
|
140
|
+
'',
|
|
141
|
+
'## Endpoints',
|
|
142
|
+
'',
|
|
143
|
+
toc,
|
|
144
|
+
'',
|
|
145
|
+
'---',
|
|
146
|
+
'',
|
|
147
|
+
sections,
|
|
148
|
+
'## Shared',
|
|
149
|
+
'',
|
|
150
|
+
'### Authentication',
|
|
151
|
+
'',
|
|
152
|
+
'Every v2 request sends `Authorization: Bearer ma_xxx`. Get a key via `agent-media login` (CLI) or the dashboard.',
|
|
153
|
+
'',
|
|
154
|
+
'### Polling',
|
|
155
|
+
'',
|
|
156
|
+
'`GET /v1/videos/{job_id}` returns the same shape for v1 and v2 jobs. v2-specific fields:',
|
|
157
|
+
'',
|
|
158
|
+
'- `character_id` — present on jobs that create a v2 character (`char_xxxxxxxxxx`).',
|
|
159
|
+
'- `video_url` — present on completed video jobs.',
|
|
160
|
+
'',
|
|
161
|
+
'### Shot grammar (Selfie)',
|
|
162
|
+
'',
|
|
163
|
+
`Selfie's \`preset\` field accepts one of:`,
|
|
164
|
+
'',
|
|
165
|
+
V2_SHOT_PRESETS.map((p) => `- \`${p}\``).join('\n'),
|
|
166
|
+
'',
|
|
167
|
+
`Or \`custom-scene:<text>\` to compose a new shot ad-hoc.`,
|
|
168
|
+
'',
|
|
169
|
+
'### Vibes (Selfie)',
|
|
170
|
+
'',
|
|
171
|
+
V2_VIBES.map((v) => `- \`${v}\``).join('\n'),
|
|
172
|
+
'',
|
|
173
|
+
].join('\n');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ── skills/agent-media-v2/SKILL.md ─────────────────────────────────────────
|
|
177
|
+
|
|
178
|
+
function renderSkill(): string {
|
|
179
|
+
const generators = Object.values(V2_GENERATORS);
|
|
180
|
+
const generatorBlocks = generators
|
|
181
|
+
.map((g) => {
|
|
182
|
+
const cliExample = g.cli?.examples?.[0] ?? `agent-media ${g.cli?.command ?? g.id} …`;
|
|
183
|
+
const pricingLine = g.pricing
|
|
184
|
+
? g.pricing.basis === 'one_shot'
|
|
185
|
+
? `Cost: **${g.pricing.baseCredits} credits** ($${(g.pricing.baseCredits / 100).toFixed(2)}).`
|
|
186
|
+
: `Cost: **${g.pricing.baseCredits} + ${g.pricing.perSecondCredits}/sec** (8s ≈ ${quoteV2Credits(g.id as any, { durationSeconds: 8 })} credits ≈ $${(quoteV2Credits(g.id as any, { durationSeconds: 8 }) / 100).toFixed(2)}).`
|
|
187
|
+
: '';
|
|
188
|
+
return [
|
|
189
|
+
`### ${g.id}`,
|
|
190
|
+
'',
|
|
191
|
+
g.description,
|
|
192
|
+
'',
|
|
193
|
+
pricingLine,
|
|
194
|
+
'',
|
|
195
|
+
'**CLI:**',
|
|
196
|
+
'',
|
|
197
|
+
'```bash',
|
|
198
|
+
cliExample,
|
|
199
|
+
'```',
|
|
200
|
+
'',
|
|
201
|
+
'**MCP tool:** `' + (g.mcp?.toolName ?? '(none)') + '`',
|
|
202
|
+
'',
|
|
203
|
+
'**REST:** `' + (g.rest?.method ?? '') + ' ' + (g.rest?.path ?? '(internal)') + '`',
|
|
204
|
+
'',
|
|
205
|
+
].join('\n');
|
|
206
|
+
})
|
|
207
|
+
.join('---\n\n');
|
|
208
|
+
|
|
209
|
+
return [
|
|
210
|
+
'---',
|
|
211
|
+
'name: agent-media-v2',
|
|
212
|
+
'description: AI UGC video production via agent-media v2 — Selfie videos and reusable Characters. Use this skill when the user wants to make TikTok-style "AI person talking to camera" clips or save a character for reuse across multiple generations.',
|
|
213
|
+
'homepage: https://agent-media.ai/skill',
|
|
214
|
+
`version: 1.3.0`,
|
|
215
|
+
'---',
|
|
216
|
+
'',
|
|
217
|
+
GENERATED_NOTE,
|
|
218
|
+
'',
|
|
219
|
+
'# agent-media — STRICT RULES (read first, every time)',
|
|
220
|
+
'',
|
|
221
|
+
'## 0. CONVERSATION FLOW — follow this in order',
|
|
222
|
+
'',
|
|
223
|
+
'Before you EVER call the CLI, walk the user through these steps. Ask in batches of 2-3 questions at a time, never one-by-one, never silently default. Use the user\'s answers to populate the CLI args.',
|
|
224
|
+
'',
|
|
225
|
+
'### Step 1 — What\'s the video about?',
|
|
226
|
+
'',
|
|
227
|
+
'This is the FIRST question, every time. Even if the user typed a full prompt with a script, confirm:',
|
|
228
|
+
'',
|
|
229
|
+
'> *"Got it — quick check before I generate:*',
|
|
230
|
+
'> *• What\'s the video about? (the topic / what they\'re selling / what\'s happening in the scene)*',
|
|
231
|
+
'> *• What\'s the exact line you want them to say? (the script — 1-3 sentences usually works best)"*',
|
|
232
|
+
'',
|
|
233
|
+
'Don\'t skip this. The "topic" informs the preset + vibe selection later. If the user already gave you a script, repeat it back so they can confirm or tweak it.',
|
|
234
|
+
'',
|
|
235
|
+
'### Step 2 — Who\'s the character?',
|
|
236
|
+
'',
|
|
237
|
+
'**agent-media generates the character image itself from your description. You DO NOT need to provide a photo.** A description like "25yo asian woman with long wavy dark hair" is enough — the pipeline produces a consistent on-model person.',
|
|
238
|
+
'',
|
|
239
|
+
'Three valid input paths (in order of preference):',
|
|
240
|
+
'',
|
|
241
|
+
'1. **Description only** (the default — use this 95% of the time)',
|
|
242
|
+
' Just `--description "..."`. agent-media generates the portrait and the multi-pose sheet, then runs the video. No photo, no upload, no questions about photos. Example:',
|
|
243
|
+
' ```bash',
|
|
244
|
+
' agent-media selfie --description "25yo asian woman, long wavy dark hair, soft smile" --script "..."',
|
|
245
|
+
' ```',
|
|
246
|
+
'',
|
|
247
|
+
'2. **Saved character** (`--character char_xxxxxxxxxx`)',
|
|
248
|
+
' Use when the user references a person from a previous run ("use the same girl again", "the one from yesterday\'s video"). Run `agent-media character` to list saved characters if you need to look one up.',
|
|
249
|
+
'',
|
|
250
|
+
'3. **Real-person photo + description** (`--photo <file|url> --description "..."`)',
|
|
251
|
+
' Use ONLY when the user explicitly says "use THIS person" and gives you a photo of someone specific. Otherwise default to path 1.',
|
|
252
|
+
'',
|
|
253
|
+
'Hard rules:',
|
|
254
|
+
'- NEVER ask the user "do you have a photo?". Default to description-only.',
|
|
255
|
+
'- NEVER ask the user about the underlying models (gpt-image-2, Seedance, etc.). They are implementation details. Just say "agent-media generates the character".',
|
|
256
|
+
'- NEVER fall back to `agent-media ugc` or a stock actor library. Forbidden.',
|
|
257
|
+
'- If the user wants the SAME person across multiple videos, run `agent-media character create --description "..."` once first (no photo needed) to get a `char_xxxxxxxxxx`, then pass `--character <id>` to every selfie.',
|
|
258
|
+
'',
|
|
259
|
+
'### Step 3 — Where + how does it feel? (preset + vibe)',
|
|
260
|
+
'',
|
|
261
|
+
'Pick one **preset** (location/shot grammar) + one **vibe** (emotional tone). If you can guess from the topic, propose them as defaults to confirm; if not, ask.',
|
|
262
|
+
'',
|
|
263
|
+
'**Presets (20, one of):**',
|
|
264
|
+
'',
|
|
265
|
+
'| Preset | Best for |',
|
|
266
|
+
'|---|---|',
|
|
267
|
+
'| `bedroom-morning-ritual` | Default. Skincare, routine, "morning vibes" content |',
|
|
268
|
+
'| `getting-ready-mirror-edge` | OOTD, makeup, fashion |',
|
|
269
|
+
'| `bathroom-skincare-routine` | Beauty, hair care, skincare reveals |',
|
|
270
|
+
'| `bedside-lamp-evening` | Wind-down, journal, ASMR-style |',
|
|
271
|
+
'| `kitchen-glow-up` | Food, drinks, supplements, cooking |',
|
|
272
|
+
'| `backyard-morning-coffee` | Lifestyle, mindfulness, slow content |',
|
|
273
|
+
'| `picnic-blanket-outdoor` | Outdoor, summer, friends |',
|
|
274
|
+
'| `car-quick-honest-review` | Honest reviews, "I just bought this..." |',
|
|
275
|
+
'| `car-passenger-honest` | Same vibe, passenger angle |',
|
|
276
|
+
'| `outdoor-walking-talking` | Walking-and-talking, candid |',
|
|
277
|
+
'| `couch-haul-show-off` | Unboxing, hauls, "look what I got" |',
|
|
278
|
+
'| `closet-fit-check` | Fashion, fit-checks, OOTD |',
|
|
279
|
+
'| `studio-apartment-tour` | Lifestyle, apartment content |',
|
|
280
|
+
'| `balcony-evening-vibes` | Aesthetic, lifestyle, golden hour |',
|
|
281
|
+
'| `desk-wfh-quick-pitch` | SaaS, productivity, work-from-home |',
|
|
282
|
+
'| `cafe-window-seat` | Lifestyle, work, coffee culture |',
|
|
283
|
+
'| `office-bathroom-discreet` | Workplace anecdotes, "let me tell you" |',
|
|
284
|
+
'| `gym-post-workout` | Fitness, supplements, wellness |',
|
|
285
|
+
'| `salon-mirror-result` | Hair, beauty reveals, "before/after" |',
|
|
286
|
+
'| `travel-hotel-room-review` | Travel, hotel reviews |',
|
|
287
|
+
'',
|
|
288
|
+
'**Vibes (5, one of):** `excited` · `calm` · `sassy` · `serious` · `curious`',
|
|
289
|
+
'',
|
|
290
|
+
'- `excited` — high energy, "you won\'t believe this", default for hype/product/reveal',
|
|
291
|
+
'- `calm` — softer, intimate, default for wellness/skincare/lifestyle',
|
|
292
|
+
'- `sassy` — playful, eye-roll, default for "let me tell you" anecdotes',
|
|
293
|
+
'- `serious` — measured, default for honest reviews / SaaS / B2B',
|
|
294
|
+
'- `curious` — thoughtful, leaning-in, default for storytelling',
|
|
295
|
+
'',
|
|
296
|
+
'### Step 4 — How long?',
|
|
297
|
+
'',
|
|
298
|
+
'**Allowed durations: 5, 10, or 15 seconds. ONLY these three.** Default to 10 if unsure. The schema rejects any other value (the worker will reject 6, 8, 12, etc.).',
|
|
299
|
+
'',
|
|
300
|
+
'- `5` — single hook, no body. Best for ads with strong intro.',
|
|
301
|
+
'- `10` — default. Hook + payoff. Best for organic UGC.',
|
|
302
|
+
'- `15` — full mini-story with setup + reveal.',
|
|
303
|
+
'',
|
|
304
|
+
'### Step 5 — Voice direction (optional, but improves quality)',
|
|
305
|
+
'',
|
|
306
|
+
'Ask: *"Any voice direction? E.g. \'whisper\', \'gen-z deadpan\', \'NYC accent\', \'sleepy morning voice\'. Skip if you\'re happy with the default."*',
|
|
307
|
+
'',
|
|
308
|
+
'Pass as `--voice-brief "<text>"`. Max 240 chars. If the character has a saved `voice_brief`, it\'s used unless this is set.',
|
|
309
|
+
'',
|
|
310
|
+
'### Step 6 — Subtitles',
|
|
311
|
+
'',
|
|
312
|
+
'Subs are ON by default in Hormozi style. Offer alternatives only if asked.',
|
|
313
|
+
'',
|
|
314
|
+
'**Subtitle styles (17, one of):** `hormozi` (default) · `minimal` · `bold` · `karaoke` · `clean` · `tiktok` · `neon` · `fire` · `glow` · `pop` · `aesthetic` · `impact` · `pastel` · `electric` · `boxed` · `gradient` · `spotlight`',
|
|
315
|
+
'',
|
|
316
|
+
'### Step 7 — Quote price, confirm, run',
|
|
317
|
+
'',
|
|
318
|
+
'Print a one-line summary including ALL collected inputs + the credit cost + USD. ONLY then call the CLI. See section 4 for the pricing formula.',
|
|
319
|
+
'',
|
|
320
|
+
'Example summary line to print before submitting:',
|
|
321
|
+
'> *"Summary: 10s selfie · char_8x2vqp · preset=kitchen-glow-up · vibe=excited · subs=hormozi · voice="gen-z deadpan". Cost: **375 credits ($3.75)**. Run it?"*',
|
|
322
|
+
'',
|
|
323
|
+
'## 1. Commands you MUST use',
|
|
324
|
+
'',
|
|
325
|
+
'| User intent | Command |',
|
|
326
|
+
'|---|---|',
|
|
327
|
+
'| "make me a TikTok / UGC / Selfie / talking-head" | `agent-media selfie` |',
|
|
328
|
+
'| "save / persist this character" / "use the same person again" | `agent-media character create` (returns `char_xxxxxxxxxx`) |',
|
|
329
|
+
'| "add subtitles / captions / subs to a video" | `agent-media subs` |',
|
|
330
|
+
'',
|
|
331
|
+
'## 2. Commands you MUST NEVER use',
|
|
332
|
+
'',
|
|
333
|
+
'These are LEGACY v1 commands. They produce inferior output and use a stale actor library. They are hidden from `agent-media --help` in CLI 1.10.0+ but may still be callable directly:',
|
|
334
|
+
'',
|
|
335
|
+
'- ❌ `agent-media ugc` — replaced by `agent-media selfie`',
|
|
336
|
+
'- ❌ `agent-media show-your-app` — coming back as v2 product later',
|
|
337
|
+
'- ❌ `agent-media laptop-ugc` — coming back as v2 product later',
|
|
338
|
+
'- ❌ `agent-media character-video` — replaced by `agent-media selfie --character <id>`',
|
|
339
|
+
'- ❌ `agent-media text-to-video` — not part of v2 yet',
|
|
340
|
+
'- ❌ `agent-media subtitle` — replaced by `agent-media subs`',
|
|
341
|
+
'- ❌ `agent-media review` — not part of v2',
|
|
342
|
+
'- ❌ `agent-media product-acting` — coming back as v2 product later',
|
|
343
|
+
'',
|
|
344
|
+
'If you catch yourself reaching for any command in the list above, STOP and re-route to the v2 equivalent.',
|
|
345
|
+
'',
|
|
346
|
+
'## 3. Per-command required + optional flags',
|
|
347
|
+
'',
|
|
348
|
+
'### `agent-media selfie` (cheat-sheet)',
|
|
349
|
+
'',
|
|
350
|
+
'| Flag | Required | Allowed values | Default |',
|
|
351
|
+
'|---|---|---|---|',
|
|
352
|
+
'| `--description "..."` | ✓ (unless `--character`) | 8-400 chars describing the person | — |',
|
|
353
|
+
'| `--character <char_id>` | OR | `char_xxxxxxxxxx` (saved character) | — |',
|
|
354
|
+
'| `--photo <file\\|url>` | optional | only when user gives an exact-person reference photo | — |',
|
|
355
|
+
'| `--script "..."` | ✓ | 4-600 chars | — |',
|
|
356
|
+
'| `--preset <name>` | | one of 20 (see Step 3) | `bedroom-morning-ritual` |',
|
|
357
|
+
'| `--vibe <name>` | | `excited\\|calm\\|sassy\\|serious\\|curious` | `excited` |',
|
|
358
|
+
'| `--duration <n>` | | **`5` \\| `10` \\| `15`** | `10` |',
|
|
359
|
+
'| `--voice-brief "..."` | | 4-240 chars | (none / character default) |',
|
|
360
|
+
'| `--subs-style <name>` | | one of 17 (see Step 6) | `hormozi` |',
|
|
361
|
+
'| `--no-subs` | | flag | (subs on) |',
|
|
362
|
+
'',
|
|
363
|
+
'### `agent-media character create`',
|
|
364
|
+
'',
|
|
365
|
+
'| Flag | Required | Notes |',
|
|
366
|
+
'|---|---|---|',
|
|
367
|
+
'| `--name <slug>` | ✓ | lowercase, hyphens, e.g. `sofia` |',
|
|
368
|
+
'| `--description "..."` | ✓ | free text — age, look, vibe. agent-media generates the portrait from this. |',
|
|
369
|
+
'| `--photo <file\\|url>` | optional | ONLY when the user wants an exact real-person likeness. Otherwise omit. |',
|
|
370
|
+
'| `--voice-brief "..."` | | default voice direction baked into character |',
|
|
371
|
+
'| `--preset-default <name>` | | preset to use when this character runs selfie |',
|
|
372
|
+
'',
|
|
373
|
+
'Returns `char_xxxxxxxxxx` — copy and reuse it.',
|
|
374
|
+
'',
|
|
375
|
+
'### `agent-media subs`',
|
|
376
|
+
'',
|
|
377
|
+
'| Flag | Required | Notes |',
|
|
378
|
+
'|---|---|---|',
|
|
379
|
+
'| `--video <url>` | ✓ | publicly-fetchable mp4 URL |',
|
|
380
|
+
'| `--style <name>` | | one of 17 (see Step 6). Default: `hormozi`. |',
|
|
381
|
+
'| `--transcript "..."` | | skip Whisper if you already have the exact words |',
|
|
382
|
+
'| `--language <code>` | | ISO code (`en`, `es`, `pt`, `fr`, …) |',
|
|
383
|
+
'',
|
|
384
|
+
'## 4. PRICING FORMULA — do not improvise',
|
|
385
|
+
'',
|
|
386
|
+
'**1 credit = $0.01 USD. Period.** Never quote a price without using this conversion.',
|
|
387
|
+
'',
|
|
388
|
+
'Selfie cost = `75 base + 30 × seconds`:',
|
|
389
|
+
'',
|
|
390
|
+
'| Duration | Credits | USD |',
|
|
391
|
+
'|---|---:|---:|',
|
|
392
|
+
'| 5s | 225 | **$2.25** |',
|
|
393
|
+
'| 10s | 375 | **$3.75** |',
|
|
394
|
+
'| 15s | 525 | **$5.25** |',
|
|
395
|
+
'',
|
|
396
|
+
'Character create = 27 credits = **$0.27** (one-shot).',
|
|
397
|
+
'Subtitle = `0 base + 3 × seconds` (a 10s clip = 30 credits = **$0.30**).',
|
|
398
|
+
'',
|
|
399
|
+
'If you find yourself about to print any USD value not in this table or derivable by `credits / 100`, STOP. Recompute.',
|
|
400
|
+
'',
|
|
401
|
+
'## 5. Output handling',
|
|
402
|
+
'',
|
|
403
|
+
'Every command returns a job id. Poll until terminal:',
|
|
404
|
+
'',
|
|
405
|
+
'```bash',
|
|
406
|
+
'agent-media status <job-id>',
|
|
407
|
+
'```',
|
|
408
|
+
'',
|
|
409
|
+
'When `status: "completed"`, the response carries `video_url`. Print the FULL url to the user verbatim — do not abbreviate.',
|
|
410
|
+
'',
|
|
411
|
+
'Sync mode (blocking): add `--sync` to selfie/subs and the CLI will poll for you and print the URL when done.',
|
|
412
|
+
'',
|
|
413
|
+
'### For `agent-media character create`',
|
|
414
|
+
'',
|
|
415
|
+
'Required: `--photo <file|url>`, `--name <slug>`, `--description "..."`.',
|
|
416
|
+
'Optional: `--voice-brief`, `--preset`.',
|
|
417
|
+
'',
|
|
418
|
+
'Cost: 27 credits ($0.27). Confirm.',
|
|
419
|
+
'',
|
|
420
|
+
'### For `agent-media subs`',
|
|
421
|
+
'',
|
|
422
|
+
'Required: `--video <url>`.',
|
|
423
|
+
'Optional: `--style` (default `hormozi`, one of 17), `--transcript` (skip Whisper), `--language`.',
|
|
424
|
+
'',
|
|
425
|
+
'Cost: ~24 credits / 8s clip. Confirm.',
|
|
426
|
+
'',
|
|
427
|
+
'## 4. Output handling',
|
|
428
|
+
'',
|
|
429
|
+
'Every command returns a job id. Poll until terminal:',
|
|
430
|
+
'',
|
|
431
|
+
'```bash',
|
|
432
|
+
'agent-media status <job-id>',
|
|
433
|
+
'```',
|
|
434
|
+
'',
|
|
435
|
+
'When status is `completed`, the final mp4 URL is printed. Show it to the user. Do not summarize or shorten it.',
|
|
436
|
+
'',
|
|
437
|
+
'---',
|
|
438
|
+
'',
|
|
439
|
+
'# agent-media v2 — Selfie + Characters',
|
|
440
|
+
'',
|
|
441
|
+
'The v2 surface ships two generators today: **Selfie** (a 9:16 TikTok-style video of an AI person talking to camera) and **character_create** (persist an AI character so subsequent Selfies stay on-model). When the next v2 product lands (Product-in-hands), it appears here automatically — this file is generated from `packages/schema/src/v2/generators.ts`.',
|
|
442
|
+
'',
|
|
443
|
+
'## When to use this skill',
|
|
444
|
+
'',
|
|
445
|
+
'Trigger phrases:',
|
|
446
|
+
'- "make me a TikTok / Selfie / UGC video"',
|
|
447
|
+
'- "have an AI person say …"',
|
|
448
|
+
'- "create a character / actor / persona for reuse"',
|
|
449
|
+
'- "use the same person across multiple videos"',
|
|
450
|
+
'',
|
|
451
|
+
'## Setup',
|
|
452
|
+
'',
|
|
453
|
+
'```bash',
|
|
454
|
+
'npm install -g agent-media-cli',
|
|
455
|
+
'agent-media login # opens browser; pastes ma_xxx into ~/.agent-media',
|
|
456
|
+
'```',
|
|
457
|
+
'',
|
|
458
|
+
'Or use the SDK directly:',
|
|
459
|
+
'',
|
|
460
|
+
'```ts',
|
|
461
|
+
"import { AgentMedia } from '@agentmedia/sdk';",
|
|
462
|
+
"const client = new AgentMedia({ apiKey: process.env.AGENT_MEDIA_API_KEY! });",
|
|
463
|
+
"const job = await client.v2.createCharacter({ photo_url: '…', display_name: 'sofia', description: '…' });",
|
|
464
|
+
"const done = await client.v2.runUntilDone(Promise.resolve(job));",
|
|
465
|
+
'```',
|
|
466
|
+
'',
|
|
467
|
+
'## Generators',
|
|
468
|
+
'',
|
|
469
|
+
generatorBlocks,
|
|
470
|
+
'## Recommended flow (multi-clip from one character)',
|
|
471
|
+
'',
|
|
472
|
+
'1. **Create the character once** with `character create`. Returns `char_xxxxxxxxxx`.',
|
|
473
|
+
'2. **Reuse that id** for every subsequent Selfie — same face, same voice, same seed.',
|
|
474
|
+
'3. If the user wants a different *look*, make a new character; don\'t mutate the existing one.',
|
|
475
|
+
'',
|
|
476
|
+
'```bash',
|
|
477
|
+
"ID=$(agent-media character create --photo me.png --name sofia \\",
|
|
478
|
+
' --description "25, asian, long wavy dark hair, casual confident" --quiet)',
|
|
479
|
+
'',
|
|
480
|
+
'agent-media selfie --character $ID --script "Got my first 100 customers in 30 days." --preset desk-wfh-quick-pitch',
|
|
481
|
+
'agent-media selfie --character $ID --script "Here\'s how I did it." --preset bedroom-morning-ritual',
|
|
482
|
+
'```',
|
|
483
|
+
'',
|
|
484
|
+
'## Shot grammar (Selfie)',
|
|
485
|
+
'',
|
|
486
|
+
'`--preset` accepts one of:',
|
|
487
|
+
'',
|
|
488
|
+
V2_SHOT_PRESETS.map((p) => `- \`${p}\``).join('\n'),
|
|
489
|
+
'',
|
|
490
|
+
'Or pass `--preset custom-scene:"<your scene description>"` for an ad-hoc setup.',
|
|
491
|
+
'',
|
|
492
|
+
'## Vibes',
|
|
493
|
+
'',
|
|
494
|
+
V2_VIBES.map((v) => `- \`${v}\``).join('\n'),
|
|
495
|
+
'',
|
|
496
|
+
'## Realism rubric',
|
|
497
|
+
'',
|
|
498
|
+
'Every Selfie clip is composed with these constraints baked into the prompt:',
|
|
499
|
+
'',
|
|
500
|
+
'1. Skin micro-detail visible — pores, freckles, oil sheen, baby hairs at hairline.',
|
|
501
|
+
'2. Hands always doing something — hair-touch, strap-fix, product hold.',
|
|
502
|
+
'3. Mouth caught mid-syllable when talking, not closed.',
|
|
503
|
+
'4. Eyes slightly off-center to camera, not a dead stare.',
|
|
504
|
+
'5. Single mixed light source (daylight + warm bulb).',
|
|
505
|
+
'6. Real setting (bedroom / kitchen / car / dresser-corner) — never plain wall / studio.',
|
|
506
|
+
'7. Outfit plain + matte or satin — never patterned or logo\'d.',
|
|
507
|
+
'8. Hair long, brushed, in motion.',
|
|
508
|
+
'9. Product (if any) held mid-chest, ~25° tilt.',
|
|
509
|
+
'',
|
|
510
|
+
'You don\'t need to repeat these in the script — they\'re always applied.',
|
|
511
|
+
'',
|
|
512
|
+
'## Error handling',
|
|
513
|
+
'',
|
|
514
|
+
'| Error code | What it means |',
|
|
515
|
+
'|---|---|',
|
|
516
|
+
'| `VALIDATION_ERROR` | Inputs failed Zod validation. Look at `error.issues`. |',
|
|
517
|
+
'| `INSUFFICIENT_CREDITS` | User\'s balance is below the quote. Tell them the exact amount needed. |',
|
|
518
|
+
'| `MISSING_CHARACTER_INPUT` | Selfie was called without either `--character` or `--photo + --description`. |',
|
|
519
|
+
'| `AMBIGUOUS_CHARACTER_INPUT` | Both `--character` and `--photo` were passed. Pick one. |',
|
|
520
|
+
'| `JOB_FAILED` | Worker reported failure. `error_message` carries the reason. |',
|
|
521
|
+
'| `POLL_TIMEOUT` | Job didn\'t complete within 30 minutes. Surface the job id; it may still finish. |',
|
|
522
|
+
'',
|
|
523
|
+
'## When NOT to use this skill',
|
|
524
|
+
'',
|
|
525
|
+
'- The user has an EXISTING legacy job they want to check or modify — use the v1 commands (`agent-media status`, `agent-media ugc`, etc.).',
|
|
526
|
+
'- The user wants a Show-Your-App / Product-Acting / Laptop-UGC clip — those products live in the v1 surface (separate generators, separate skill).',
|
|
527
|
+
'',
|
|
528
|
+
].join('\n');
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// ── Run ────────────────────────────────────────────────────────────────────
|
|
532
|
+
|
|
533
|
+
function main() {
|
|
534
|
+
mkdirSync(dirname(DOCS_OUT), { recursive: true });
|
|
535
|
+
mkdirSync(dirname(SKILL_OUT), { recursive: true });
|
|
536
|
+
|
|
537
|
+
const docs = renderApiReference();
|
|
538
|
+
writeFileSync(DOCS_OUT, docs, 'utf8');
|
|
539
|
+
|
|
540
|
+
const skill = renderSkill();
|
|
541
|
+
writeFileSync(SKILL_OUT, skill, 'utf8');
|
|
542
|
+
|
|
543
|
+
console.log(`✓ wrote ${DOCS_OUT} (${docs.length} bytes)`);
|
|
544
|
+
console.log(`✓ wrote ${SKILL_OUT} (${skill.length} bytes)`);
|
|
545
|
+
console.log(` generators emitted: ${Object.keys(V2_GENERATORS).length}`);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
main();
|