bazaar.it 0.1.0 → 0.2.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.
Files changed (57) hide show
  1. package/README.md +489 -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 +529 -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 +186 -0
  42. package/dist/lib/api.js +717 -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 +39 -5
@@ -0,0 +1,498 @@
1
+ /**
2
+ * Context Commands - Project context for agent handshake
3
+ *
4
+ * Store and retrieve flexible context for projects:
5
+ * - Goals, requirements, specs
6
+ * - Reference URLs, videos
7
+ * - Uploaded files (PDFs, images)
8
+ * - Any other context the agent needs
9
+ *
10
+ * The AI uses this context when generating content.
11
+ */
12
+ import { Command } from 'commander';
13
+ import chalk from 'chalk';
14
+ import ora from 'ora';
15
+ import * as fs from 'fs';
16
+ import * as path from 'path';
17
+ import { loadConfig, hasAuth, getProjectId } from '../lib/config.js';
18
+ import { apiRequest, uploadMedia, ApiError } from '../lib/api.js';
19
+ import { output } from '../lib/output.js';
20
+ function exitContextAuthError(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
+ }, { json: true, compact: globalOpts.compact });
32
+ }
33
+ else {
34
+ console.error(chalk.red('Not authenticated. Run: baz auth login <api-key>'));
35
+ }
36
+ process.exit(13);
37
+ }
38
+ function exitContextProjectValidationError(message, globalOpts) {
39
+ if (globalOpts.json) {
40
+ output({
41
+ type: 'error',
42
+ code: 'VALIDATION',
43
+ message,
44
+ category: 'validation',
45
+ retryable: false,
46
+ transient: false,
47
+ exitCode: 64,
48
+ }, { json: true, compact: globalOpts.compact });
49
+ }
50
+ else {
51
+ console.error(chalk.red(message));
52
+ }
53
+ process.exit(64);
54
+ }
55
+ export const contextCommand = new Command('context')
56
+ .description('Manage project context (goals, requirements, references)');
57
+ /**
58
+ * baz context add "<content>" [--label <label>] [--file <path>]
59
+ */
60
+ contextCommand
61
+ .command('add')
62
+ .description('Add context to the current project')
63
+ .argument('[content]', 'Text content, URL, or description')
64
+ .option('--label <label>', 'Label for this context (e.g., "goal", "requirement", "brand")')
65
+ .option('--file <path>', 'Upload a file as context (PDF, image, video)')
66
+ .option('--type <type>', 'Content type: text, url, file, image, video', 'text')
67
+ .action(async (content, options, cmd) => {
68
+ const globalOpts = cmd.optsWithGlobals();
69
+ const config = loadConfig({
70
+ configPath: globalOpts.config,
71
+ apiUrl: globalOpts.apiUrl,
72
+ projectId: globalOpts.projectId,
73
+ });
74
+ if (!hasAuth(config)) {
75
+ exitContextAuthError(globalOpts);
76
+ }
77
+ let projectId;
78
+ try {
79
+ projectId = getProjectId(config, globalOpts.projectId);
80
+ }
81
+ catch (err) {
82
+ exitContextProjectValidationError(err.message, globalOpts);
83
+ }
84
+ // Handle file upload
85
+ if (options.file) {
86
+ const filePath = path.resolve(options.file);
87
+ if (!fs.existsSync(filePath)) {
88
+ console.error(chalk.red(`File not found: ${filePath}`));
89
+ process.exit(64);
90
+ }
91
+ const spinner = globalOpts.json ? null : ora('Uploading file...').start();
92
+ try {
93
+ const buffer = fs.readFileSync(filePath);
94
+ const fileName = path.basename(filePath);
95
+ const ext = path.extname(fileName).toLowerCase();
96
+ // Determine content type from extension
97
+ const mimeTypes = {
98
+ '.pdf': 'application/pdf',
99
+ '.png': 'image/png',
100
+ '.jpg': 'image/jpeg',
101
+ '.jpeg': 'image/jpeg',
102
+ '.gif': 'image/gif',
103
+ '.webp': 'image/webp',
104
+ '.mp4': 'video/mp4',
105
+ '.mov': 'video/quicktime',
106
+ '.webm': 'video/webm',
107
+ };
108
+ const mimeType = mimeTypes[ext] || 'application/octet-stream';
109
+ const contentType = ext === '.pdf' ? 'file'
110
+ : mimeType.startsWith('image/') ? 'image'
111
+ : mimeType.startsWith('video/') ? 'video'
112
+ : 'file';
113
+ const uploadResult = await uploadMedia(config, {
114
+ projectId,
115
+ buffer,
116
+ filename: fileName,
117
+ mimeType,
118
+ });
119
+ if (spinner)
120
+ spinner.text = 'Saving context...';
121
+ const result = await apiRequest(config, 'context.add', {
122
+ projectId,
123
+ content: content || `Uploaded file: ${fileName}`,
124
+ label: options.label,
125
+ contentType,
126
+ fileUrl: uploadResult.url,
127
+ fileName: uploadResult.originalName,
128
+ fileSizeBytes: uploadResult.size,
129
+ });
130
+ spinner?.stop();
131
+ if (globalOpts.json) {
132
+ output({
133
+ type: 'context_added',
134
+ ...result.context,
135
+ fileUrl: uploadResult.url,
136
+ }, { json: true, compact: globalOpts.compact });
137
+ }
138
+ else {
139
+ console.log(chalk.green(`āœ“ Added context with file: ${fileName}`));
140
+ if (options.label)
141
+ console.log(chalk.gray(` Label: ${options.label}`));
142
+ }
143
+ }
144
+ catch (err) {
145
+ spinner?.stop();
146
+ if (err instanceof ApiError) {
147
+ if (globalOpts.json) {
148
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
149
+ }
150
+ else {
151
+ console.error(chalk.red(`Error: ${err.message}`));
152
+ if (err.suggestion)
153
+ console.error(chalk.gray(`Suggestion: ${err.suggestion}`));
154
+ }
155
+ process.exit(err.exitCode);
156
+ }
157
+ throw err;
158
+ }
159
+ return;
160
+ }
161
+ // Text/URL content
162
+ if (!content) {
163
+ console.error(chalk.red('Content is required. Provide text, URL, or use --file'));
164
+ process.exit(65);
165
+ }
166
+ // Auto-detect URL
167
+ const isUrl = content.startsWith('http://') || content.startsWith('https://');
168
+ const contentType = options.type || (isUrl ? 'url' : 'text');
169
+ const spinner = globalOpts.json ? null : ora('Adding context...').start();
170
+ try {
171
+ const result = await apiRequest(config, 'context.add', {
172
+ projectId,
173
+ content,
174
+ label: options.label,
175
+ contentType,
176
+ });
177
+ spinner?.stop();
178
+ if (globalOpts.json) {
179
+ output({
180
+ type: 'context_added',
181
+ ...result.context,
182
+ }, { json: true, compact: globalOpts.compact });
183
+ }
184
+ else {
185
+ console.log(chalk.green('āœ“ Context added'));
186
+ if (options.label)
187
+ console.log(chalk.gray(` Label: ${options.label}`));
188
+ console.log(chalk.gray(` "${content.slice(0, 60)}${content.length > 60 ? '...' : ''}"`));
189
+ }
190
+ }
191
+ catch (err) {
192
+ spinner?.stop();
193
+ if (err instanceof ApiError) {
194
+ if (globalOpts.json) {
195
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
196
+ }
197
+ else {
198
+ console.error(chalk.red(`Error: ${err.message}`));
199
+ if (err.suggestion)
200
+ console.error(chalk.gray(`Suggestion: ${err.suggestion}`));
201
+ }
202
+ process.exit(err.exitCode);
203
+ }
204
+ throw err;
205
+ }
206
+ });
207
+ /**
208
+ * baz context list [--label <label>]
209
+ */
210
+ contextCommand
211
+ .command('list')
212
+ .description('List all context for the current project')
213
+ .option('--label <label>', 'Filter by label')
214
+ .action(async (options, cmd) => {
215
+ const globalOpts = cmd.optsWithGlobals();
216
+ const config = loadConfig({
217
+ configPath: globalOpts.config,
218
+ apiUrl: globalOpts.apiUrl,
219
+ projectId: globalOpts.projectId,
220
+ });
221
+ if (!hasAuth(config)) {
222
+ exitContextAuthError(globalOpts);
223
+ }
224
+ let projectId;
225
+ try {
226
+ projectId = getProjectId(config, globalOpts.projectId);
227
+ }
228
+ catch (err) {
229
+ exitContextProjectValidationError(err.message, globalOpts);
230
+ }
231
+ const spinner = globalOpts.json ? null : ora('Fetching context...').start();
232
+ try {
233
+ const result = await apiRequest(config, 'context.list', {
234
+ projectId,
235
+ label: options.label,
236
+ });
237
+ spinner?.stop();
238
+ if (globalOpts.json) {
239
+ output({
240
+ type: 'context_list',
241
+ projectId,
242
+ count: result.length,
243
+ entries: result,
244
+ }, { json: true, compact: globalOpts.compact });
245
+ }
246
+ else {
247
+ if (result.length === 0) {
248
+ console.log(chalk.gray('No context found for this project.'));
249
+ console.log(chalk.gray('Add context with: baz context add "<content>" --label "goal"'));
250
+ return;
251
+ }
252
+ console.log(chalk.bold(`\nšŸ“‹ Project Context (${result.length} entries)\n`));
253
+ // Group by label
254
+ const grouped = {};
255
+ for (const entry of result) {
256
+ const key = entry.label || '(no label)';
257
+ if (!grouped[key])
258
+ grouped[key] = [];
259
+ grouped[key].push(entry);
260
+ }
261
+ for (const [label, entries] of Object.entries(grouped)) {
262
+ console.log(chalk.cyan.bold(` ${label}:`));
263
+ for (const entry of entries) {
264
+ const preview = entry.content.slice(0, 70);
265
+ const suffix = entry.content.length > 70 ? '...' : '';
266
+ const typeIcon = entry.contentType === 'url' ? 'šŸ”—'
267
+ : entry.contentType === 'file' ? 'šŸ“„'
268
+ : entry.contentType === 'image' ? 'šŸ–¼ļø'
269
+ : entry.contentType === 'video' ? 'šŸŽ¬'
270
+ : 'šŸ“';
271
+ console.log(` ${typeIcon} ${preview}${suffix}`);
272
+ console.log(chalk.gray(` ID: ${entry.id.slice(0, 8)}`));
273
+ }
274
+ console.log();
275
+ }
276
+ }
277
+ }
278
+ catch (err) {
279
+ spinner?.stop();
280
+ if (err instanceof ApiError) {
281
+ if (globalOpts.json) {
282
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
283
+ }
284
+ else {
285
+ console.error(chalk.red(`Error: ${err.message}`));
286
+ }
287
+ process.exit(err.exitCode);
288
+ }
289
+ throw err;
290
+ }
291
+ });
292
+ /**
293
+ * baz context remove <id>
294
+ */
295
+ contextCommand
296
+ .command('remove')
297
+ .description('Remove a specific context entry')
298
+ .argument('<id>', 'Context entry ID (or partial ID)')
299
+ .action(async (contextId, options, cmd) => {
300
+ const globalOpts = cmd.optsWithGlobals();
301
+ const config = loadConfig({
302
+ configPath: globalOpts.config,
303
+ apiUrl: globalOpts.apiUrl,
304
+ projectId: globalOpts.projectId,
305
+ });
306
+ if (!hasAuth(config)) {
307
+ exitContextAuthError(globalOpts);
308
+ }
309
+ let projectId;
310
+ try {
311
+ projectId = getProjectId(config, globalOpts.projectId);
312
+ }
313
+ catch (err) {
314
+ exitContextProjectValidationError(err.message, globalOpts);
315
+ }
316
+ const spinner = globalOpts.json ? null : ora('Removing context...').start();
317
+ try {
318
+ let resolvedContextId = contextId;
319
+ const uuidLike = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
320
+ // Resolve partial IDs against project context entries for better agent ergonomics.
321
+ if (!uuidLike.test(resolvedContextId)) {
322
+ const entries = await apiRequest(config, 'context.list', { projectId });
323
+ const match = entries.find((entry) => entry.id.startsWith(resolvedContextId));
324
+ if (!match) {
325
+ spinner?.stop();
326
+ console.error(chalk.red(`Context not found: ${contextId}`));
327
+ process.exit(64);
328
+ }
329
+ resolvedContextId = match.id;
330
+ }
331
+ await apiRequest(config, 'context.remove', { contextId: resolvedContextId });
332
+ spinner?.stop();
333
+ if (globalOpts.json) {
334
+ output({
335
+ type: 'context_removed',
336
+ contextId: resolvedContextId,
337
+ success: true,
338
+ }, { json: true, compact: globalOpts.compact });
339
+ }
340
+ else {
341
+ console.log(chalk.green('āœ“ Context removed'));
342
+ }
343
+ }
344
+ catch (err) {
345
+ spinner?.stop();
346
+ if (err instanceof ApiError) {
347
+ if (globalOpts.json) {
348
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
349
+ }
350
+ else {
351
+ console.error(chalk.red(`Error: ${err.message}`));
352
+ }
353
+ process.exit(err.exitCode);
354
+ }
355
+ throw err;
356
+ }
357
+ });
358
+ /**
359
+ * baz context clear [--label <label>]
360
+ */
361
+ contextCommand
362
+ .command('clear')
363
+ .description('Clear all context for the current project')
364
+ .option('--label <label>', 'Only clear context with this label')
365
+ .action(async (options, cmd) => {
366
+ const globalOpts = cmd.optsWithGlobals();
367
+ const config = loadConfig({
368
+ configPath: globalOpts.config,
369
+ apiUrl: globalOpts.apiUrl,
370
+ projectId: globalOpts.projectId,
371
+ });
372
+ if (!hasAuth(config)) {
373
+ exitContextAuthError(globalOpts);
374
+ }
375
+ let projectId;
376
+ try {
377
+ projectId = getProjectId(config, globalOpts.projectId);
378
+ }
379
+ catch (err) {
380
+ exitContextProjectValidationError(err.message, globalOpts);
381
+ }
382
+ const spinner = globalOpts.json ? null : ora('Clearing context...').start();
383
+ try {
384
+ const result = await apiRequest(config, 'context.clear', {
385
+ projectId,
386
+ label: options.label,
387
+ });
388
+ spinner?.stop();
389
+ if (globalOpts.json) {
390
+ output({
391
+ type: 'context_cleared',
392
+ projectId,
393
+ deletedCount: result.deletedCount,
394
+ label: options.label,
395
+ }, { json: true, compact: globalOpts.compact });
396
+ }
397
+ else {
398
+ const labelSuffix = options.label ? ` with label "${options.label}"` : '';
399
+ console.log(chalk.green(`āœ“ Cleared ${result.deletedCount} context entries${labelSuffix}`));
400
+ }
401
+ }
402
+ catch (err) {
403
+ spinner?.stop();
404
+ if (err instanceof ApiError) {
405
+ if (globalOpts.json) {
406
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
407
+ }
408
+ else {
409
+ console.error(chalk.red(`Error: ${err.message}`));
410
+ }
411
+ process.exit(err.exitCode);
412
+ }
413
+ throw err;
414
+ }
415
+ });
416
+ /**
417
+ * baz context show (alias for list with full details)
418
+ */
419
+ contextCommand
420
+ .command('show')
421
+ .description('Show all context for the current project (alias for list)')
422
+ .option('--label <label>', 'Filter by label')
423
+ .action(async (options, cmd) => {
424
+ const globalOpts = cmd.optsWithGlobals();
425
+ const config = loadConfig({
426
+ configPath: globalOpts.config,
427
+ apiUrl: globalOpts.apiUrl,
428
+ projectId: globalOpts.projectId,
429
+ });
430
+ if (!hasAuth(config)) {
431
+ exitContextAuthError(globalOpts);
432
+ }
433
+ let projectId;
434
+ try {
435
+ projectId = getProjectId(config, globalOpts.projectId);
436
+ }
437
+ catch (err) {
438
+ exitContextProjectValidationError(err.message, globalOpts);
439
+ }
440
+ const spinner = globalOpts.json ? null : ora('Fetching context...').start();
441
+ try {
442
+ const result = await apiRequest(config, 'context.list', {
443
+ projectId,
444
+ label: options.label,
445
+ });
446
+ spinner?.stop();
447
+ if (globalOpts.json) {
448
+ output({
449
+ type: 'context_list',
450
+ projectId,
451
+ count: result.length,
452
+ entries: result,
453
+ }, { json: true, compact: globalOpts.compact });
454
+ return;
455
+ }
456
+ if (result.length === 0) {
457
+ console.log(chalk.gray('No context found for this project.'));
458
+ console.log(chalk.gray('Add context with: baz context add "<content>" --label "goal"'));
459
+ return;
460
+ }
461
+ console.log(chalk.bold(`\nšŸ“‹ Project Context (${result.length} entries)\n`));
462
+ const grouped = {};
463
+ for (const entry of result) {
464
+ const key = entry.label || '(no label)';
465
+ if (!grouped[key])
466
+ grouped[key] = [];
467
+ grouped[key].push(entry);
468
+ }
469
+ for (const [label, entries] of Object.entries(grouped)) {
470
+ console.log(chalk.cyan.bold(` ${label}:`));
471
+ for (const entry of entries) {
472
+ const preview = entry.content.slice(0, 70);
473
+ const suffix = entry.content.length > 70 ? '...' : '';
474
+ const typeIcon = entry.contentType === 'url' ? 'šŸ”—'
475
+ : entry.contentType === 'file' ? 'šŸ“„'
476
+ : entry.contentType === 'image' ? 'šŸ–¼ļø'
477
+ : entry.contentType === 'video' ? 'šŸŽ¬'
478
+ : 'šŸ“';
479
+ console.log(` ${typeIcon} ${preview}${suffix}`);
480
+ console.log(chalk.gray(` ID: ${entry.id.slice(0, 8)}`));
481
+ }
482
+ console.log();
483
+ }
484
+ }
485
+ catch (err) {
486
+ spinner?.stop();
487
+ if (err instanceof ApiError) {
488
+ if (globalOpts.json) {
489
+ output(err.toJSON(), { json: true, compact: globalOpts.compact });
490
+ }
491
+ else {
492
+ console.error(chalk.red(`Error: ${err.message}`));
493
+ }
494
+ process.exit(err.exitCode);
495
+ }
496
+ throw err;
497
+ }
498
+ });
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare const exportCommand: Command;