Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ evidence in one place.
```bash
# Cloud context for a build or comparison
vizzly context build abc123 --source cloud
vizzly context build abc123 --source cloud --agent --json --offset 10
vizzly context comparison def456 --source cloud --agent --json

# Local workspace context from .vizzly/
Expand All @@ -111,11 +110,12 @@ vizzly context screenshot build-detail-screenshots --source local --json
vizzly context review-queue --source local --json
```

`--json` is the durable automation path. `--agent` gives a normalized handoff
for prompt assembly. Build handoffs contain up to 10 records; use the returned
next-page command or `--offset` to continue without loading the full build. Add
`--full` when you need the whole payload, or
`--include screenshots,diffs,comments` when compact JSON needs selected detail.
`--json` is the durable automation path. `--agent` gives you the compact API
handoff for prompt assembly. Build handoffs contain up to 10 records; use the
exact next-page command returned in `suggested_commands` to continue safely.
That command carries the API's opaque `--cursor`. Add
`--include diffs` for raw diff diagnostics, or `--full` when you need the whole
payload.

Local context is read-only and file-backed. It reads your existing `.vizzly`
workspace state from TDD runs, including screenshots, diffs, and saved hotspot
Expand Down
150 changes: 82 additions & 68 deletions docs/json-output.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,31 +264,27 @@ cloud data or your local `.vizzly` workspace.
```bash
vizzly context build abc123 --source cloud --json
vizzly context build abc123 --source cloud --agent --json
vizzly context build abc123 --source cloud --agent --json --offset 10
vizzly context build abc123 --source cloud --agent --json --include diffs,comments
vizzly context build abc123 --source cloud --agent --json --include diffs
vizzly context build abc123 --source cloud --agent --json --full
vizzly context build current --source local --json
vizzly context build current --source local --agent
```

Use `--json` for durable automation. Use `--agent --json` when you want the compact handoff that
agents should read first. It returns at most 10 actionable evidence records while preserving API
order, with failed captures first and one variant from each screenshot group before additional
variants. Follow the returned next-page command or use `--offset` to continue through that order.
Add `--include diffs` for raw Honeydiff diagnostics on those selected records. Explicit
`screenshots` and `comments` includes return those API collections, and `--full` returns the
complete build context payload unchanged.
agents should read first. The API chooses and orders up to 10 evidence records, then returns an
opaque cursor when more evidence is available. Follow the suggested next-page command or pass that
cursor to `--cursor`. Add `--include diffs` for raw Honeydiff diagnostics on the same page.
`--full` returns the complete build context payload unchanged.

Compact agent JSON:

