bazaar.it 0.1.0 → 0.2.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.
Files changed (57) hide show
  1. package/README.md +485 -3
  2. package/bin/baz.js +6 -1
  3. package/dist/commands/auth.d.ts +2 -0
  4. package/dist/commands/auth.js +109 -0
  5. package/dist/commands/capabilities.d.ts +2 -0
  6. package/dist/commands/capabilities.js +44 -0
  7. package/dist/commands/context.d.ts +13 -0
  8. package/dist/commands/context.js +498 -0
  9. package/dist/commands/export.d.ts +2 -0
  10. package/dist/commands/export.js +360 -0
  11. package/dist/commands/logs.d.ts +2 -0
  12. package/dist/commands/logs.js +180 -0
  13. package/dist/commands/loop.d.ts +2 -0
  14. package/dist/commands/loop.js +538 -0
  15. package/dist/commands/mcp.d.ts +2 -0
  16. package/dist/commands/mcp.js +143 -0
  17. package/dist/commands/media.d.ts +2 -0
  18. package/dist/commands/media.js +362 -0
  19. package/dist/commands/project.d.ts +2 -0
  20. package/dist/commands/project.js +786 -0
  21. package/dist/commands/prompt.d.ts +2 -0
  22. package/dist/commands/prompt.js +540 -0
  23. package/dist/commands/recipe.d.ts +15 -0
  24. package/dist/commands/recipe.js +607 -0
  25. package/dist/commands/review.d.ts +17 -0
  26. package/dist/commands/review.js +345 -0
  27. package/dist/commands/scenes.d.ts +2 -0
  28. package/dist/commands/scenes.js +481 -0
  29. package/dist/commands/share.d.ts +2 -0
  30. package/dist/commands/share.js +226 -0
  31. package/dist/commands/state.d.ts +2 -0
  32. package/dist/commands/state.js +171 -0
  33. package/dist/commands/status.d.ts +2 -0
  34. package/dist/commands/status.js +219 -0
  35. package/dist/commands/template.d.ts +2 -0
  36. package/dist/commands/template.js +123 -0
  37. package/dist/commands/verify.d.ts +2 -0
  38. package/dist/commands/verify.js +150 -0
  39. package/dist/index.d.ts +2 -0
  40. package/dist/index.js +124 -0
  41. package/dist/lib/api.d.ts +188 -0
  42. package/dist/lib/api.js +719 -0
  43. package/dist/lib/banner.d.ts +12 -0
  44. package/dist/lib/banner.js +69 -0
  45. package/dist/lib/config.d.ts +33 -0
  46. package/dist/lib/config.js +99 -0
  47. package/dist/lib/output.d.ts +52 -0
  48. package/dist/lib/output.js +162 -0
  49. package/dist/lib/project-state.d.ts +52 -0
  50. package/dist/lib/project-state.js +178 -0
  51. package/dist/lib/sse.d.ts +168 -0
  52. package/dist/lib/sse.js +227 -0
  53. package/dist/lib/version.d.ts +1 -0
  54. package/dist/lib/version.js +3 -0
  55. package/dist/repl.d.ts +4 -0
  56. package/dist/repl.js +764 -0
  57. package/package.json +32 -5
