Pinta Documentation
Pinta is a Chrome extension and companion server that hands visual UI annotations to a coding agent — Claude Code, Cursor, Aider, or any MCP-compatible tool — which edits your source files. This guide covers the mental model, every module, the multi-agent workflow, and a troubleshooting reference.
What is Pinta
Pinta closes the loop between "I see what's wrong on this page" and "the agent edits the right file." You annotate the running app — pick an element, draw on a screenshot, drop a pin, type a comment — and hit Submit. The agent reads your annotation, finds the source file, makes the change, and reports back. No screenshots pasted into chat, no manually grepping for selectors, no copy-paste round trips.
Three pieces work together:
| Component | Role |
|---|---|
| Extension | Chrome side panel + on-page overlay. Where you draw and submit. |
| Companion | Node server on 127.0.0.1:7878+. Receives your submission, persists the session. |
| Agent | Claude Code (or compatible) running the /pinta skill. Reads the session, edits source files. |
The mental model
Pinta is opinionated about the verbs you use to talk to your codebase. Today there are three:
- Act — Annotate, optionally with GitLab Issues. You're asking the agent to do something to your source. This is the original Pinta loop.
- Test — Test Pilot. You're driving an interactive QA spec with the agent's help.
- Inquire — Chat. You're asking before you commit — explanations, what-if questions, "what does this do?"
- Audit — AuditFlow. You're asking the agent to look across the project and surface findings, then route the ones you care about back into Act.
Each verb is a Pinta module. Enable the ones you want in Settings; ignore the rest. The extension's UI is just the surface; the wire protocol and agent skill stay narrow.
Install & first run
1. Install the Chrome extension
Get it from the Chrome Web Store (search for "Pinta"), or load it unpacked from the GitHub repo if you want the unreleased build.
An unpacked build has its own extension id, so the companion refuses it
until you trust it once, from a terminal (copy the id from
chrome://extensions):
npx pinta-companion trust <extension-id>
# undo: npx pinta-companion untrust <extension-id> list: npx pinta-companion trust --list
2. Start the companion in your project
npx pinta-companion .
# Or, if you cloned the repo for Pinta dev:
node ~/.claude/skills/pinta/start-companion.js .
The companion binds the first free port in 7878-7898 and
registers itself in ~/.pinta/registry.json so the
extension + agent skill can find it.
3. Install the /pinta skill
The /pinta slash command is a Claude Code skill — it is
not installed by npx pinta-companion.
Install it once (no clone needed), then fully restart Claude
Code (skills load at startup; /clear is not
enough):
npx pinta-companion@0.10.0 install-skill
The @0.5.0 is deliberate — npx may have an
older pinta-companion cached, and the
install-skill subcommand only exists in 0.5.0+. Prefer a
plugin? /plugin marketplace add kevzlou7979/pinta then
/plugin install pinta@pinta (invoked as
/pinta:pinta, auto-updates).
4. Run /pinta in Claude Code
Open a Claude Code terminal in your project root and type
/pinta. The skill discovers the companion via the
registry, opens a long-lived SSE stream, and waits for sessions.
It will say something like:
"Companion is up on port 7878 for /path/to/your/project.
Open the Pinta Chrome extension, annotate the page you want
changed, and hit Submit. I'll wait."
5. Open the side panel
Click the Pinta icon in your Chrome toolbar (or pin it first). The side panel opens on the right; the project picker at the top should say Connected · your-project.
Your first annotation
- Open the page you want to change in any tab.
- In the side panel, click Select (the cursor icon) and click any element on the page. A pink halo appears; the element gets a pin badge.
- Type a comment in the popup: "make this button match the brand color".
- Hit Submit in the panel.
- Your
/pintaterminal lights up: the agent reads the annotation, finds the source file, shows you the plan, and waits for confirmation. - Reply "go" (or just confirm) and the edit happens. Refresh the page to see it — or rely on HMR / auto-reload if your dev server supports it.
Annotate always on
The core verb. Three annotation shapes:
- Select — pick a DOM element. The agent gets the selector, outer HTML, computed styles, and surrounding text.
- Draw — arrow, rectangle, circle, freehand, pin. The agent reads the composited screenshot, plus the element under the drawing's anchor when it's on-screen.
- Image — drop a reference image at a specific spot. The agent sees both the screenshot and your reference.
Inline editor
On select mode, the popup has tabs for Content
(edit text in place), Font / Sizing / Spacing
(CSS pickers populated from getComputedStyle), and
CSS (free-form). Edits apply to the DOM live so
you see the result before submitting. The diff is captured and
shipped to the agent verbatim.
Hotkeys
On the page (the overlay ignores INPUT / TEXTAREA / contenteditable so typing into your app never triggers these):
Alt+S— toggle Select modeAlt+P— toggle Draw mode (Pen)Alt+X— exit to idleAlt+V— dictate into the focused field (Voice Command enabled; works inside fields too)Esc— cancel an in-progress annotation or exit the active modeCtrl+Alt+<letter>— pick a tool while the floating toolbar is on:VSelect,AArrow,RRect,PPen,NPin,IImage,MMove,TText,DDelete,SResize,BPaint,CScale,FFree transform
Side panel (not while typing in a field):
Ctrl/Cmd+Z— undo the last annotationCtrl/Cmd+Shift+Z(orCtrl/Cmd+Y) — redo
Inline element editor + comment popover:
Ctrl/Cmd+Enter— submit comment / save editEsc— cancel and close
Chat input (Test Pilot per-row, global, Just Ask):
Enter— send messageShift+Enter(orAlt+Enter) — new line
Settings → Keyboard shortcuts lists the same set inside the side panel for quick reference.
GitLab Issues module
Tick Create GitLab issues in the submit footer and
the agent files one issue per annotation after the source edits land.
Auth is delegated to your glab CLI (run
glab auth login --scopes api,write_repository once);
Pinta never sees the token.
Each issue carries the annotation's selector, source-file pointer,
page URL, the full-page screenshot (uploaded to your project's
uploads endpoint), and a footer linking back to the
Pinta session, with a summary / Root cause / Fix direction / Repro
body. The gear next to Create GitLab
issues sets the Assignee and
Labels (default domain:client, bug)
applied to every issue — the agent files straight away, no chat
prompt. Blank labels means no labels.
Test Pilot module
Interactive UAT module in its own side-panel tab. Two ways to start:
- Import a hand-written markdown test spec.
- Generate a UAT spec from project context — the agent scans your routes / components / auth flow and writes one.
Either way you get a catalog — sections of test rows. Mark each row Pass, Fail, or leave it untested. Click the ? on any row to ask the agent for step-by-step instructions (toggle Detailed help steps in Settings for deeper technical context).
Editing the catalog (Phase 13)
Right-click the kebab on any section or row for delete / add /
rename / move-up / move-down / "add test below". Drag-and-drop
also works — grab a row's grip handle on the leading edge. Edits
sync to .pinta/test-docs/{docId}.md on disk so the
agent's ? flow works against rows you added
yourself.
Auto-save status to disk
Pass / Fail marks land in a Result column in the on-disk markdown — so if browser storage gets wiped, re-importing the same spec restores your marks. Catalog structure is the source of truth.
Tester round-trip v0.10.0
Export → Tester sheet (.md / .docx) or
Email hands the catalog to a human tester. Any test
without steps gets them generated first (untick Generate
missing steps first to export as-is). The tester imports the
sheet in Pinta with no companion, marks Pass / Fail, and sends ONE
file back: Results (.md) with a sign-off, or a
.pinta bundle with Contents set to
Annotations + test results. Importing it overlays
their marks read-only on your catalog; Clear
restores your own.
Smoke vs Thorough
The Smoke test / Thorough toggle in the tab (or Settings → Test Pilot → Thorough test generation) sets how much catalog import / generate produces. Smoke (default) is a quick happy-path pass; Thorough covers every feature with edge cases and negative paths — more tokens, slower.
File failed tests + Suggest Test
File failed tests in the header files every failed,
not-yet-filed row in one run: a GitLab issue per test via
glab (no duplicates), or entries in
.pinta/tasks.md when GitLab isn't set up.
Suggest Test on a section header asks the agent for
missing scenarios; tick the ones you want and they join the section.
Chat module
Inquiry-mode module — one Settings toggle lights up three chat surfaces:
- Global chat — speech-bubble icon in the side-panel header. Ask Pinta-shaped questions ("how do I disable screenshot opt-in?"). Supports pasting images.
- Annotate "Just Ask" — checkbox in the submit footer. When ticked, Submit re-labels to "Ask agent" — the agent answers without editing source. Pivot to a real submit by unticking.
- Test Pilot per-row chat — chat button on each test row. Context auto-attaches (test ID, expected result, current steps).
Replies render with light markdown (inline code, fenced
blocks with syntax highlighting, > Note: callouts).
The Test Pilot tier has a special affordance: when the agent
suggests test scenarios in its reply, a one-click
Add N to spec button appears below the bubble —
adds the suggestions to the current section (or creates a new
section).
Detailed responses toggle
Settings → Chat → Detailed responses. Off (default) = short, tester-friendly. On = deeper technical detail (curl, payloads, env vars, ARIA names). Affects all three chat surfaces.
AuditFlow module
Lighthouse-style audit surface in its own side-panel tab. Pick categories, run audit, get back per-category scores with actionable findings.
Categories (Phase 15b)
- Security — XSS, CSRF,
eval/{@html}misuse, secret leakage in source, CSRF guards on destructive endpoints,npm auditfor dependency CVEs. - Performance — bundle entry count, production deps count, heavy known offenders (lodash full, moment), sync fetches in render, missing lazy routes, build target sanity, missing
loading="lazy". - Accessibility — alt text, labeled inputs, ARIA misuse, heading hierarchy, click handlers on non-interactive elements, focus-visible, missing
lang, plus localization bugs: hardcoded UI strings, missing translation keys, sentence concatenation, hand-rolled plurals, dates / numbers / currency formatted by hand, truncation under text expansion, RTL readiness, locale-blind sorting. - Mobile — viewport meta, fixed-px containers, touch-target sizing, hover-only interactions, horizontal-overflow risks, mouse-only drag listeners.
- Cross-Browser soon — caniuse-resolved support matrix per finding.
All categories are static analysis of your source by the agent — no browser or Lighthouse run. Add your own categories and checks, or ask for more with Suggest checks.
Fix with agent
Every finding has a Fix with agent action that
fixes the source in place, with a per-row loader; a category's ⋮
menu adds Fix All. Discuss opens a
chat about the finding and File issue files it to
GitLab (or .pinta/tasks.md). Mark findings resolved or
won't-fix — the marks survive re-runs.
Scoring
Deterministic:
(pass × 1 + warn × 0.5 + fail × 0) / (pass + warn + fail) × 100.
Info checks excluded from the denominator. Rating: 90+ Excellent,
70+ Good, 50+ Needs work, else Poor.
Design Variants module v0.9.0
Ask for design options without leaving your app. Pick an element (or the whole page) and the agent returns 1–5 variants that stay inside your design system, as preview cards. Enable it in Settings, then open the Design Variants tab.
- Brief — optional art direction (up to 280 characters) and a pasted screenshot of the look you want. The agent translates it into your project's tokens.
- Preview on page — swaps the real element for the variant in place; Restore or
Escputs it back. - Refine — chat about one variant ("add a subtle background to the heading"); the card updates, source doesn't.
- Use this — the agent edits source to match the card exactly. Then a match check compares the live element with the card (no agent tokens): it says "matches" or lists the differing properties, and Fix differences sends only those back.
- Reset clears the pick, direction, image and results. The optional Pages gallery shows your routes live at a device width.
Settings: optional design-system path and device presets. Agent HTML is sanitized before it reaches the panel or your page.
Code Review module v0.9.0
A game-style review of your own change. The agent reads your uncommitted diff (or the last commit when the tree is clean) and deals one card per logical change. Enable it in Settings, then open the Code Review tab.
- Topic mode — type a focus (e.g. "MFA authentication") to review the relevant code instead of the diff.
- Pass / Fail / Learn each card (keys
P/F/L, arrows to move). Fail takes an optional one-line note. - Learn opens a chat that explains how the code works; read-only.
- The end card grades the run (S–D) and lists failed cards, each with Fix — the only action that edits code.
Devices module v0.9.0
See your running app on many devices at once. Enable it in Settings, open the Devices tab, and launch the canvas — a full browser tab of live, clickable device frames. The canvas needs no agent; annotations you make inside a device go to the agent like any other.
- Add phones, tablets, laptops and desktops (or a whole group), or a Custom size. Rotate, zoom, expand one frame, drag frames anywhere; Rearrange tidies the canvas.
- Sync — navigate in one frame and the others follow.
- Annotate this device — the frame header's Annotate button makes that frame the target, so the side panel's tools work inside it. One device at a time; its width tags each annotation, so the agent edits that breakpoint. Screenshots capture the device's viewport.
- Frames respond to real media queries. Apps that forbid framing (
X-Frame-Options/frame-ancestors) won't render, and cookie logins may not carry into frames.
Report module
See what you shipped. Enable it in Settings, open the Report tab and pick a range; the agent gathers git, GitHub / GitLab and Pinta activity (read-only) into day cards.
- Ranges: a day, a week, a 10-day sprint, or a custom window. Weekend work folds into a nearby weekday.
- Projects — combine other repos into one report.
- Per entry: a proof screenshot and How to test steps.
- Export — a day as detailed markdown, or the whole report as an invoice-ready summary: one short paragraph per weekday, full dates, no duplicates.
Role flags (Phase 18) multi-terminal
Run multiple /pinta Claude Code terminals against the
same project and dedicate each to a workload. Each terminal declares
which session kinds it claims; mismatches silently skip to other
terminals.
| Flag | Claims sessions where… |
|---|---|
--annotate | modules[] contains none of test-pilot, audit-flow, chat, design-variants, code-review |
--test-pilot | modules[].id contains test-pilot |
--audit | modules[].id contains audit-flow |
--chat | modules[].id contains chat |
--variants | modules[].id contains design-variants |
--review | modules[].id contains code-review |
| (no flag) | Claim everything — default, current behavior |
Flags stack: /pinta --test-pilot --audit claims both
kinds. Typical 4-terminal setup:
Terminal 1: /pinta --annotate # source-edit work
Terminal 2: /pinta --test-pilot # UAT runs + per-row chat
Terminal 3: /pinta --audit # audit runs, low traffic
Terminal 4: /pinta --chat # Just-Ask + global chat
The companion enforces roles: a terminal that tries to claim a session outside its role gets a 403.
/pinta terminal is claiming annotation
sessions. Run /pinta in your project's Claude Code
terminal (or /pinta --annotate)."
Multi-project setup
One companion runs per project. The extension's project picker (top
of side panel) shows every running companion. When you switch tabs,
Pinta auto-routes to the companion whose URL patterns match —
annotations made on localhost:5173 go to one project,
annotations on localhost:3000 go to another, all without
you re-picking.
Add URL patterns by clicking the project name in the picker, or by
editing .pinta.json in your project root:
{
"urlPatterns": [
"http://localhost:5173/**",
"https://staging.example.com/**"
]
}
Settings
Click the gear icon in the side-panel header. Modules are listed; tick to enable. Each enabled module exposes its own settings:
- GitLab Issues — optional
project_idoverride + default labels. - Test Pilot — Detailed help steps + Thorough test generation (both default off).
- Chat — Detailed responses (default off) + Redact PII (default on — strips emails / phones / IDs from page content sent to the agent).
- AuditFlow, Code Review, Report — no settings.
- Design Variants — optional design-system path + device presets.
- Devices — optional custom devices (JSON).
Visual feedback
Settings → Visual feedback → Processing pulse enables a pulsing colored glow around the page edges while the agent is applying changes. Off by default; cosmetic only.
Standalone mode
Pinta works without a companion. Useful for testers hitting deployed URLs who don't have the project on disk: the side panel falls into Standalone mode, sessions live in browser IndexedDB, and you can Copy (markdown to clipboard) or Download instead of Submit.
Standalone is also useful for trying Pinta before you've installed
the companion — the annotation UX works fully; only the
agent-edit loop is gated on having a companion + /pinta
running.
Share files (.pinta)
Click the download icon in the panel header to export a session as a .pinta file: a self-contained JSON document with the annotations, screenshots inline, and your manifest (title, author, accent color, description). Send it to a teammate; they open Pinta and pick Import .pinta.
Imported sessions are read-only in the viewer. Two follow-up paths:
- Send to agent (connected mode) — files the imported annotations as a brand-new submission against your project. Agent picks up like any other.
- Fork to local (standalone mode) — clones into an editable draft you can extend.
Module settings (e.g. GitLab tokens) are stripped from share files. Each recipient uses their own configured modules.
Troubleshooting
Side panel says "No companion is running"
Start the companion in your project root:
npx pinta-companion .
If it complains about another companion already on the port,
check ~/.pinta/registry.json — stale entries from
crashed companions can linger.
Side panel says the companion refuses this extension
The companion only talks to the Web Store build, ids in
PINTA_EXTENSION_IDS, and ids you trusted with
npx pinta-companion trust <extension-id> (stored in
~/.pinta/trusted-extensions.json). Nothing is trusted
automatically, so another extension can't pose as Pinta. Run the
trust command only for a build you loaded yourself.
Companion is running but the agent doesn't pick up sessions
Most likely: /pinta isn't running in a Claude Code
terminal for this project. Start one. If you're using role flags,
make sure at least one terminal covers the kind you're submitting
(see Role flags for the matrix).
Agent edited the wrong file
Pinta uses a 3-tier locate strategy: data-source-file
attributes (Phase 6 Vite plugin), then a project-wide grep for
nearby text, then a route-scoped fallback. The Vite plugin gives
the highest accuracy (~95%); without it, you're at grep-accuracy
(~80%). If you keep hitting wrong files, install the plugin or
give the agent more nearby text by typing a verbose comment.
Auto-apply edited a file but I wanted to discuss first
Auto-apply is off by default. If it's on and you want a discussion instead, untick it AND tick Just Ask — the submit button re-labels to "Ask agent" and routes to the chat sheet instead of editing source.
GitLab Issues filed but no screenshot embedded
Your glab token likely lacks api /
write_repository scope. Re-auth:
glab auth logout
glab auth login --scopes api,write_repository
Chat says "Agent returned an empty response"
Usually means the agent had no answer for an image-context prompt
and submitted blank. Try rephrasing with more context (paste a
screenshot into the chat, mention the file path), or restart
/pinta if the agent seems stuck.
Sessions sitting in "Submitted" forever
After 10 seconds, the side panel surfaces a claim-warning telling
you what's likely wrong (no /pinta running, or no
terminal accepting the session's kind under your role flags).
Most fixes: start a /pinta, or unblock a stuck agent
terminal.
Privacy
Pinta sends annotation contents (selectors, comments, optional
screenshots) to the agent you've configured — the
same Claude Code / Cursor / etc. you'd use anyway. Pinta itself
doesn't host servers, store sessions remotely, or send telemetry.
The companion binds 127.0.0.1 only.
See the full Privacy Policy for the per-field breakdown.
What's next
See the Roadmap on the landing page for what's planned. Quick highlights:
- Phase 6 — a Vite plugin for instant source mapping (Pinta already reads
data-source-file/data-source-lineif your build adds them) - Phase 17 — Claude Design (ambient design-system module)
- AuditFlow — generate custom checks from pasted docs or a URL
- Design Variants, Code Review and Devices — shipping in the next release