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.
- package/README.md +485 -3
- package/bin/baz.js +6 -1
- package/dist/commands/auth.d.ts +2 -0
- package/dist/commands/auth.js +109 -0
- package/dist/commands/capabilities.d.ts +2 -0
- package/dist/commands/capabilities.js +44 -0
- package/dist/commands/context.d.ts +13 -0
- package/dist/commands/context.js +498 -0
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.js +360 -0
- package/dist/commands/logs.d.ts +2 -0
- package/dist/commands/logs.js +180 -0
- package/dist/commands/loop.d.ts +2 -0
- package/dist/commands/loop.js +538 -0
- package/dist/commands/mcp.d.ts +2 -0
- package/dist/commands/mcp.js +143 -0
- package/dist/commands/media.d.ts +2 -0
- package/dist/commands/media.js +362 -0
- package/dist/commands/project.d.ts +2 -0
- package/dist/commands/project.js +786 -0
- package/dist/commands/prompt.d.ts +2 -0
- package/dist/commands/prompt.js +540 -0
- package/dist/commands/recipe.d.ts +15 -0
- package/dist/commands/recipe.js +607 -0
- package/dist/commands/review.d.ts +17 -0
- package/dist/commands/review.js +345 -0
- package/dist/commands/scenes.d.ts +2 -0
- package/dist/commands/scenes.js +481 -0
- package/dist/commands/share.d.ts +2 -0
- package/dist/commands/share.js +226 -0
- package/dist/commands/state.d.ts +2 -0
- package/dist/commands/state.js +171 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +219 -0
- package/dist/commands/template.d.ts +2 -0
- package/dist/commands/template.js +123 -0
- package/dist/commands/verify.d.ts +2 -0
- package/dist/commands/verify.js +150 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +124 -0
- package/dist/lib/api.d.ts +188 -0
- package/dist/lib/api.js +719 -0
- package/dist/lib/banner.d.ts +12 -0
- package/dist/lib/banner.js +69 -0
- package/dist/lib/config.d.ts +33 -0
- package/dist/lib/config.js +99 -0
- package/dist/lib/output.d.ts +52 -0
- package/dist/lib/output.js +162 -0
- package/dist/lib/project-state.d.ts +52 -0
- package/dist/lib/project-state.js +178 -0
- package/dist/lib/sse.d.ts +168 -0
- package/dist/lib/sse.js +227 -0
- package/dist/lib/version.d.ts +1 -0
- package/dist/lib/version.js +3 -0
- package/dist/repl.d.ts +4 -0
- package/dist/repl.js +764 -0
- 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
|
+
});
|