UI annotation and bug traces for AI coding agents — precise, element-pinned hand-offs between you and Claude Code / Cursor, in both directions.
Caliper is built on one idea: a running page already knows everything an agent needs. A clicked element carries a stable selector, its owning component and computed styles matched to your design tokens; a reproduction carries its steps, DOM, console, network and store actions. None of that survives a screenshot, and all of it is text an agent can act on directly.
Two products put that to work in opposite directions.
🐞 Human → agent — Caliper QA (@caliper/qa-extension)
A Chrome extension for manual QA. A reviewer marks broken UI on the live app; the export is a compact payload — selector, component, token-matched styles — an agent fixes straight from the file.
Some defects are not a moment but a sequence — "save works, but only the second time". No screenshot will ever explain one. Start trace records the reproduction instead: the steps taken, the DOM as it changed, console with stack traces, every request with its status and body, and the actions the store dispatched — plus a ~1 MB video.
The split is the point: the trace is what the agent reads, the video is for the human. Below, a
second checkout submit comes back 409, the app dispatches Save Succeeded anyway, and a TypeError
lands two milliseconds later — the agent sees all three, correlated by timestamp, without the app
running.
💬 Agent → human — @dendiem/caliper
An MCP server for the reverse flow. While a coding agent implements a UI and is unsure what a region should do, it asks you — questions pinned to the live elements, answered in place, sent straight back as structured data.
Both products share the same element-picking core and in-page overlay:
| Package | Description |
|---|---|
packages/core |
Element → annotation logic and the session/trace schema. No chrome.*, no UI framework, portable to any shell. |
packages/overlay |
In-page picker UI rendered in a Shadow DOM. |
packages/recorder |
Trace collection — buffers, console/network collectors, the devtools state bridge. No chrome.*, so it is testable outside a browser. |
apps/qa-extension |
Chrome MV3 extension for manual QA — README. |
apps/ask |
MCP server for live agent→developer UI review, and the pull / read / trace readers — README. |
Two independent tools — set up whichever direction you work in (or both).
Install from the Chrome Web Store, or build it locally:
pnpm install
pnpm --filter @caliper/qa-extension buildThen load apps/qa-extension/.output/chrome-mv3 via chrome://extensions → Load unpacked.
pnpm --filter @caliper/qa-extension dev gives you hot reload, but writes a development build to the
same directory: it registers the content script at runtime through the dev server instead of
declaring it in the manifest, so the picker stops working the moment that server is gone. If the
shortcut list shows Alt+R — Reload the extension during development, you are running the dev build.
Click the toolbar icon to open the side panel, mark an element, describe the defect, and save — then review and export from the panel. Start trace in the same panel records a whole reproduction instead of a single element; press Stop and the trace joins the session.
A developer reads either kind the same way — caliper pull <ticket> when QA filed it to Jira, or
caliper read <zip> when QA sent the archive directly.
Register it with your coding agent (Claude Code, Codex, Cursor, Windsurf, VS Code, Gemini CLI, or any MCP client) — one command, and it auto-updates on each launch:
npx @dendiem/caliper initThat installs the caliper_ask and caliper_design tools plus the caliper-ask / caliper-fix
skills. In Claude Code you can instead add it as a plugin: /plugin marketplace add DenDiem/caliper.
See apps/ask for the proxy/snippet modes, named targets, and the agent contract.
The extension turns each marked defect into a compact record — this is the JSON export (TOON and a screenshot zip are available too):
{
"schemaVersion": 1,
"annotations": [
{
"comment": "Padding is too small",
"severity": "minor",
"target": {
"selector": "soa-inform-block p.info",
"selectorConfidence": "medium",
"componentName": "soa-inform-block",
"componentSource": "tag-heuristic",
"styles": {
"padding-top": {"value": "4px", "token": "--spacing-1", "tokenMatch": "exact"},
"color": {"value": "rgb(51, 51, 51)", "token": "--color-text-primary", "tokenMatch": "exact"}
}
}
}
]
}Screenshots live in a separate assets map keyed by screenshotId, and are omitted from
Copy JSON by default.
Every tag matching v* builds, verifies, publishes to the Chrome Web Store and attaches the zip to
a GitHub release:
git tag v0.2.0
git push --follow-tagsThe workflow takes the version from the tag name, so package.json is never bumped by hand.
One-time setup. The Chrome Web Store API can only update an existing item, so the first version has to be uploaded manually — that upload is what mints the extension ID. After that:
- Google Cloud Console → new project → enable Chrome Web Store API → OAuth client of type Desktop app.
- Run
pnpm --filter @caliper/qa-extension exec wxt submit init— it walks through the OAuth flow and prints the refresh token. - Add four repository secrets:
CHROME_EXTENSION_ID,CHROME_CLIENT_ID,CHROME_CLIENT_SECRET,CHROME_REFRESH_TOKEN. - Verify without uploading anything:
wxt submit --dry-run --chrome-zip .output/caliper-<v>-chrome.zip.
Each upload goes through Google's review, so a published tag is not live immediately.
Everything stays in chrome.storage.local. No backend, no network requests, no telemetry — see
PRIVACY.md.
MIT