```json
{
"resource": "build_agent_context",
"source": "cloud",
"project": {
"organization": "acme",
"slug": "storybook",
"name": "Storybook"
"scope": {
"organization": { "slug": "acme" },
"project": { "slug": "storybook", "name": "Storybook" }
},
"build": {
"id": "abc123",
Expand Down Expand Up @@ -316,51 +312,44 @@ Compact agent JSON:
"new": 1
}
},
"evidence_limit": 10,
"evidence_offset": 0,
"evidence_total": 1,
"evidence_returned": 1,
"evidence_has_more": false,
"evidence_truncated": false,
"evidence": [
{
"kind": "comparison",
"id": "cmp-1",
"name": "Dashboard",
"result": "changed",
"review_state": "pending",
"needs_review": true,
"group": {
"name": "Dashboard",
"variant_count": 2,
"needs_review_count": 1,
"failed_count": 0,
"max_diff_percentage": 0.42
},
"screenshot": {
"id": "current-1",
"browser": "chrome",
"viewport": { "width": 1440, "height": 900 },
"bitmap": { "width": 2880, "height": 1800 },
"signature": "Dashboard|1440|chrome",
"url": "https://.../current.png"
},
"baseline": {
"id": "baseline-1",
"build_id": "baseline-build",
"url": "https://.../baseline.png"
},
"diff": {
"percentage": 0.42,
"fingerprint_hash": "00000000001ec127",
"region_count": 12,
"projection": {
"clusters": { "count": 12 }
"evidence": {
"items": [
{
"type": "comparison",
"id": "cmp-1",
"screenshot_name": "Dashboard",
"result": "changed",
"approval_status": "pending",
"needs_review": true,
"screenshot": {
"id": "current-1",
"browser": "chrome",
"viewport": { "width": 1440, "height": 900 },
"bitmap": { "width": 2880, "height": 1800 },
"signature": "Dashboard|1440|chrome",
"url": "https://.../current.png",
"baseline": {
"id": "baseline-1",
"build_id": "baseline-build",
"url": "https://.../baseline.png"
}
},
"image_url": "https://..."
"diff": {
"percentage": 0.42,
"fingerprint_hash": "00000000001ec127",
"region_count": 12,
"image_url": "https://..."
}
}
],
"page": {
"limit": 10,
"returned": 1,
"total": 12,
"has_more": true,
"next_cursor": "opaque-cursor-returned-by-api"
}
],
},
"suggested_commands": [
{
"label": "Inspect comparison context",
Expand All @@ -373,16 +362,20 @@ Compact agent JSON:
{
"label": "Load raw diff diagnostics",
"command": "vizzly --json context build abc123 --agent --include diffs --source cloud"
},
{
"label": "Load next evidence page",
"command": "vizzly --json context build abc123 --agent --cursor opaque-cursor-returned-by-api --source cloud"
}
]
}
```

`status`, `summary`, review state, asset URLs, and Honeydiff values come from the API. The compact
client does not estimate processing progress or rebuild server aggregates. Its local work is
limited to normalization, API-ordered evidence paging, truncation facts, and executable
`suggested_commands`. When more records follow the current page, the suggestions include the exact
next `--offset`. When the page omits any records, they also include a `--full` command.
`status`, `summary`, evidence order, pagination, asset URLs, and Honeydiff values come from the API.
The CLI does not estimate processing progress, rebuild server aggregates, or rank evidence. It adds
source-pinned `suggested_commands` so follow-up requests stay on the same provider. When another page
exists, the next command carries the API's opaque cursor. A `--full` command is always available when
you need the raw payload.

Full build context JSON:

Expand Down Expand Up @@ -458,11 +451,13 @@ Full build context JSON:
```bash
vizzly context comparison cmp-1 --source cloud --json
vizzly context comparison cmp-1 --source cloud --agent --json
vizzly context comparison cmp-1 --source cloud --agent --json --include diffs
vizzly context comparison build-detail-screenshots --source local --json
```

Raw JSON preserves the provider response. Add `--agent` to normalize current, baseline, diff, and
Honeydiff fields into the same evidence shape used by compact build context.
Raw JSON preserves the provider response. Add `--agent` to request the compact API shape. The focal
comparison stays in its API-native shape, including `analysis`; the CLI does not rename those facts.
Similar fingerprint history and recent same-name history stay in separate paged collections.

Agent comparison JSON:

