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,345 @@
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
+ import chalk from 'chalk';
18
+ import ora from 'ora';
19
+ import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
20
+ import { apiRequest, ApiError } from '../lib/api.js';
21
+ import { error, output } from '../lib/output.js';
22
+ const FPS = 30;
23
+ export const reviewCommand = new Command('review')
24
+ .description('Get full project state for external agent evaluation')
25
+ .option('--include-code', 'Include full TSX code for each scene (default: true)')
26
+ .option('--no-code', 'Exclude TSX code (smaller payload)')
27
+ .option('--summary', 'Compact summary (no TSX code or full context)')
28
+ .action(async (options, cmd) => {
29
+ const globalOpts = cmd.optsWithGlobals();
30
+ const summary = options.summary === true;
31
+ const includeCode = summary ? false : options.code !== false; // Default true unless --no-code
32
+ const config = loadConfig({
33
+ configPath: globalOpts.config,
34
+ apiUrl: globalOpts.apiUrl,
35
+ projectId: globalOpts.projectId,
36
+ });
37
+ if (!hasAuth(config)) {
38
+ if (globalOpts.json) {
39
+ output({
40
+ type: 'error',
41
+ code: 'AUTH_MISSING',
42
+ message: 'Not authenticated',
43
+ category: 'auth',
44
+ retryable: false,
45
+ transient: false,
46
+ exitCode: 13,
47
+ suggestion: 'Run: baz auth login <api-key>',
48
+ }, { json: true, compact: globalOpts.compact });
49
+ }
50
+ else {
51
+ error('Not authenticated', 'Run: baz auth login <api-key>');
52
+ }
53
+ process.exit(13);
54
+ }
55
+ let projectId;
56
+ try {
57
+ projectId = getProjectId(config, globalOpts.projectId);
58
+ }
59
+ catch (err) {
60
+ if (globalOpts.json) {
61
+ output({
62
+ type: 'error',
63
+ code: 'VALIDATION',
64
+ message: err.message,
65
+ category: 'validation',
66
+ retryable: false,
67
+ transient: false,
68
+ exitCode: 64,
69
+ }, { json: true, compact: globalOpts.compact });
70
+ }
71
+ else {
72
+ error(err.message);
73
+ }
74
+ process.exit(64);
75
+ }
76
+ const spinner = globalOpts.json ? null : ora('Gathering project state...').start();
77
+ try {
78
+ // Fetch full project with scenes
79
+ const project = await apiRequest(config, 'project.getFullProject', {
80
+ id: projectId,
81
+ include: ['scenes'],
82
+ includeSceneCode: includeCode,
83
+ });
84
+ // Fetch recipe (contains original plan, prompts, voiceovers)
85
+ const recipeResult = await apiRequest(config, 'recipe.getByProject', {
86
+ projectId,
87
+ });
88
+ // Fetch generated assets (includes AI prompts)
89
+ let generatedAssets = [];
90
+ try {
91
+ generatedAssets = await apiRequest(config, 'recipe.getGeneratedAssets', {
92
+ projectId,
93
+ });
94
+ }
95
+ catch {
96
+ // Assets may not exist yet
97
+ }
98
+ // Fetch project context (goals, requirements, references)
99
+ let projectContextData = {};
100
+ try {
101
+ const contextResult = await apiRequest(config, 'context.getForAI', {
102
+ projectId,
103
+ });
104
+ projectContextData = contextResult.context;
105
+ }
106
+ catch {
107
+ // Context may not exist yet
108
+ }
109
+ spinner?.stop();
110
+ const scenes = project.scenes || [];
111
+ const recipe = recipeResult?.recipe;
112
+ // Helper to get start frame (may be in props.start or start)
113
+ const getStartFrame = (s) => {
114
+ return s.start ?? s.props?.start ?? 0;
115
+ };
116
+ // Helper to detect scene type from code patterns
117
+ const detectSceneType = (scene, recipeScene) => {
118
+ if (scene.type)
119
+ return scene.type;
120
+ if (scene.props?.type)
121
+ return scene.props.type;
122
+ if (recipeScene?.type)
123
+ return recipeScene.type;
124
+ // Try to detect from TSX code patterns
125
+ const code = scene.tsxCode || '';
126
+ if (code.includes('background') || code.includes('gradient'))
127
+ return 'background';
128
+ if (code.includes('IconifyIcon') || code.includes('button'))
129
+ return 'ui';
130
+ if (code.includes('text') || code.includes('Typography'))
131
+ return 'text';
132
+ return 'motion_graphics';
133
+ };
134
+ // Calculate timing info
135
+ const totalFrames = scenes.reduce((max, s) => {
136
+ const start = getStartFrame(s);
137
+ return Math.max(max, start + (s.duration || 0));
138
+ }, 0);
139
+ const totalDuration = totalFrames / FPS;
140
+ // Build scene data with code and linked prompts
141
+ const sceneData = scenes.map(scene => {
142
+ // Find the recipe scene that generated this (by sceneId match or name)
143
+ const recipeScene = recipe?.scenes.find(rs => rs.sceneId === scene.id || rs.name === scene.name);
144
+ // Find linked voiceover
145
+ const linkedVoiceover = recipeScene?.voiceoverRef
146
+ ? recipe?.voiceovers.find(v => v.id === recipeScene.voiceoverRef)
147
+ : undefined;
148
+ const startFrame = getStartFrame(scene);
149
+ const durationFrames = scene.duration || 0;
150
+ const base = {
151
+ id: scene.id,
152
+ name: scene.name,
153
+ type: detectSceneType(scene, recipeScene),
154
+ track: scene.track ?? 0,
155
+ start: startFrame / FPS, // Convert to seconds
156
+ duration: durationFrames / FPS, // Convert to seconds
157
+ order: scene.order,
158
+ };
159
+ if (summary) {
160
+ return base;
161
+ }
162
+ return {
163
+ ...base,
164
+ // The actual code - this is what's really being rendered
165
+ ...(includeCode && scene.tsxCode && { tsxCode: scene.tsxCode }),
166
+ // The original prompt that was used to generate this scene
167
+ ...(recipeScene?.prompt && { originalPrompt: recipeScene.prompt }),
168
+ // Linked voiceover text (for sync evaluation)
169
+ ...(linkedVoiceover && {
170
+ linkedVoiceover: {
171
+ id: linkedVoiceover.id,
172
+ text: linkedVoiceover.text,
173
+ duration: linkedVoiceover.duration,
174
+ }
175
+ }),
176
+ };
177
+ });
178
+ const contextSummary = summary
179
+ ? Object.fromEntries(Object.entries(projectContextData).map(([label, items]) => {
180
+ const typeCounts = {};
181
+ for (const item of items) {
182
+ const key = item.contentType || 'text';
183
+ typeCounts[key] = (typeCounts[key] || 0) + 1;
184
+ }
185
+ return [label, { count: items.length, types: typeCounts }];
186
+ }))
187
+ : projectContextData;
188
+ const generatedAssetsSummary = summary
189
+ ? generatedAssets.reduce((acc, asset) => {
190
+ acc[asset.type] = (acc[asset.type] || 0) + 1;
191
+ return acc;
192
+ }, {})
193
+ : undefined;
194
+ // Build the full state dump
195
+ const reviewData = {
196
+ type: summary ? 'review_summary' : 'review_state',
197
+ projectId,
198
+ projectName: project.name,
199
+ // Timing overview
200
+ timing: {
201
+ totalDuration,
202
+ sceneCount: scenes.length,
203
+ fps: FPS,
204
+ },
205
+ // The original plan (if recipe exists)
206
+ ...(recipe && {
207
+ originalPlan: summary
208
+ ? {
209
+ title: recipe.title,
210
+ sceneCount: recipe.scenes.length,
211
+ voiceoverCount: recipe.voiceovers.length,
212
+ hasMusic: Boolean(recipe.music),
213
+ }
214
+ : {
215
+ title: recipe.title,
216
+ style: recipe.style,
217
+ // Music prompt if any
218
+ ...(recipe.music && { musicPrompt: recipe.music.prompt }),
219
+ },
220
+ }),
221
+ // Original URL if this was URL-to-video
222
+ ...(recipeResult?.sourceUrl && { sourceUrl: recipeResult.sourceUrl }),
223
+ // Voiceovers (the script)
224
+ voiceovers: recipe?.voiceovers.map(v => summary ? ({
225
+ id: v.id,
226
+ duration: v.duration,
227
+ hasAudio: Boolean(v.audioUrl),
228
+ textPreview: v.text.slice(0, 80),
229
+ }) : ({
230
+ id: v.id,
231
+ text: v.text,
232
+ voiceId: v.voiceId,
233
+ duration: v.duration,
234
+ hasAudio: Boolean(v.audioUrl),
235
+ })) || [],
236
+ // All scenes with code and prompts
237
+ scenes: sceneData,
238
+ // AI-generated assets with their prompts
239
+ ...(summary
240
+ ? { generatedAssetsSummary }
241
+ : {
242
+ generatedAssets: generatedAssets.map(a => ({
243
+ assetId: a.assetId,
244
+ type: a.type,
245
+ name: a.name,
246
+ duration: a.duration,
247
+ // The prompt sent to Seedance, image gen, etc.
248
+ ...(a.prompt && { aiPrompt: a.prompt }),
249
+ })),
250
+ }),
251
+ // Recipe scene prompts (in case scene linking didn't work)
252
+ ...(!summary && recipe && {
253
+ recipeScenePrompts: recipe.scenes.map(rs => ({
254
+ id: rs.id,
255
+ name: rs.name,
256
+ type: rs.type,
257
+ prompt: rs.prompt,
258
+ track: rs.track,
259
+ })),
260
+ }),
261
+ // Project context (goals, requirements, references)
262
+ // External agent can compare this against the actual output
263
+ ...(Object.keys(contextSummary).length > 0 && { context: contextSummary }),
264
+ // Signal to external agent: evaluate this and decide
265
+ continue: true,
266
+ };
267
+ if (globalOpts.json) {
268
+ output(reviewData, { json: true, compact: globalOpts.compact });
269
+ return;
270
+ }
271
+ // Human-readable output
272
+ console.log();
273
+ console.log(chalk.bold(`📋 Project State for Review`));
274
+ console.log(chalk.gray(`Project: ${project.name} (${projectId.slice(0, 8)}...)`));
275
+ console.log(chalk.gray(`Duration: ${totalDuration.toFixed(1)}s | Scenes: ${scenes.length}`));
276
+ console.log();
277
+ if (recipe) {
278
+ console.log(chalk.bold('Original Plan:'));
279
+ console.log(chalk.gray(` Title: ${recipe.title}`));
280
+ if (!summary && recipe.style?.mood) {
281
+ console.log(chalk.gray(` Mood: ${recipe.style.mood}`));
282
+ }
283
+ console.log();
284
+ }
285
+ if (recipe?.voiceovers && recipe.voiceovers.length > 0) {
286
+ console.log(chalk.bold('Voiceovers:'));
287
+ recipe.voiceovers.forEach((v, i) => {
288
+ const dur = v.duration ? ` (${v.duration.toFixed(1)}s)` : '';
289
+ const preview = summary ? `${v.text.slice(0, 40)}...` : `${v.text.slice(0, 60)}...`;
290
+ console.log(chalk.gray(` ${i + 1}. "${preview}"${dur}`));
291
+ });
292
+ console.log();
293
+ }
294
+ console.log(chalk.bold('Scenes:'));
295
+ sceneData.forEach((s, i) => {
296
+ const timing = `${s.start.toFixed(1)}s - ${(s.start + s.duration).toFixed(1)}s`;
297
+ console.log(` ${i + 1}. ${s.name} (${s.type}, track ${s.track})`);
298
+ console.log(chalk.gray(` ${timing} | ${s.duration.toFixed(1)}s`));
299
+ if (!summary && s.originalPrompt) {
300
+ console.log(chalk.gray(` Prompt: "${s.originalPrompt.slice(0, 50)}..."`));
301
+ }
302
+ if (!summary && includeCode && s.tsxCode) {
303
+ console.log(chalk.gray(` Code: ${s.tsxCode.length} chars`));
304
+ }
305
+ });
306
+ console.log();
307
+ if (!summary && generatedAssets.length > 0) {
308
+ console.log(chalk.bold('AI-Generated Assets:'));
309
+ generatedAssets.forEach(a => {
310
+ console.log(chalk.gray(` • ${a.name} (${a.type})`));
311
+ if (a.prompt) {
312
+ console.log(chalk.gray(` Prompt: "${a.prompt.slice(0, 50)}..."`));
313
+ }
314
+ });
315
+ console.log();
316
+ }
317
+ if (summary && generatedAssets.length > 0) {
318
+ const typeCounts = generatedAssets.reduce((acc, asset) => {
319
+ acc[asset.type] = (acc[asset.type] || 0) + 1;
320
+ return acc;
321
+ }, {});
322
+ console.log(chalk.bold('AI-Generated Assets:'));
323
+ Object.entries(typeCounts).forEach(([type, count]) => {
324
+ console.log(chalk.gray(` • ${type}: ${count}`));
325
+ });
326
+ console.log();
327
+ }
328
+ console.log(chalk.cyan('For JSON output: baz review --json'));
329
+ console.log(chalk.gray('External agent evaluates this state against original goal.'));
330
+ }
331
+ catch (err) {
332
+ spinner?.stop();
333
+ if (err instanceof ApiError) {
334
+ if (globalOpts.json) {
335
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
336
+ }
337
+ else {
338
+ error(err.message, err.suggestion);
339
+ }
340
+ process.exit(err.exitCode);
341
+ }
342
+ error(err.message);
343
+ process.exit(1);
344
+ }
345
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const scenesCommand: Command;