@vizzly-testing/cli 0.29.5 → 0.30.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.
- package/README.md +28 -0
- package/dist/api/endpoints.js +59 -0
- package/dist/api/index.js +1 -1
- package/dist/auth/core.js +1 -1
- package/dist/auth/index.js +2 -1
- package/dist/cli.js +156 -30
- package/dist/commands/context.js +559 -0
- package/dist/commands/logout.js +1 -1
- package/dist/commands/whoami.js +1 -1
- package/dist/config/core.js +1 -1
- package/dist/context/local-workspace-provider.js +412 -0
- package/dist/context/provider-resolver.js +32 -0
- package/dist/reporter/reporter-bundle.css +2 -1
- package/dist/reporter/reporter-bundle.iife.js +119 -119
- package/dist/reporter-ssr/ssr-entry.js +605 -509
- package/dist/sdk/index.js +1 -1
- package/dist/server/routers/assets.js +4 -0
- package/dist/server/routers/events.js +50 -10
- package/dist/tdd/core/hotspot-coverage.js +1 -1
- package/dist/tdd/services/hotspot-service.js +1 -1
- package/dist/tdd/services/region-service.js +1 -1
- package/dist/types/sdk.d.ts +7 -7
- package/dist/utils/colors.js +46 -44
- package/dist/utils/global-config.js +2 -2
- package/dist/utils/report-data.js +31 -0
- package/dist/utils/security.js +3 -2
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -58,6 +58,33 @@ export VIZZLY_TOKEN=your-project-token
|
|
|
58
58
|
vizzly run "npm test" --wait
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
### Visual Context For Agents
|
|
62
|
+
|
|
63
|
+
Use `vizzly context` when you want Vizzly to act more like visual context than a test runner.
|
|
64
|
+
|
|
65
|
+
This is especially useful for LLM agents, automation, and quick debugging loops. Instead of
|
|
66
|
+
making a bunch of narrow API calls, you can ask for one build, comparison, screenshot, or review
|
|
67
|
+
queue bundle and get the evidence in one place.
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# Cloud context for a build or comparison
|
|
71
|
+
vizzly context build abc123
|
|
72
|
+
vizzly context comparison def456 --json
|
|
73
|
+
|
|
74
|
+
# Local workspace context from .vizzly/
|
|
75
|
+
vizzly context build current --source local
|
|
76
|
+
vizzly context screenshot build-detail-screenshots --source local --json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`--json` is the main automation path. Human-readable output is there for quick terminal use, but
|
|
80
|
+
JSON is what you want for scripts, agents, and prompt assembly.
|
|
81
|
+
|
|
82
|
+
Local context is read-only and file-backed. It reads your existing `.vizzly` workspace state from
|
|
83
|
+
TDD runs, including screenshots, diffs, and any saved hotspot or region metadata.
|
|
84
|
+
|
|
85
|
+
Cloud context is also read-only right now. That is intentional. It keeps the trust model simple:
|
|
86
|
+
Vizzly helps you see and inspect visual changes, while people still decide what gets approved.
|
|
87
|
+
|
|
61
88
|
## Capture Screenshots
|
|
62
89
|
|
|
63
90
|
Add screenshots to your existing tests:
|
|
@@ -116,6 +143,7 @@ export default {
|
|
|
116
143
|
| `vizzly tdd start` | Start local TDD server with dashboard |
|
|
117
144
|
| `vizzly tdd run "cmd"` | Run tests once, generate static report |
|
|
118
145
|
| `vizzly run "cmd"` | Run tests with cloud integration |
|
|
146
|
+
| `vizzly context ...` | Fetch visual context bundles for builds, comparisons, and screenshots |
|
|
119
147
|
| `vizzly upload <dir>` | Upload existing screenshots |
|
|
120
148
|
| `vizzly login` | Authenticate via browser |
|
|
121
149
|
| `vizzly doctor` | Validate local setup |
|
package/dist/api/endpoints.js
CHANGED
|
@@ -210,6 +210,65 @@ export async function getComparison(client, comparisonId) {
|
|
|
210
210
|
return response.comparison;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
+
/**
|
|
214
|
+
* Get build context bundle for agent and reviewer workflows
|
|
215
|
+
* @param {Object} client - API client
|
|
216
|
+
* @param {string} buildId - Build ID
|
|
217
|
+
* @returns {Promise<Object>} Build context bundle
|
|
218
|
+
*/
|
|
219
|
+
export async function getBuildContext(client, buildId) {
|
|
220
|
+
return client.request(`/api/sdk/context/builds/${buildId}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get comparison context bundle with similarity and history
|
|
225
|
+
* @param {Object} client - API client
|
|
226
|
+
* @param {string} comparisonId - Comparison ID
|
|
227
|
+
* @param {Object} options - Optional query params
|
|
228
|
+
* @returns {Promise<Object>} Comparison context bundle
|
|
229
|
+
*/
|
|
230
|
+
export async function getComparisonContext(client, comparisonId, options = {}) {
|
|
231
|
+
let endpoint = buildEndpointWithParams(`/api/sdk/context/comparisons/${comparisonId}`, options);
|
|
232
|
+
return client.request(endpoint);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get screenshot context bundle for a screenshot name
|
|
237
|
+
* @param {Object} client - API client
|
|
238
|
+
* @param {string} screenshotName - Screenshot name
|
|
239
|
+
* @param {Object} options - Optional query params
|
|
240
|
+
* @returns {Promise<Object>} Screenshot context bundle
|
|
241
|
+
*/
|
|
242
|
+
export async function getScreenshotContext(client, screenshotName, options = {}) {
|
|
243
|
+
let encodedName = encodeURIComponent(screenshotName);
|
|
244
|
+
let endpoint = buildEndpointWithParams(`/api/sdk/context/screenshots/${encodedName}`, options);
|
|
245
|
+
return client.request(endpoint);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Get project-scoped similar comparisons for a fingerprint hash
|
|
250
|
+
* @param {Object} client - API client
|
|
251
|
+
* @param {string} fingerprintHash - Honeydiff fingerprint hash
|
|
252
|
+
* @param {Object} options - Optional query params
|
|
253
|
+
* @returns {Promise<Object>} Fingerprint context bundle
|
|
254
|
+
*/
|
|
255
|
+
export async function getSimilarFingerprintContext(client, fingerprintHash, options = {}) {
|
|
256
|
+
let encodedFingerprint = encodeURIComponent(fingerprintHash);
|
|
257
|
+
let endpoint = buildEndpointWithParams(`/api/sdk/context/fingerprints/${encodedFingerprint}/similar`, options);
|
|
258
|
+
return client.request(endpoint);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Get pending review queue context for a project
|
|
263
|
+
* @param {Object} client - API client
|
|
264
|
+
* @param {Object} options - Optional query params
|
|
265
|
+
* @returns {Promise<Object>} Review queue context bundle
|
|
266
|
+
*/
|
|
267
|
+
export async function getReviewQueueContext(client, options = {}) {
|
|
268
|
+
let endpoint = buildEndpointWithParams('/api/sdk/context/review-queue', options);
|
|
269
|
+
return client.request(endpoint);
|
|
270
|
+
}
|
|
271
|
+
|
|
213
272
|
/**
|
|
214
273
|
* Search for comparisons by name
|
|
215
274
|
* @param {Object} client - API client
|
package/dist/api/index.js
CHANGED
|
@@ -16,4 +16,4 @@ export { createApiClient, DEFAULT_API_URL } from './client.js';
|
|
|
16
16
|
export { buildApiUrl, buildAuthHeader, buildBuildPayload, buildEndpointWithParams, buildQueryParams, buildRequestHeaders, buildScreenshotCheckObject, buildScreenshotPayload, buildShaCheckPayload, buildUserAgent, computeSha256, extractErrorBody, findScreenshotBySha, isAuthError, isRateLimited, parseApiError, partitionByShaExistence, shaExists, shouldRetryWithRefresh } from './core.js';
|
|
17
17
|
|
|
18
18
|
// Endpoint functions
|
|
19
|
-
export { checkShas, createBuild, finalizeBuild, finalizeParallelBuild, getBatchHotspots, getBuild, getBuilds, getComparison, getPreviewInfo, getScreenshotHotspots, getTddBaselines, getTokenContext, searchComparisons, updateBuildStatus, uploadPreviewZip, uploadScreenshot } from './endpoints.js';
|
|
19
|
+
export { checkShas, createBuild, finalizeBuild, finalizeParallelBuild, getBatchHotspots, getBuild, getBuildContext, getBuilds, getComparison, getComparisonContext, getPreviewInfo, getReviewQueueContext, getScreenshotContext, getScreenshotHotspots, getSimilarFingerprintContext, getTddBaselines, getTokenContext, searchComparisons, updateBuildStatus, uploadPreviewZip, uploadScreenshot } from './endpoints.js';
|
package/dist/auth/core.js
CHANGED
|
@@ -162,7 +162,7 @@ export function buildTokenData(response, existingUser = null) {
|
|
|
162
162
|
* @returns {{ valid: boolean, error: Error|null }}
|
|
163
163
|
*/
|
|
164
164
|
export function validateTokens(auth, requiredField = 'accessToken') {
|
|
165
|
-
if (!auth
|
|
165
|
+
if (!auth?.[requiredField]) {
|
|
166
166
|
let message = requiredField === 'refreshToken' ? 'No refresh token found. Please run "vizzly login" first.' : 'No authentication token found. Please run "vizzly login" first.';
|
|
167
167
|
return {
|
|
168
168
|
valid: false,
|
package/dist/auth/index.js
CHANGED
|
@@ -9,13 +9,14 @@
|
|
|
9
9
|
|
|
10
10
|
// Re-export token store utilities for convenience
|
|
11
11
|
import { clearAuthTokens, getAuthTokens, saveAuthTokens } from '../utils/global-config.js';
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
// HTTP client factory
|
|
14
14
|
export { createAuthClient } from './client.js';
|
|
15
15
|
// Core pure functions
|
|
16
16
|
export { buildAuthHeader, buildAuthUserAgent, buildDevicePollPayload, buildLogoutPayload, buildRefreshPayload, buildRequestHeaders, buildTokenData, parseAuthError, parseAuthenticatedError, validateTokens } from './core.js';
|
|
17
17
|
// Auth operations (take dependencies as parameters)
|
|
18
18
|
export { completeDeviceFlow, initiateDeviceFlow, isAuthenticated, logout, pollDeviceAuthorization, refresh, whoami } from './operations.js';
|
|
19
|
+
export { clearAuthTokens, getAuthTokens, saveAuthTokens };
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Create a token store adapter from global-config functions
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ import { baselinesCommand, validateBaselinesOptions } from './commands/baselines
|
|
|
6
6
|
import { buildsCommand, validateBuildsOptions } from './commands/builds.js';
|
|
7
7
|
import { comparisonsCommand, validateComparisonsOptions } from './commands/comparisons.js';
|
|
8
8
|
import { configCommand, validateConfigOptions } from './commands/config-cmd.js';
|
|
9
|
+
import { contextBuildCommand, contextComparisonCommand, contextReviewQueueCommand, contextScreenshotCommand, contextSimilarCommand, validateContextBuildOptions, validateContextComparisonOptions, validateContextReviewQueueOptions, validateContextScreenshotOptions, validateContextSimilarOptions } from './commands/context.js';
|
|
9
10
|
import { doctorCommand, validateDoctorOptions } from './commands/doctor.js';
|
|
10
11
|
import { finalizeCommand, validateFinalizeOptions } from './commands/finalize.js';
|
|
11
12
|
import { init } from './commands/init.js';
|
|
@@ -71,7 +72,7 @@ const formatHelp = (cmd, helper) => {
|
|
|
71
72
|
key: 'core',
|
|
72
73
|
icon: '▸',
|
|
73
74
|
title: 'Core',
|
|
74
|
-
names: ['run', 'tdd', 'upload', 'status', 'finalize', 'preview', 'builds', 'comparisons']
|
|
75
|
+
names: ['run', 'tdd', 'upload', 'status', 'context', 'finalize', 'preview', 'builds', 'comparisons']
|
|
75
76
|
}, {
|
|
76
77
|
key: 'review',
|
|
77
78
|
icon: '▸',
|
|
@@ -202,40 +203,65 @@ const formatHelp = (cmd, helper) => {
|
|
|
202
203
|
lines.push('');
|
|
203
204
|
return lines.join('\n');
|
|
204
205
|
};
|
|
206
|
+
function extractGlobalOptionsFromArgv(argv, commandNames = null) {
|
|
207
|
+
let configPath = null;
|
|
208
|
+
let verboseMode = false;
|
|
209
|
+
let logLevelArg = null;
|
|
210
|
+
let jsonArg = null;
|
|
211
|
+
for (let i = 0; i < argv.length; i++) {
|
|
212
|
+
if ((argv[i] === '-c' || argv[i] === '--config') && argv[i + 1]) {
|
|
213
|
+
configPath = argv[i + 1];
|
|
214
|
+
}
|
|
215
|
+
if (argv[i] === '-v' || argv[i] === '--verbose') {
|
|
216
|
+
verboseMode = true;
|
|
217
|
+
}
|
|
218
|
+
if (argv[i] === '--log-level' && argv[i + 1]) {
|
|
219
|
+
logLevelArg = argv[i + 1];
|
|
220
|
+
}
|
|
221
|
+
if (argv[i] === '--json') {
|
|
222
|
+
let nextArg = argv[i + 1];
|
|
223
|
+
let nextArgIsCommand = commandNames?.has(nextArg);
|
|
224
|
+
if (nextArg && !nextArg.startsWith('-') && !nextArgIsCommand) {
|
|
225
|
+
jsonArg = nextArg;
|
|
226
|
+
} else {
|
|
227
|
+
jsonArg = true;
|
|
228
|
+
}
|
|
229
|
+
} else if (argv[i].startsWith('--json=')) {
|
|
230
|
+
jsonArg = argv[i].substring('--json='.length);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return {
|
|
234
|
+
configPath,
|
|
235
|
+
verboseMode,
|
|
236
|
+
logLevelArg,
|
|
237
|
+
jsonArg
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function normalizeJsonArgv(argv, commandNames) {
|
|
241
|
+
let normalizedArgv = [...argv];
|
|
242
|
+
for (let i = 0; i < normalizedArgv.length; i++) {
|
|
243
|
+
if (normalizedArgv[i] !== '--json') {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
let nextArg = normalizedArgv[i + 1];
|
|
247
|
+
if (nextArg && !nextArg.startsWith('-') && commandNames.has(nextArg)) {
|
|
248
|
+
normalizedArgv[i] = '--json=true';
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return normalizedArgv;
|
|
252
|
+
}
|
|
205
253
|
program.name('vizzly').description('Vizzly CLI for visual regression testing').version(getPackageVersion()).option('-c, --config <path>', 'Config file path').option('--token <token>', 'Vizzly API token').option('-v, --verbose', 'Verbose output (shorthand for --log-level debug)').option('--log-level <level>', 'Log level: debug, info, warn, error (default: info, or VIZZLY_LOG_LEVEL env var)').option('--json [fields]', 'JSON output, optionally specify fields (e.g., --json id,status,branch)').option('--color', 'Force colored output (even in non-TTY)').option('--no-color', 'Disable colored output').option('--strict', 'Fail on any error (default: be resilient, warn on non-critical issues)').configureHelp({
|
|
206
254
|
formatHelp
|
|
207
255
|
});
|
|
208
256
|
|
|
209
257
|
// Load plugins before defining commands
|
|
210
258
|
// We need to manually parse to get the config option early
|
|
211
|
-
let
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
configPath = process.argv[i + 1];
|
|
218
|
-
}
|
|
219
|
-
if (process.argv[i] === '-v' || process.argv[i] === '--verbose') {
|
|
220
|
-
verboseMode = true;
|
|
221
|
-
}
|
|
222
|
-
if (process.argv[i] === '--log-level' && process.argv[i + 1]) {
|
|
223
|
-
logLevelArg = process.argv[i + 1];
|
|
224
|
-
}
|
|
225
|
-
// Handle --json with optional field selection
|
|
226
|
-
// --json (no value) = true, --json=fields or --json fields = "fields"
|
|
227
|
-
if (process.argv[i] === '--json') {
|
|
228
|
-
let nextArg = process.argv[i + 1];
|
|
229
|
-
// If next arg exists and doesn't start with -, it's the fields value
|
|
230
|
-
if (nextArg && !nextArg.startsWith('-')) {
|
|
231
|
-
jsonArg = nextArg;
|
|
232
|
-
} else {
|
|
233
|
-
jsonArg = true;
|
|
234
|
-
}
|
|
235
|
-
} else if (process.argv[i].startsWith('--json=')) {
|
|
236
|
-
jsonArg = process.argv[i].substring('--json='.length);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
259
|
+
let {
|
|
260
|
+
configPath,
|
|
261
|
+
verboseMode,
|
|
262
|
+
logLevelArg,
|
|
263
|
+
jsonArg
|
|
264
|
+
} = extractGlobalOptionsFromArgv(process.argv);
|
|
239
265
|
|
|
240
266
|
// Configure output early
|
|
241
267
|
// Priority: --log-level > --verbose > VIZZLY_LOG_LEVEL env var > default ('info')
|
|
@@ -479,6 +505,96 @@ Examples:
|
|
|
479
505
|
}
|
|
480
506
|
await comparisonsCommand(options, globalOptions);
|
|
481
507
|
});
|
|
508
|
+
let contextCmd = program.command('context').description('Fetch visual context bundles for agents and reviewers');
|
|
509
|
+
contextCmd.command('build').description('Fetch a build context bundle').argument('<build-id>', 'Build ID to fetch context for').option('--source <source>', 'Context source: auto, cloud, or local', 'auto').addHelpText('after', `
|
|
510
|
+
Examples:
|
|
511
|
+
$ vizzly context build abc123
|
|
512
|
+
$ vizzly context build current --source local
|
|
513
|
+
$ vizzly context build abc123 --json
|
|
514
|
+
$ vizzly context build abc123 --json build.id,summary.comparisons
|
|
515
|
+
`).action(async (buildId, options) => {
|
|
516
|
+
const globalOptions = program.opts();
|
|
517
|
+
const validationErrors = validateContextBuildOptions(options);
|
|
518
|
+
if (validationErrors.length > 0) {
|
|
519
|
+
output.error('Validation errors:');
|
|
520
|
+
for (let error of validationErrors) {
|
|
521
|
+
output.printErr(` - ${error}`);
|
|
522
|
+
}
|
|
523
|
+
process.exit(1);
|
|
524
|
+
}
|
|
525
|
+
await contextBuildCommand(buildId, options, globalOptions);
|
|
526
|
+
});
|
|
527
|
+
contextCmd.command('comparison').description('Fetch a comparison context bundle').argument('<comparison-id>', 'Comparison ID to fetch context for').option('--source <source>', 'Context source: auto, cloud, or local', 'auto').option('--similar-limit <n>', 'Maximum similar fingerprint matches to return (1-50)', val => parseInt(val, 10)).option('--recent-limit <n>', 'Maximum recent same-name comparisons to return (1-50)', val => parseInt(val, 10)).option('--window-size <n>', 'Historical hotspot analysis window size (1-50)', val => parseInt(val, 10)).addHelpText('after', `
|
|
528
|
+
Examples:
|
|
529
|
+
$ vizzly context comparison def456
|
|
530
|
+
$ vizzly context comparison def456 --source local
|
|
531
|
+
$ vizzly context comparison def456 --similar-limit 5 --recent-limit 5
|
|
532
|
+
$ vizzly context comparison def456 --json
|
|
533
|
+
`).action(async (comparisonId, options) => {
|
|
534
|
+
const globalOptions = program.opts();
|
|
535
|
+
const validationErrors = validateContextComparisonOptions(options);
|
|
536
|
+
if (validationErrors.length > 0) {
|
|
537
|
+
output.error('Validation errors:');
|
|
538
|
+
for (let error of validationErrors) {
|
|
539
|
+
output.printErr(` - ${error}`);
|
|
540
|
+
}
|
|
541
|
+
process.exit(1);
|
|
542
|
+
}
|
|
543
|
+
await contextComparisonCommand(comparisonId, options, globalOptions);
|
|
544
|
+
});
|
|
545
|
+
contextCmd.command('screenshot').description('Fetch screenshot context and historical memory').argument('<name>', 'Screenshot name').option('--source <source>', 'Context source: auto, cloud, or local', 'auto').option('-p, --project <slug-or-id>', 'Project scope for user auth lookups').option('--org <slug>', 'Organization slug when project slug is ambiguous').option('--recent-limit <n>', 'Maximum recent comparisons to return (1-50)', val => parseInt(val, 10)).option('--window-size <n>', 'Historical hotspot analysis window size (1-50)', val => parseInt(val, 10)).addHelpText('after', `
|
|
546
|
+
Examples:
|
|
547
|
+
$ vizzly context screenshot Dashboard
|
|
548
|
+
$ vizzly context screenshot Dashboard --source local
|
|
549
|
+
$ vizzly context screenshot Dashboard --project storybook --org acme
|
|
550
|
+
$ vizzly context screenshot Dashboard --json
|
|
551
|
+
`).action(async (name, options) => {
|
|
552
|
+
const globalOptions = program.opts();
|
|
553
|
+
const validationErrors = validateContextScreenshotOptions(options);
|
|
554
|
+
if (validationErrors.length > 0) {
|
|
555
|
+
output.error('Validation errors:');
|
|
556
|
+
for (let error of validationErrors) {
|
|
557
|
+
output.printErr(` - ${error}`);
|
|
558
|
+
}
|
|
559
|
+
process.exit(1);
|
|
560
|
+
}
|
|
561
|
+
await contextScreenshotCommand(name, options, globalOptions);
|
|
562
|
+
});
|
|
563
|
+
contextCmd.command('similar').description('Fetch project-scoped matches for a fingerprint hash').argument('<fingerprint-hash>', 'Fingerprint hash to search for').option('--source <source>', 'Context source: auto, cloud, or local', 'auto').option('-p, --project <slug-or-id>', 'Project scope for user auth lookups').option('--org <slug>', 'Organization slug when project slug is ambiguous').option('--limit <n>', 'Maximum matches to return (1-50)', val => parseInt(val, 10)).addHelpText('after', `
|
|
564
|
+
Examples:
|
|
565
|
+
$ vizzly context similar fp-dashboard
|
|
566
|
+
$ vizzly context similar fp-dashboard --project storybook --org acme
|
|
567
|
+
$ vizzly context similar fp-dashboard --json
|
|
568
|
+
`).action(async (fingerprintHash, options) => {
|
|
569
|
+
const globalOptions = program.opts();
|
|
570
|
+
const validationErrors = validateContextSimilarOptions(options);
|
|
571
|
+
if (validationErrors.length > 0) {
|
|
572
|
+
output.error('Validation errors:');
|
|
573
|
+
for (let error of validationErrors) {
|
|
574
|
+
output.printErr(` - ${error}`);
|
|
575
|
+
}
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
await contextSimilarCommand(fingerprintHash, options, globalOptions);
|
|
579
|
+
});
|
|
580
|
+
contextCmd.command('review-queue').description('Fetch pending review context for a project').option('--source <source>', 'Context source: auto, cloud, or local', 'auto').option('-p, --project <slug-or-id>', 'Project scope for user auth lookups').option('--org <slug>', 'Organization slug when project slug is ambiguous').option('--limit <n>', 'Maximum comparisons to return (1-100)', val => parseInt(val, 10)).option('--offset <n>', 'Skip first N comparisons', val => parseInt(val, 10)).addHelpText('after', `
|
|
581
|
+
Examples:
|
|
582
|
+
$ vizzly context review-queue --project storybook --org acme
|
|
583
|
+
$ vizzly context review-queue --source local
|
|
584
|
+
$ vizzly context review-queue --limit 10
|
|
585
|
+
$ vizzly context review-queue --json
|
|
586
|
+
`).action(async options => {
|
|
587
|
+
const globalOptions = program.opts();
|
|
588
|
+
const validationErrors = validateContextReviewQueueOptions(options);
|
|
589
|
+
if (validationErrors.length > 0) {
|
|
590
|
+
output.error('Validation errors:');
|
|
591
|
+
for (let error of validationErrors) {
|
|
592
|
+
output.printErr(` - ${error}`);
|
|
593
|
+
}
|
|
594
|
+
process.exit(1);
|
|
595
|
+
}
|
|
596
|
+
await contextReviewQueueCommand(options, globalOptions);
|
|
597
|
+
});
|
|
482
598
|
program.command('config').description('Display current configuration').argument('[key]', 'Specific config key to get (dot notation, e.g., comparison.threshold)').action(async (key, options) => {
|
|
483
599
|
const globalOptions = program.opts();
|
|
484
600
|
|
|
@@ -747,4 +863,14 @@ program.command('whoami').description('Show current authentication status and us
|
|
|
747
863
|
// Save user's PATH for menubar app (non-blocking, runs in background)
|
|
748
864
|
// This auto-configures the menubar app so it can find npx/node
|
|
749
865
|
saveUserPath().catch(() => {});
|
|
750
|
-
program.
|
|
866
|
+
let commandNames = new Set(program.commands.map(command => command.name()));
|
|
867
|
+
let normalizedArgv = normalizeJsonArgv(process.argv, commandNames);
|
|
868
|
+
let normalizedGlobals = extractGlobalOptionsFromArgv(normalizedArgv, commandNames);
|
|
869
|
+
output.configure({
|
|
870
|
+
logLevel: normalizedGlobals.logLevelArg,
|
|
871
|
+
verbose: normalizedGlobals.verboseMode,
|
|
872
|
+
color: colorOverride,
|
|
873
|
+
json: normalizedGlobals.jsonArg,
|
|
874
|
+
resetTimer: false
|
|
875
|
+
});
|
|
876
|
+
program.parse(normalizedArgv);
|