Expand All @@ -472,27 +467,46 @@ Agent comparison JSON:
"source": "cloud",
"comparison": {
"id": "cmp-1",
"name": "Dashboard",
"screenshot_name": "Dashboard",
"result": "changed",
"review_state": "pending",
"approval_status": "pending",
"screenshot": {
"url": "https://.../current.png"
},
"baseline": {
"url": "https://.../baseline.png"
},
"diff": {
"image_url": "https://.../diff.png",
"analysis": {
"diff_image_url": "https://.../diff.png",
"fingerprint_hash": "00000000001ec127",
"regions": [],
"diff_regions": [],
"cluster_metadata": {
"classification": "dynamic_content"
}
}
},
"history": {
"similar_by_fingerprint": [],
"recent_by_name": []
"active_stream": null,
"similar_by_fingerprint": {
"items": [],
"page": {
"limit": 10,
"returned": 0,
"total": 0,
"has_more": false,
"next_cursor": null
}
},
"recent_by_name": {
"items": [],
"page": {
"limit": 10,
"returned": 0,
"total": 0,
"has_more": false,
"next_cursor": null
}
}
}
}
```
Expand Down
1 change: 1 addition & 0 deletions src/api/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ export function createApiClient(options = {}) {
let error = parseApiError(response.status, errorBody, url);
throw new VizzlyError(error.message, error.code, {
status: error.status,
details: error.details,
});
}

Expand Down
23 changes: 21 additions & 2 deletions src/api/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,23 @@ export function shouldRetryWithRefresh(status, isRetry, hasRefreshToken) {
*/
export function parseApiError(status, body, url) {
let message = `API request failed: ${status}`;
let responseDetails = null;
let responseCode = null;
let responseMessage = body;

if (body) {
message += ` - ${body}`;
try {
let parsedBody = JSON.parse(body);
responseDetails = parsedBody.details ?? null;
responseCode = parsedBody.code ?? parsedBody.details?.code ?? null;
responseMessage = parsedBody.error ?? parsedBody.message ?? body;
} catch {
// Plain-text API errors remain valid and are surfaced unchanged.
}
}

if (responseMessage) {
message += ` - ${responseMessage}`;
}

message += ` (URL: ${url})`;
Expand All @@ -268,7 +282,12 @@ export function parseApiError(status, body, url) {
if (status === 429) code = 'RATE_LIMITED';
if (status >= 500) code = 'SERVER_ERROR';

return { message, code, status };
return {
message,
code: responseCode || code,
status,
details: responseDetails,
};
}

/**
Expand Down
32 changes: 11 additions & 21 deletions src/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -978,16 +978,14 @@ contextCmd
.argument('<build-id>', 'Build ID to fetch context for')
.option('--source <source>', 'Context source: auto, cloud, or local', 'auto')
.option('--agent', 'Output compact context for LLM agents')
.option('--full', 'Return the full build context payload with --agent --json')
.option('--full', 'Return the full build context instead of compact context')
.option(
'--offset <n>',
'Skip the first N evidence records with --agent --json',
Number,
0
'--cursor <cursor>',
'Continue compact evidence from an opaque API cursor'
)
.option(
'--include <items>',
'Add detail to compact agent JSON: screenshots,diffs,comments'
'Add raw diff diagnostics to compact context: diffs'
)
.addHelpText(
'after',
Expand All @@ -997,8 +995,7 @@ Examples:
$ vizzly context build current --source local
$ vizzly context build current --source local --agent
$ vizzly context build abc123 --source cloud --agent --json
$ vizzly context build abc123 --source cloud --agent --json --offset 10
$ vizzly context build abc123 --source cloud --agent --json --include diffs,comments
$ vizzly context build abc123 --source cloud --agent --json --include diffs
$ vizzly context build abc123 --source cloud --agent --json --full
`
)
Expand All @@ -1017,29 +1014,22 @@ contextCmd
.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('--agent', 'Normalize JSON evidence for LLM agents')
.option('--agent', 'Output compact context for LLM agents')
.option(
'--similar-limit <n>',
'Maximum similar fingerprint matches to return (1-50)',
Number
'--full',
'Return the full comparison context instead of compact context'
)
.option(
'--recent-limit <n>',
'Maximum recent same-name comparisons to return (1-50)',
Number
)
.option(
'--window-size <n>',
'Historical hotspot analysis window size (1-50)',
Number
'--cursor <cursor>',
'Continue compact history from an opaque API cursor'
)
.option('--include <items>', 'Add detail to compact context: diffs')
.addHelpText(
'after',
`
Examples:
$ vizzly context comparison def456 --source cloud
$ vizzly context comparison def456 --source local
$ vizzly context comparison def456 --source cloud --similar-limit 5 --recent-limit 5
$ vizzly context comparison def456 --source cloud --json
$ vizzly context comparison def456 --source cloud --agent --json
`
Expand Down
Loading