@amg555/frameforge-mcp 2.3.2 → 2.3.4
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/.mcp.json +2 -4
- package/CHANGELOG.md +24 -1
- package/CLAUDE.md +104 -26
- package/README.md +17 -12
- package/SECURITY.md +1 -3
- package/package.json +7 -3
- package/plugins/video-pipeline/.claude-plugin/plugin.json +19 -0
- package/plugins/video-pipeline/skills/html-renderer/SKILL.md +292 -0
- package/plugins/video-pipeline/skills/video-pipeline/SKILL.md +308 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/ANIMATION_GUIDE.md +257 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/STORYBOARD.md +77 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/index.html +80 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/pipeline/briefings/diagram-author.md +43 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/pipeline/briefings/orchestrator.md +56 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/pipeline/briefings/scene-author.md +42 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/reel/engine.css +140 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/reel/engine.js +870 -0
- package/plugins/video-pipeline/skills/video-pipeline/assets/project/reel/virtual-clock.js +138 -0
- package/plugins/video-pipeline/skills/video-pipeline/references/mcp-tools.md +167 -0
- package/plugins/video-pipeline/skills/video-pipeline/references/providers.md +174 -0
- package/scripts/db-migrate.js +112 -0
- package/scripts/deep-validate.js +326 -0
- package/scripts/generate-initial-assets.js +342 -0
- package/scripts/html-renderer/new-project.mjs +408 -0
- package/scripts/html-renderer/transcribe.py +294 -0
- package/src/asset-library/README.md +2 -2
- package/src/asset-library/asset-library.js +1 -1
- package/src/cli/doctor.js +12 -5
- package/src/cli/make.js +52 -71
- package/src/cli/setup-keys.js +15 -17
- package/src/fact-checker/review-gate.js +5 -1
- package/src/harness/bin/harness.mjs +324 -0
- package/src/harness/lib/browser.mjs +137 -0
- package/src/harness/lib/capture.mjs +161 -0
- package/src/harness/lib/encode.mjs +89 -0
- package/src/harness/lib/jobs.mjs +135 -0
- package/src/harness/lib/pixels.mjs +137 -0
- package/src/harness/lib/sheets.mjs +164 -0
- package/src/harness/lib/verify-purity.mjs +119 -0
- package/src/harness/test/fixtures/broken-scene.html +27 -0
- package/src/harness/test/fixtures/reference-scene.html +23 -0
- package/src/harness/test/fixtures/sample-script.json +18 -0
- package/src/mcp-server/server.js +285 -144
- package/src/pipeline/telemetry.js +1 -0
- package/src/utils/log-redaction.js +5 -3
- package/src/video-renderer/renderer.js +12 -4
- package/src/worker/server-render-free.js +2 -3
- package/src/workflows/assets.js +20 -43
- package/src/workflows/ingest.js +5 -5
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Initial Asset Library
|
|
3
|
+
*
|
|
4
|
+
* Generates a starter set of composable assets. Characters are generic/neutral
|
|
5
|
+
* so they work for any topic — not locked to any historical period or style.
|
|
6
|
+
* Customise ASSET_DEFINITIONS before running for your project.
|
|
7
|
+
*
|
|
8
|
+
* Total: ~47 composable assets
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/generate-initial-assets.js [--dry-run]
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { AssetLibrary } = require('../src/asset-library/asset-library');
|
|
15
|
+
|
|
16
|
+
// Asset definitions — edit these to match your project's visual identity.
|
|
17
|
+
const ASSET_DEFINITIONS = {
|
|
18
|
+
characters: [
|
|
19
|
+
{
|
|
20
|
+
id: 'narrator',
|
|
21
|
+
description: 'Friendly narrator figure, simple flat-design illustration, neutral modern clothing',
|
|
22
|
+
poses: ['standing', 'running', 'sitting', 'jumping'],
|
|
23
|
+
emotions: ['neutral', 'happy', 'angry', 'sad', 'surprised', 'determined']
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'expert',
|
|
27
|
+
description: 'Professional expert figure, smart casual clothing, confident posture, simple flat-design illustration',
|
|
28
|
+
poses: ['standing', 'running', 'sitting', 'jumping'],
|
|
29
|
+
emotions: ['neutral', 'happy', 'angry', 'sad', 'surprised', 'relaxed']
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
props: [
|
|
34
|
+
{ id: 'spear', description: 'Stone-tipped wooden spear' },
|
|
35
|
+
{ id: 'phone', description: 'Modern smartphone with glowing screen' },
|
|
36
|
+
{ id: 'fire', description: 'Campfire with orange flames' },
|
|
37
|
+
{ id: 'wheel', description: 'Simple wooden wheel' },
|
|
38
|
+
{ id: 'car', description: 'Modern car (side view)' },
|
|
39
|
+
{ id: 'meat', description: 'Raw meat on bone' },
|
|
40
|
+
{ id: 'burger', description: 'Fast food hamburger' },
|
|
41
|
+
{ id: 'cave_painting', description: 'Primitive cave wall art' },
|
|
42
|
+
{ id: 'laptop', description: 'Modern laptop computer' },
|
|
43
|
+
{ id: 'club', description: 'Wooden club weapon' },
|
|
44
|
+
{ id: 'credit_card', description: 'Plastic credit card' },
|
|
45
|
+
{ id: 'mammoth', description: 'Woolly mammoth (cartoon)' },
|
|
46
|
+
{ id: 'clock', description: 'Modern analog clock' },
|
|
47
|
+
{ id: 'sun', description: 'Simple sun icon' },
|
|
48
|
+
{ id: 'moon', description: 'Crescent moon icon' }
|
|
49
|
+
],
|
|
50
|
+
|
|
51
|
+
backgrounds: [
|
|
52
|
+
{ id: 'forest', description: 'Dense prehistoric forest with tall trees' },
|
|
53
|
+
{ id: 'cave', description: 'Dark cave entrance with rocky walls' },
|
|
54
|
+
{ id: 'city', description: 'Modern city skyline with tall buildings' },
|
|
55
|
+
{ id: 'savanna', description: 'African savanna with grass plains' },
|
|
56
|
+
{ id: 'office', description: 'Modern office cubicle workspace' },
|
|
57
|
+
{ id: 'mountain', description: 'Rocky mountain landscape' },
|
|
58
|
+
{ id: 'beach', description: 'Sandy beach with ocean waves' },
|
|
59
|
+
{ id: 'night', description: 'Night sky with stars' },
|
|
60
|
+
{ id: 'desert', description: 'Sandy desert with dunes' },
|
|
61
|
+
{ id: 'village', description: 'Primitive tribal village with huts' }
|
|
62
|
+
],
|
|
63
|
+
|
|
64
|
+
effects: [
|
|
65
|
+
{ id: 'arrow', description: 'Curved arrow pointer (SVG)' },
|
|
66
|
+
{ id: 'question', description: 'Question mark icon (SVG)' },
|
|
67
|
+
{ id: 'exclamation', description: 'Exclamation mark icon (SVG)' },
|
|
68
|
+
{ id: 'vs', description: 'VS text (versus symbol, SVG)' },
|
|
69
|
+
{ id: 'sparkle', description: 'Sparkle/star effect (SVG)' },
|
|
70
|
+
{ id: 'lightning', description: 'Lightning bolt (SVG)' },
|
|
71
|
+
{ id: 'heart', description: 'Heart icon (SVG)' },
|
|
72
|
+
{ id: 'skull', description: 'Skull danger icon (SVG)' },
|
|
73
|
+
{ id: 'checkmark', description: 'Checkmark icon (SVG)' },
|
|
74
|
+
{ id: 'x_mark', description: 'X mark / cross (SVG)' }
|
|
75
|
+
]
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
async function generateAssets(dryRun = false) {
|
|
79
|
+
console.log('🎨 Generating Initial Asset Library\n');
|
|
80
|
+
console.log('='.repeat(60));
|
|
81
|
+
|
|
82
|
+
const library = new AssetLibrary();
|
|
83
|
+
await library.initialize();
|
|
84
|
+
|
|
85
|
+
let totalGenerated = 0;
|
|
86
|
+
let totalSkipped = 0;
|
|
87
|
+
|
|
88
|
+
// Generate character identities
|
|
89
|
+
console.log('\n👤 CHARACTER IDENTITIES');
|
|
90
|
+
console.log('-'.repeat(60));
|
|
91
|
+
|
|
92
|
+
for (const char of ASSET_DEFINITIONS.characters) {
|
|
93
|
+
const query = {
|
|
94
|
+
asset_type: 'character_identity',
|
|
95
|
+
character_id: char.id
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const existing = await library.findAsset(query);
|
|
99
|
+
|
|
100
|
+
if (existing) {
|
|
101
|
+
console.log(`⏭️ Skipping ${char.id} (already exists)`);
|
|
102
|
+
totalSkipped++;
|
|
103
|
+
} else {
|
|
104
|
+
console.log(`🎨 Generating ${char.id}...`);
|
|
105
|
+
|
|
106
|
+
if (!dryRun) {
|
|
107
|
+
try {
|
|
108
|
+
await library.generateAsset({
|
|
109
|
+
asset_type: 'character_identity',
|
|
110
|
+
character_id: char.id,
|
|
111
|
+
visual_description: char.description
|
|
112
|
+
});
|
|
113
|
+
totalGenerated++;
|
|
114
|
+
} catch (error) {
|
|
115
|
+
console.error(` ❌ Failed: ${error.message}`);
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
console.log(` [DRY RUN] Would generate: ${char.description}`);
|
|
119
|
+
totalGenerated++;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Generate poses
|
|
125
|
+
console.log('\n🏃 POSES');
|
|
126
|
+
console.log('-'.repeat(60));
|
|
127
|
+
|
|
128
|
+
for (const char of ASSET_DEFINITIONS.characters) {
|
|
129
|
+
for (const pose of char.poses) {
|
|
130
|
+
const query = {
|
|
131
|
+
asset_type: 'pose',
|
|
132
|
+
character_id: char.id,
|
|
133
|
+
pose: pose
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const existing = await library.findAsset(query);
|
|
137
|
+
|
|
138
|
+
if (existing) {
|
|
139
|
+
console.log(`⏭️ Skipping ${char.id}/${pose} (already exists)`);
|
|
140
|
+
totalSkipped++;
|
|
141
|
+
} else {
|
|
142
|
+
console.log(`🎨 Generating ${char.id}/${pose}...`);
|
|
143
|
+
|
|
144
|
+
if (!dryRun) {
|
|
145
|
+
try {
|
|
146
|
+
await library.generateAsset({
|
|
147
|
+
asset_type: 'pose',
|
|
148
|
+
character_id: char.id,
|
|
149
|
+
pose: pose,
|
|
150
|
+
visual_description: `${char.description}, ${pose} pose`
|
|
151
|
+
});
|
|
152
|
+
totalGenerated++;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
console.error(` ❌ Failed: ${error.message}`);
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
console.log(` [DRY RUN] Would generate: ${char.description}, ${pose} pose`);
|
|
158
|
+
totalGenerated++;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Generate emotions
|
|
165
|
+
console.log('\n😀 EMOTIONS');
|
|
166
|
+
console.log('-'.repeat(60));
|
|
167
|
+
|
|
168
|
+
for (const char of ASSET_DEFINITIONS.characters) {
|
|
169
|
+
for (const emotion of char.emotions) {
|
|
170
|
+
const query = {
|
|
171
|
+
asset_type: 'emotion',
|
|
172
|
+
character_id: char.id,
|
|
173
|
+
emotion: emotion
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const existing = await library.findAsset(query);
|
|
177
|
+
|
|
178
|
+
if (existing) {
|
|
179
|
+
console.log(`⏭️ Skipping ${char.id}/${emotion} (already exists)`);
|
|
180
|
+
totalSkipped++;
|
|
181
|
+
} else {
|
|
182
|
+
console.log(`🎨 Generating ${char.id}/${emotion}...`);
|
|
183
|
+
|
|
184
|
+
if (!dryRun) {
|
|
185
|
+
try {
|
|
186
|
+
await library.generateAsset({
|
|
187
|
+
asset_type: 'emotion',
|
|
188
|
+
character_id: char.id,
|
|
189
|
+
emotion: emotion,
|
|
190
|
+
visual_description: `Face with ${emotion} expression, close-up`
|
|
191
|
+
});
|
|
192
|
+
totalGenerated++;
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(` ❌ Failed: ${error.message}`);
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
console.log(` [DRY RUN] Would generate: Face with ${emotion} expression`);
|
|
198
|
+
totalGenerated++;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Generate props
|
|
205
|
+
console.log('\n🔧 PROPS');
|
|
206
|
+
console.log('-'.repeat(60));
|
|
207
|
+
|
|
208
|
+
for (const prop of ASSET_DEFINITIONS.props) {
|
|
209
|
+
const query = {
|
|
210
|
+
asset_type: 'prop',
|
|
211
|
+
character_id: prop.id
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const existing = await library.findAsset(query);
|
|
215
|
+
|
|
216
|
+
if (existing) {
|
|
217
|
+
console.log(`⏭️ Skipping ${prop.id} (already exists)`);
|
|
218
|
+
totalSkipped++;
|
|
219
|
+
} else {
|
|
220
|
+
console.log(`🎨 Generating ${prop.id}...`);
|
|
221
|
+
|
|
222
|
+
if (!dryRun) {
|
|
223
|
+
try {
|
|
224
|
+
await library.generateAsset({
|
|
225
|
+
asset_type: 'prop',
|
|
226
|
+
character_id: prop.id,
|
|
227
|
+
visual_description: prop.description
|
|
228
|
+
});
|
|
229
|
+
totalGenerated++;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
console.error(` ❌ Failed: ${error.message}`);
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
console.log(` [DRY RUN] Would generate: ${prop.description}`);
|
|
235
|
+
totalGenerated++;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Generate backgrounds
|
|
241
|
+
console.log('\n🏞️ BACKGROUNDS');
|
|
242
|
+
console.log('-'.repeat(60));
|
|
243
|
+
|
|
244
|
+
for (const bg of ASSET_DEFINITIONS.backgrounds) {
|
|
245
|
+
const query = {
|
|
246
|
+
asset_type: 'background',
|
|
247
|
+
character_id: bg.id
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const existing = await library.findAsset(query);
|
|
251
|
+
|
|
252
|
+
if (existing) {
|
|
253
|
+
console.log(`⏭️ Skipping ${bg.id} (already exists)`);
|
|
254
|
+
totalSkipped++;
|
|
255
|
+
} else {
|
|
256
|
+
console.log(`🎨 Generating ${bg.id}...`);
|
|
257
|
+
|
|
258
|
+
if (!dryRun) {
|
|
259
|
+
try {
|
|
260
|
+
await library.generateAsset({
|
|
261
|
+
asset_type: 'background',
|
|
262
|
+
character_id: bg.id,
|
|
263
|
+
visual_description: bg.description
|
|
264
|
+
});
|
|
265
|
+
totalGenerated++;
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.error(` ❌ Failed: ${error.message}`);
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
console.log(` [DRY RUN] Would generate: ${bg.description}`);
|
|
271
|
+
totalGenerated++;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Generate effects
|
|
277
|
+
console.log('\n✨ EFFECTS');
|
|
278
|
+
console.log('-'.repeat(60));
|
|
279
|
+
|
|
280
|
+
for (const effect of ASSET_DEFINITIONS.effects) {
|
|
281
|
+
const query = {
|
|
282
|
+
asset_type: 'effect',
|
|
283
|
+
character_id: effect.id
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
const existing = await library.findAsset(query);
|
|
287
|
+
|
|
288
|
+
if (existing) {
|
|
289
|
+
console.log(`⏭️ Skipping ${effect.id} (already exists)`);
|
|
290
|
+
totalSkipped++;
|
|
291
|
+
} else {
|
|
292
|
+
console.log(`🎨 Generating ${effect.id}...`);
|
|
293
|
+
|
|
294
|
+
if (!dryRun) {
|
|
295
|
+
try {
|
|
296
|
+
await library.generateAsset({
|
|
297
|
+
asset_type: 'effect',
|
|
298
|
+
character_id: effect.id,
|
|
299
|
+
visual_description: effect.description
|
|
300
|
+
});
|
|
301
|
+
totalGenerated++;
|
|
302
|
+
} catch (error) {
|
|
303
|
+
console.error(` ❌ Failed: ${error.message}`);
|
|
304
|
+
}
|
|
305
|
+
} else {
|
|
306
|
+
console.log(` [DRY RUN] Would generate: ${effect.description}`);
|
|
307
|
+
totalGenerated++;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Summary
|
|
313
|
+
console.log('\n' + '='.repeat(60));
|
|
314
|
+
console.log('📊 GENERATION SUMMARY');
|
|
315
|
+
console.log('='.repeat(60));
|
|
316
|
+
console.log(`Assets generated: ${totalGenerated}`);
|
|
317
|
+
console.log(`Assets skipped: ${totalSkipped}`);
|
|
318
|
+
console.log(`Total assets: ${totalGenerated + totalSkipped}`);
|
|
319
|
+
|
|
320
|
+
if (dryRun) {
|
|
321
|
+
console.log('\n⚠️ DRY RUN MODE - No assets were actually generated');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log('\n✅ Asset generation complete!\n');
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// Parse command line arguments
|
|
328
|
+
const args = process.argv.slice(2);
|
|
329
|
+
const dryRun = args.includes('--dry-run');
|
|
330
|
+
|
|
331
|
+
if (dryRun) {
|
|
332
|
+
console.log('🔍 Running in DRY RUN mode\n');
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Run generation
|
|
336
|
+
generateAssets(dryRun)
|
|
337
|
+
.then(() => process.exit(0))
|
|
338
|
+
.catch(error => {
|
|
339
|
+
console.error('\n❌ Generation failed:', error.message);
|
|
340
|
+
console.error(error.stack);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
});
|