@@ -0,0 +1,607 @@
1
+ /**
2
+ * Recipe Commands - Agent Handshake Protocol
3
+ *
4
+ * Implements the REVIEW → APPROVE/EDIT → EXECUTE flow for external agents.
5
+ * See: memory-bank/sprints/sprint501_baz_cli/HANDSHAKE-PROTOCOL.md
6
+ *
7
+ * Commands:
8
+ * - baz recipe show → Review the current plan
9
+ * - baz recipe execute → Approve and run
10
+ * - baz recipe edit → Modify before executing
11
+ * - baz recipe clear → Reject/abandon
12
+ * - baz recipe assets → List generated assets
13
+ */
14
+ import { Command } from 'commander';
15
+ import chalk from 'chalk';
16
+ import ora from 'ora';
17
+ import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
18
+ import { apiRequest, ApiError } from '../lib/api.js';
19
+ import { success, error, output, outputNDJSON, table } from '../lib/output.js';
20
+ function exitRecipeAuthError(globalOpts) {
21
+ if (globalOpts.json) {
22
+ output({
23
+ type: 'error',
24
+ code: 'AUTH_MISSING',
25
+ message: 'Not authenticated',
26
+ category: 'auth',
27
+ retryable: false,
28
+ transient: false,
29
+ exitCode: 13,
30
+ suggestion: 'Run: baz auth login <api-key>',
31
+ continue: false,
32
+ }, { json: true, compact: globalOpts.compact });
33
+ }
34
+ else {
35
+ error('Not authenticated', 'Run: baz auth login <api-key>');
36
+ }
37
+ process.exit(13);
38
+ }
39
+ function exitRecipeProjectValidationError(message, globalOpts) {
40
+ if (globalOpts.json) {
41
+ output({
42
+ type: 'error',
43
+ code: 'VALIDATION',
44
+ message,
45
+ category: 'validation',
46
+ retryable: false,
47
+ transient: false,
48
+ exitCode: 64,
49
+ continue: false,
50
+ }, { json: true, compact: globalOpts.compact });
51
+ }
52
+ else {
53
+ error(message);
54
+ }
55
+ process.exit(64);
56
+ }
57
+ export const recipeCommand = new Command('recipe')
58
+ .description('Manage video recipes (agent handshake protocol)');
59
+ /**
60
+ * baz recipe show
61
+ * Phase 2: REVIEW - External agent inspects the plan
62
+ */
63
+ recipeCommand
64
+ .command('show')
65
+ .description('Show current recipe for review')
66
+ .action(async (options, cmd) => {
67
+ const globalOpts = cmd.optsWithGlobals();
68
+ const config = loadConfig({
69
+ configPath: globalOpts.config,
70
+ apiUrl: globalOpts.apiUrl,
71
+ projectId: globalOpts.projectId,
72
+ });
73
+ if (!hasAuth(config)) {
74
+ exitRecipeAuthError(globalOpts);
75
+ }
76
+ let projectId;
77
+ try {
78
+ projectId = getProjectId(config, globalOpts.projectId);
79
+ }
80
+ catch (err) {
81
+ exitRecipeProjectValidationError(err.message, globalOpts);
82
+ }
83
+ const spinner = globalOpts.json ? null : ora('Fetching recipe...').start();
84
+ try {
85
+ const result = await apiRequest(config, 'recipe.getByProject', {
86
+ projectId,
87
+ });
88
+ spinner?.stop();
89
+ if (!result || !result.recipe) {
90
+ if (globalOpts.json) {
91
+ output({
92
+ type: 'no_recipe',
93
+ message: 'No recipe found for this project',
94
+ projectId,
95
+ continue: false,
96
+ }, { json: true, compact: globalOpts.compact });
97
+ }
98
+ else {
99
+ console.log(chalk.gray('No recipe found for this project.'));
100
+ console.log(chalk.gray('Create one with: baz prompt "..." --plan'));
101
+ }
102
+ return;
103
+ }
104
+ if (globalOpts.json) {
105
+ output({
106
+ type: 'recipe',
107
+ executionId: result.executionId,
108
+ status: result.status,
109
+ recipe: result.recipe,
110
+ videoPlan: result.videoPlan,
111
+ sourceUrl: result.sourceUrl,
112
+ createdAt: result.createdAt,
113
+ continue: true, // Agent should decide: approve, edit, or reject
114
+ }, { json: true, compact: globalOpts.compact });
115
+ return;
116
+ }
117
+ // Human-readable output
118
+ const recipe = result.recipe;
119
+ console.log(chalk.bold(`\n📋 Recipe: ${recipe.title}`));
120
+ console.log(chalk.gray(`Execution ID: ${result.executionId}`));
121
+ console.log(chalk.gray(`Status: ${result.status}`));
122
+ console.log();
123
+ if (recipe.voiceovers && recipe.voiceovers.length > 0) {
124
+ console.log(chalk.bold('🎙️ Voiceovers:'));
125
+ recipe.voiceovers.forEach((vo, i) => {
126
+ const duration = vo.duration ? ` (${vo.duration.toFixed(1)}s)` : '';
127
+ console.log(` ${i + 1}. [${vo.id.slice(0, 8)}]${duration}`);
128
+ console.log(chalk.gray(` "${vo.text.slice(0, 80)}${vo.text.length > 80 ? '...' : ''}"`));
129
+ });
130
+ console.log();
131
+ }
132
+ if (recipe.scenes && recipe.scenes.length > 0) {
133
+ console.log(chalk.bold('🎬 Scenes:'));
134
+ recipe.scenes.forEach((scene, i) => {
135
+ const trackLabel = `Track ${scene.track ?? 1}`;
136
+ console.log(` ${i + 1}. ${scene.name} (${scene.type}, ${trackLabel})`);
137
+ console.log(chalk.gray(` [${scene.id.slice(0, 8)}] "${scene.prompt.slice(0, 60)}..."`));
138
+ });
139
+ console.log();
140
+ }
141
+ if (recipe.music) {
142
+ console.log(chalk.bold('🎵 Music:'));
143
+ console.log(chalk.gray(` "${recipe.music.prompt}"`));
144
+ console.log();
145
+ }
146
+ console.log(chalk.cyan('Next steps:'));
147
+ console.log(chalk.gray(' baz recipe execute # Approve and run'));
148
+ console.log(chalk.gray(' baz recipe edit ... # Modify first'));
149
+ console.log(chalk.gray(' baz recipe clear # Reject/abandon'));
150
+ }
151
+ catch (err) {
152
+ spinner?.stop();
153
+ if (err instanceof ApiError) {
154
+ if (globalOpts.json) {
155
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
156
+ }
157
+ else {
158
+ error(err.message, err.suggestion);
159
+ }
160
+ process.exit(err.exitCode);
161
+ }
162
+ error(err.message);
163
+ process.exit(1);
164
+ }
165
+ });
166
+ /**
167
+ * baz recipe execute
168
+ * Phase 3A: APPROVE - Execute the approved plan
169
+ */
170
+ recipeCommand
171
+ .command('execute')
172
+ .description('Execute the current recipe (approve and run)')
173
+ .option('--stream-json', 'Stream progress events as NDJSON')
174
+ .action(async (options, cmd) => {
175
+ const globalOpts = cmd.optsWithGlobals();
176
+ const streamJson = options.streamJson || globalOpts.json;
177
+ const config = loadConfig({
178
+ configPath: globalOpts.config,
179
+ apiUrl: globalOpts.apiUrl,
180
+ projectId: globalOpts.projectId,
181
+ });
182
+ if (!hasAuth(config)) {
183
+ if (streamJson) {
184
+ outputNDJSON({
185
+ type: 'error',
186
+ code: 'AUTH_MISSING',
187
+ message: 'Not authenticated',
188
+ category: 'auth',
189
+ retryable: false,
190
+ continue: false,
191
+ });
192
+ }
193
+ else {
194
+ error('Not authenticated', 'Run: baz auth login <api-key>');
195
+ }
196
+ if (streamJson) {
197
+ process.exit(13);
198
+ }
199
+ exitRecipeAuthError(globalOpts);
200
+ }
201
+ let projectId;
202
+ try {
203
+ projectId = getProjectId(config, globalOpts.projectId);
204
+ }
205
+ catch (err) {
206
+ if (streamJson) {
207
+ outputNDJSON({
208
+ type: 'error',
209
+ code: 'NOT_FOUND',
210
+ message: err.message,
211
+ category: 'validation',
212
+ retryable: false,
213
+ continue: false,
214
+ });
215
+ }
216
+ else {
217
+ error(err.message);
218
+ }
219
+ if (streamJson) {
220
+ process.exit(64);
221
+ }
222
+ exitRecipeProjectValidationError(err.message, globalOpts);
223
+ }
224
+ // First, get the current recipe
225
+ if (streamJson) {
226
+ outputNDJSON({
227
+ type: 'execution_started',
228
+ message: 'Fetching recipe for execution...',
229
+ projectId,
230
+ continue: true,
231
+ });
232
+ }
233
+ const spinner = streamJson ? null : ora('Fetching recipe...').start();
234
+ try {
235
+ const recipeResult = await apiRequest(config, 'recipe.getByProject', {
236
+ projectId,
237
+ });
238
+ if (!recipeResult || !recipeResult.recipe) {
239
+ spinner?.stop();
240
+ if (streamJson) {
241
+ outputNDJSON({
242
+ type: 'error',
243
+ code: 'NOT_FOUND',
244
+ message: 'No recipe found. Create one first with: baz prompt "..." --plan',
245
+ category: 'validation',
246
+ retryable: false,
247
+ continue: false,
248
+ });
249
+ }
250
+ else {
251
+ error('No recipe found', 'Create one first with: baz prompt "..." --plan');
252
+ }
253
+ process.exit(64);
254
+ }
255
+ if (spinner)
256
+ spinner.text = 'Executing recipe...';
257
+ if (streamJson) {
258
+ outputNDJSON({
259
+ type: 'execution_running',
260
+ executionId: recipeResult.executionId,
261
+ recipeTitle: recipeResult.recipe.title,
262
+ scenesCount: recipeResult.recipe.scenes?.length || 0,
263
+ voiceoversCount: recipeResult.recipe.voiceovers?.length || 0,
264
+ continue: true,
265
+ });
266
+ }
267
+ // Execute the recipe
268
+ const executeResult = await apiRequest(config, 'recipe.execute', {
269
+ executionId: recipeResult.executionId,
270
+ recipe: recipeResult.recipe,
271
+ });
272
+ spinner?.stop();
273
+ if (!executeResult.success) {
274
+ if (streamJson) {
275
+ outputNDJSON({
276
+ type: 'error',
277
+ code: 'SERVER',
278
+ message: executeResult.error || 'Recipe execution failed',
279
+ category: 'transient',
280
+ retryable: true,
281
+ continue: false,
282
+ });
283
+ }
284
+ else {
285
+ error('Recipe execution failed', executeResult.error);
286
+ }
287
+ process.exit(1);
288
+ }
289
+ // Success!
290
+ if (streamJson) {
291
+ outputNDJSON({
292
+ type: 'execution_complete',
293
+ success: true,
294
+ executionId: recipeResult.executionId,
295
+ totalDuration: executeResult.totalDuration,
296
+ sceneIds: executeResult.sceneIds,
297
+ scenesCreated: executeResult.sceneIds?.length || 0,
298
+ continue: false, // Agent should verify and potentially iterate
299
+ });
300
+ }
301
+ else {
302
+ success(`Recipe executed successfully!`);
303
+ console.log(chalk.gray(` Duration: ${executeResult.totalDuration?.toFixed(1) || '?'}s`));
304
+ console.log(chalk.gray(` Scenes: ${executeResult.sceneIds?.length || 0} created`));
305
+ console.log();
306
+ console.log(chalk.cyan('Next steps:'));
307
+ console.log(chalk.gray(' baz scenes list # Verify result'));
308
+ console.log(chalk.gray(' baz export start --wait # Export video'));
309
+ }
310
+ }
311
+ catch (err) {
312
+ spinner?.stop();
313
+ if (err instanceof ApiError) {
314
+ if (streamJson) {
315
+ outputNDJSON({ ...err.toJSON(), continue: false });
316
+ }
317
+ else {
318
+ error(err.message, err.suggestion);
319
+ }
320
+ process.exit(err.exitCode);
321
+ }
322
+ if (streamJson) {
323
+ outputNDJSON({
324
+ type: 'error',
325
+ code: 'UNKNOWN',
326
+ message: err.message,
327
+ category: 'fatal',
328
+ retryable: false,
329
+ continue: false,
330
+ });
331
+ }
332
+ else {
333
+ error(err.message);
334
+ }
335
+ process.exit(1);
336
+ }
337
+ });
338
+ /**
339
+ * baz recipe edit
340
+ * Phase 3B: EDIT - Modify the plan before executing
341
+ */
342
+ recipeCommand
343
+ .command('edit')
344
+ .description('Edit the current recipe before executing')
345
+ .option('--voiceover <id>', 'Voiceover ID to edit')
346
+ .option('--scene <id>', 'Scene ID to edit')
347
+ .option('--text <text>', 'New voiceover text')
348
+ .option('--prompt <prompt>', 'New scene prompt')
349
+ .option('--name <name>', 'New scene name')
350
+ .option('--add-scene', 'Add a new scene instead of editing')
351
+ .option('--type <type>', 'Scene type for new scene (motion_graphics, image, etc.)')
352
+ .option('--track <n>', 'Track number for new scene', parseInt)
353
+ .option('--remove-voiceover <id>', 'Remove a voiceover by ID')
354
+ .option('--remove-scene <id>', 'Remove a scene by ID')
355
+ .action(async (options, cmd) => {
356
+ const globalOpts = cmd.optsWithGlobals();
357
+ const config = loadConfig({
358
+ configPath: globalOpts.config,
359
+ apiUrl: globalOpts.apiUrl,
360
+ projectId: globalOpts.projectId,
361
+ });
362
+ if (!hasAuth(config)) {
363
+ exitRecipeAuthError(globalOpts);
364
+ }
365
+ let projectId;
366
+ try {
367
+ projectId = getProjectId(config, globalOpts.projectId);
368
+ }
369
+ catch (err) {
370
+ exitRecipeProjectValidationError(err.message, globalOpts);
371
+ }
372
+ const spinner = globalOpts.json ? null : ora('Fetching recipe...').start();
373
+ try {
374
+ // Get current recipe
375
+ const recipeResult = await apiRequest(config, 'recipe.getByProject', {
376
+ projectId,
377
+ });
378
+ if (!recipeResult || !recipeResult.recipe) {
379
+ spinner?.stop();
380
+ error('No recipe found', 'Create one first with: baz prompt "..." --plan');
381
+ process.exit(64);
382
+ }
383
+ const recipe = recipeResult.recipe;
384
+ let modified = false;
385
+ // Edit voiceover
386
+ if (options.voiceover && options.text) {
387
+ const vo = recipe.voiceovers.find((v) => v.id === options.voiceover || v.id.startsWith(options.voiceover));
388
+ if (!vo) {
389
+ spinner?.stop();
390
+ error(`Voiceover not found: ${options.voiceover}`);
391
+ process.exit(64);
392
+ }
393
+ vo.text = options.text;
394
+ modified = true;
395
+ }
396
+ // Edit scene
397
+ if (options.scene) {
398
+ const scene = recipe.scenes.find((s) => s.id === options.scene || s.id.startsWith(options.scene));
399
+ if (!scene) {
400
+ spinner?.stop();
401
+ error(`Scene not found: ${options.scene}`);
402
+ process.exit(64);
403
+ }
404
+ if (options.prompt) {
405
+ scene.prompt = options.prompt;
406
+ modified = true;
407
+ }
408
+ if (options.name) {
409
+ scene.name = options.name;
410
+ modified = true;
411
+ }
412
+ if (options.track !== undefined) {
413
+ scene.track = options.track;
414
+ modified = true;
415
+ }
416
+ }
417
+ // Add new scene
418
+ if (options.addScene) {
419
+ if (!options.name || !options.prompt) {
420
+ spinner?.stop();
421
+ error('--add-scene requires --name and --prompt');
422
+ process.exit(65);
423
+ }
424
+ const newScene = {
425
+ id: crypto.randomUUID(),
426
+ name: options.name,
427
+ type: options.type || 'motion_graphics',
428
+ prompt: options.prompt,
429
+ track: options.track ?? 1,
430
+ };
431
+ recipe.scenes.push(newScene);
432
+ modified = true;
433
+ }
434
+ // Remove voiceover
435
+ if (options.removeVoiceover) {
436
+ const idx = recipe.voiceovers.findIndex((v) => v.id === options.removeVoiceover || v.id.startsWith(options.removeVoiceover));
437
+ if (idx === -1) {
438
+ spinner?.stop();
439
+ error(`Voiceover not found: ${options.removeVoiceover}`);
440
+ process.exit(64);
441
+ }
442
+ recipe.voiceovers.splice(idx, 1);
443
+ modified = true;
444
+ }
445
+ // Remove scene
446
+ if (options.removeScene) {
447
+ const idx = recipe.scenes.findIndex((s) => s.id === options.removeScene || s.id.startsWith(options.removeScene));
448
+ if (idx === -1) {
449
+ spinner?.stop();
450
+ error(`Scene not found: ${options.removeScene}`);
451
+ process.exit(64);
452
+ }
453
+ recipe.scenes.splice(idx, 1);
454
+ modified = true;
455
+ }
456
+ if (!modified) {
457
+ spinner?.stop();
458
+ error('No modifications specified', 'Use --voiceover, --scene, --add-scene, --remove-voiceover, or --remove-scene');
459
+ process.exit(65);
460
+ }
461
+ if (spinner)
462
+ spinner.text = 'Saving changes...';
463
+ // Update the recipe
464
+ await apiRequest(config, 'recipe.update', {
465
+ executionId: recipeResult.executionId,
466
+ recipe,
467
+ });
468
+ spinner?.stop();
469
+ if (globalOpts.json) {
470
+ output({
471
+ type: 'recipe_updated',
472
+ executionId: recipeResult.executionId,
473
+ recipe,
474
+ continue: true, // Agent can continue editing or execute
475
+ }, { json: true, compact: globalOpts.compact });
476
+ }
477
+ else {
478
+ success('Recipe updated');
479
+ console.log(chalk.cyan('Next steps:'));
480
+ console.log(chalk.gray(' baz recipe show # Review changes'));
481
+ console.log(chalk.gray(' baz recipe execute # Run the recipe'));
482
+ }
483
+ }
484
+ catch (err) {
485
+ spinner?.stop();
486
+ if (err instanceof ApiError) {
487
+ if (globalOpts.json) {
488
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
489
+ }
490
+ else {
491
+ error(err.message, err.suggestion);
492
+ }
493
+ process.exit(err.exitCode);
494
+ }
495
+ error(err.message);
496
+ process.exit(1);
497
+ }
498
+ });
499
+ /**
500
+ * baz recipe clear
501
+ * Phase 3C: REJECT - Abandon the current plan
502
+ */
503
+ recipeCommand
504
+ .command('clear')
505
+ .description('Clear/abandon the current recipe')
506
+ .action(async (options, cmd) => {
507
+ const globalOpts = cmd.optsWithGlobals();
508
+ const config = loadConfig({
509
+ configPath: globalOpts.config,
510
+ apiUrl: globalOpts.apiUrl,
511
+ projectId: globalOpts.projectId,
512
+ });
513
+ if (!hasAuth(config)) {
514
+ exitRecipeAuthError(globalOpts);
515
+ }
516
+ let projectId;
517
+ try {
518
+ projectId = getProjectId(config, globalOpts.projectId);
519
+ }
520
+ catch (err) {
521
+ exitRecipeProjectValidationError(err.message, globalOpts);
522
+ }
523
+ // For now, "clear" just means the agent starts fresh with a new prompt
524
+ // We could add a recipe.delete endpoint, but typically agents just create a new recipe
525
+ if (globalOpts.json) {
526
+ output({
527
+ type: 'recipe_cleared',
528
+ message: 'Recipe abandoned. Start fresh with: baz prompt "..." --plan',
529
+ projectId,
530
+ continue: true, // Agent can start over
531
+ }, { json: true, compact: globalOpts.compact });
532
+ }
533
+ else {
534
+ success('Recipe cleared');
535
+ console.log(chalk.gray('Start fresh with: baz prompt "..." --plan'));
536
+ }
537
+ });
538
+ /**
539
+ * baz recipe assets
540
+ * List generated assets from recipe execution
541
+ */
542
+ recipeCommand
543
+ .command('assets')
544
+ .description('List assets generated by recipe execution')
545
+ .action(async (options, cmd) => {
546
+ const globalOpts = cmd.optsWithGlobals();
547
+ const config = loadConfig({
548
+ configPath: globalOpts.config,
549
+ apiUrl: globalOpts.apiUrl,
550
+ projectId: globalOpts.projectId,
551
+ });
552
+ if (!hasAuth(config)) {
553
+ exitRecipeAuthError(globalOpts);
554
+ }
555
+ let projectId;
556
+ try {
557
+ projectId = getProjectId(config, globalOpts.projectId);
558
+ }
559
+ catch (err) {
560
+ exitRecipeProjectValidationError(err.message, globalOpts);
561
+ }
562
+ const spinner = globalOpts.json ? null : ora('Fetching generated assets...').start();
563
+ try {
564
+ const assets = await apiRequest(config, 'recipe.getGeneratedAssets', {
565
+ projectId,
566
+ });
567
+ spinner?.stop();
568
+ if (globalOpts.json) {
569
+ output({
570
+ type: 'assets',
571
+ projectId,
572
+ assets,
573
+ count: assets.length,
574
+ continue: false,
575
+ }, { json: true, compact: globalOpts.compact });
576
+ return;
577
+ }
578
+ if (assets.length === 0) {
579
+ console.log(chalk.gray('No generated assets yet.'));
580
+ console.log(chalk.gray('Execute a recipe first: baz recipe execute'));
581
+ return;
582
+ }
583
+ console.log(chalk.bold(`\n🎨 Generated Assets (${assets.length})`));
584
+ console.log();
585
+ const rows = assets.map((asset) => [
586
+ asset.assetId.slice(0, 12),
587
+ asset.type,
588
+ asset.name,
589
+ asset.duration ? `${asset.duration.toFixed(1)}s` : '-',
590
+ ]);
591
+ table(['ID', 'Type', 'Name', 'Duration'], rows);
592
+ }
593
+ catch (err) {
594
+ spinner?.stop();
595
+ if (err instanceof ApiError) {
596
+ if (globalOpts.json) {
597
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
598
+ }
599
+ else {
600
+ error(err.message, err.suggestion);
601
+ }
602
+ process.exit(err.exitCode);
603
+ }
604
+ error(err.message);
605
+ process.exit(1);
606
+ }
607
+ });
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Review Command - Full State Dump for External Agent Evaluation
3
+ *
4
+ * Returns all the data an external agent needs to evaluate the video:
5
+ * - Original goal and requirements
6
+ * - Each scene with actual TSX code
7
+ * - Voiceovers with text
8
+ * - AI-generated asset prompts (Seedance, etc.)
9
+ * - Timing information
10
+ *
11
+ * The external agent (Maya, Claude, GPT) then makes its own intelligent
12
+ * judgment about whether the video meets the original goal.
13
+ *
14
+ * See: memory-bank/sprints/sprint501_baz_cli/HANDSHAKE-PROTOCOL.md
15
+ */
16
+ import { Command } from 'commander';
17
+ export declare const reviewCommand: Command;