The industry-standard accessibility rule engine (axe-core — the one under Lighthouse), bundled INSIDE the extension and run locally on the live DOM: nothing leaves the page, no remote code. Hundreds of WCAG A/AA checks on top of qa_run_audit's own set, narrowed to WCAG-tagged rules so every violation maps to a criterion. Returns violations with impact, WCAG tags, help URL and up to 10 offending selectors each. Read-only. Needs an extension build that carries the engine — an older build answers "unknown command: axe" (reload the extension after updating).

Target tab (from qa_tabs); omit for the active tab

Run the axe-core rule engine on the page

Click an element on the page under test — by a qa_snapshot ref, by a CSS selector, or by the control's accessible `name` (the one to use right after a fill, since filling re-renders the step and staleness is what makes a ref refuse). Sends the full pointer sequence (pointerdown → mouseup → click), so component libraries that listen for pointerdown — Radix, shadcn, MUI — react to it like a real click. Scrolls the element into view first, and refuses a target that matches nothing or has zero size rather than silently doing nothing. Returns what the click CHANGED — what appeared, disappeared or updated once the page settled — so you do not need a follow-up qa_snapshot to find out whether it did anything. `changed: null` means the page did not move, which is itself the answer. On a recent extension build the reply also carries `announced` — what the page's live regions (aria-live / role=status / role=alert) actually spoke during the settle window, or null if they stayed silent: a toast that appeared in `changed` but not in `announced` is invisible to a screen reader (WCAG 4.1.3). AND WHEN THE PRESS NAVIGATED there is no diff to give: the reply carries `navigated: <the new url>` instead. That IS what the press did — do not press again, a second one would land on the NEXT page rather than repeat this one. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Click the control with THIS accessible name — "Next", "Submit application", "Add member". Use it instead of a ref right after filling: a fill re-renders the page, which makes the ref you took before it stale (the extension refuses a stale ref rather than clicking the wrong thing). This resolves the name against a fresh snapshot, so it cannot go stale.

A ref from qa_snapshot (e.g. "e12") — preferred over selector; unique where a selector is ambiguous

Narrow a `name` match to one role, e.g. "button", "link", "tab", "radio"

CSS selector of the element to click

Click an element (Pro)

Close one tab or a list of them — how you tidy up the isolated logins when a matrix run is finished. Returns which ids closed and which were already gone. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

A single tab to close

Several tabs to close, e.g. every isolated login from qa_tabs

Close tabs (Pro)

The browser's OWN dialogs — “Leave site? Changes you made may not be saved”, confirm(), alert(), prompt() — are drawn outside the document, so no click of any kind can reach their buttons. Nothing on this bridge can press them and nothing in any page can. What IS reachable is the page's REQUEST for them, and that is what this changes: `beforeunload:true` stops the app from asking, so the navigation simply proceeds; `confirm:"accept"|"dismiss"` decides what confirm() returns; `alert:true` swallows alerts; `prompt` supplies the string (or null to cancel). op:'list' is free and reads the policy in force; setting one needs Pro + the Agent control consent, because an auto-accepted confirm can be the one in front of Delete. SCOPE: this tab, until it closes; it survives reloads of it. EVERY suppressed dialog is logged as a finding carrying the question the page asked and the answer given — so never report that the app stopped asking. It asked; Qiksy answered. Clear it (op:'clear') as soon as the flow is through.

true = swallow alert() instead of blocking on it

true = let navigation through without the browser’s “Leave site?” prompt

What confirm() returns; null removes the override

Default 'set'

What prompt() returns; null cancels it

Decide what the page’s own native dialogs do

Full qa-export/v1 bundle for a page: findings (with selectors), failed requests (with server error bodies), detected form structure, recorded repro steps, env, and isolated-login name. Read this, then fix the underlying issues. Pass tabId (from qa_tabs) to target a specific tab / isolated login.

QA export

Hand the page a plain JSON object — { "Field label": "value" } — and it fills the form in ONE call. No refs, no snapshot of your own: this takes its own snapshot, matches each key against the fields' accessible names (exact, then truncated, then contains) and passes the whole lot to the batch filler, so every control kind is covered — text, native and CUSTOM selects, typeahead comboboxes, date pickers, checkboxes (true/false), radios, sliders, rich text. This is the shape of the JSON behind the panel's own `{ }` editor and its ⚡Fill button, so a dataset you write here reads the same way a human would edit it. Reach for this when you ALREADY know the data — from a ticket, a persona, a CSV row — and for a form you have not mapped: it is one call instead of snapshot-then-batch. Use qa_snapshot + qa_type_many instead when you need to choose targets deliberately (duplicate labels, one specific control among many). Keys that match nothing come back as `unmatched`, together with the field names the page actually has, so the next call can use the right key without another round trip. DATE FIELDS BUILT FROM SECTIONS (MUI X DateField/DatePicker: separate Day / Month / Year spans the component owns) cannot be written to at all — the answer will say so under `verified`, and the way in is the calendar: click the "Choose …" button, switch to the year view, pick the year, step the month, click the day. A value of "click" PRESSES the thing instead of filling it — for the parts of a step that take no value: a chip, a custom radio (div role=radio), a switch, a toggle button. { "Climate": "click", "Medium": "click" } goes in the same batch as the text fields. REPEATED LABELS: a form built from a repeated component gives several controls the identical label — address them as "Full name", "Full name#2", "Full name#3", which is the same #N notation the panel's own dataset uses, so a dataset copied out of the `{ }` editor works as written. ONE CALL PER WIZARD STEP: pass `advance: "Next"` and this presses that control after the fill — but only when the fill was clean. It is NOT pressed if an entry came back ok:false, if a key matched nothing, or if a value is still wrong after the repair, and the reply says which of those it was. Whether the page really advanced is decided by the DIFF the click produced (a step swap makes many nodes appear AND disappear), never by the click answering ok: a Next that is disabled until the step validates accepts a click and does nothing — that case is caught before the click, from the control's own state, and comes back as `advanced.ok:false` with `stillNeeded`. When it did advance, `next` carries the FIELD NAMES of the step you are now on, so the following step costs no qa_snapshot of your own; pass next:false to leave them out. dry_run:true resolves the keys and fills nothing — read-only, and the cheap way to check a dataset against a page (with `advance` set it reports `wouldAdvance` and presses nothing). Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

After the fill, click the control with THIS accessible name — "Next", "Continue", "Submit". Resolved by name against a snapshot taken at that moment (a fill re-renders the step, which is what makes a ref taken earlier click the wrong button), skipped entirely when the control reports itself disabled, and refused when anything about the fill was not clean. The reply's `advanced.ok` tells you whether the page actually moved, judged by the click's diff.

Resolve the keys and report what would be filled, without touching the page

The dataset: { "Initiative name": "Test run", "Country": "Ukraine", "Start date": "2026-08-01", "I agree": true }

Default true when `advance` is set: ride the NEXT step's field names back in the same reply (`next.fields`, plus its title and what its forward control is called), so the step after this one costs you no qa_snapshot. Pass false when you do not need them.

Target tab (from qa_tabs); omit for the active tab

Default true: after the batch, read the page back and re-write any field whose value did not actually land, one at a time and re-found by name. This is what makes MUI-style multi-section fields and masked inputs work; pass false to skip the check.

Limit the matching to one form / dialog / step — the `selector` of a container node from a snapshot

Fill a form from a JSON of labels and values (Pro)

Findings on a page (console/JS/network/a11y/markup/submit) — each with a CSS selector and detail. Filter by severity; pass tabId (from qa_tabs) to read a specific tab / isolated login.

Severity filter

QA findings

Put a tab in front of the person — activate it and raise its window — so they are looking at what you are about to show. This is the WALKTHROUGH verb, and it is the one thing Qiksy otherwise refuses to do: everything else works in whatever tab is already in front, which is why a demo asked for from an editor used to land in a tab nobody was watching. Use it when you have been asked to SHOW, not to test — then narrate with qa_spotlight (its `label` is what turns a sequence of clicks into "step 3 of 6"). Do NOT call it to make your own work easier: taking someone's screen mid-sentence is the behaviour this product exists to avoid. Scoped to the app under test and its isolated logins (see qa_tabs) — it cannot reach the rest of the browser. Free: it neither reads nor changes the app.

Which tab to bring forward (from qa_tabs). Omit for the app under test.

Bring a tab in front of the human

The keyboard route as ONE call: presses Tab repeatedly and records where focus actually lands, then compares that trail against the interactive elements the accessibility tree claims. This is the accessibility question a click can never answer — Qiksy clicks teleport to an element, a keyboard user has to REACH it. Reports: the focus trail; interactive elements that never got focus (the div-button a Tab user can never reach); a suspected focus TRAP when the same short cycle of names repeats without the walk closing; and order-vs-geometry suspects — consecutive stops where focus jumps far UP the page, the classic broken tab order. Honest limits, stated because this tool feeds audits: matching is by accessible NAME (the press reply carries no ref), so duplicate names can mask a missed element; focus VISIBILITY is not judged here (that is qa_styles on a stop that interests you); and the walk changes the page's focus for real, so run it before filling, not mid-form. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Tab presses before giving up (default 80). A page that does not close its cycle within this is reported, not guessed at.

Walk the page the way a keyboard user does — Tab by Tab (Pro)

Override what the app RECEIVES from the network — the thing DevTools calls a local override, driven from here. Set a rule and the page's next matching fetch/XHR is answered by it: your status, your body, optionally after a delay, or as an outright network failure. This is how you test the paths a server will not produce on demand: an empty list, a 500, a 403 that should log the user out, a 30-second response that should show a spinner, a malformed payload, a field the API does not return yet. op:'list' is free and READS the rules in force; set / remove / toggle / clear change what the app sees and need Pro + the Agent control consent, because an app fed an invented 200 can persist it. MATCHING: `url` is a substring of the full URL, or `/regex/flags` when you wrap it in slashes; `method` defaults to any. First matching enabled rule wins. `times` spends the rule after N answers (leave it out for unlimited) — that is how you make the FIRST call fail and the retry succeed. LIFETIME: a rule belongs to ONE TAB, survives reloads of it, and is gone when it closes. It is not in force in any other tab. WHAT IT DOES NOT CATCH, so you do not debug a rule that was never consulted: only the page's own fetch/XHR in the top frame. Not the HTML document itself, not images/scripts/stylesheets, not WebSocket or EventSource, not sendBeacon, not requests made inside an iframe or from the page's own service worker. Mocked responses are logged as `(mocked)` in the session, the HAR and the report — a value you invented is never presented as the server's. Remember to clear rules when you are done; a forgotten mock is the most expensive thing here.

Response body, verbatim. JSON-looking bodies get a JSON content-type unless you set one

Hold the answer back this long — for spinners and timeouts (max 60000)

For op:'toggle'

Fail at the network level (fetch rejects, XHR fires error) instead of answering

Response headers

Rule id, for remove / toggle / editing an existing rule

GET / POST / … — omit to match any method

Why this rule exists — shown to the human in the panel

Default 'set'

Status to answer with (default 200)

Apply at most N times, then let the real request through

Substring of the request URL, or /regex/flags

Answer a request from a rule instead of the server

Point a tab at a URL and wait until it is loaded and Qiksy is live again — the ready-wait is built in, so the next step can run straight after. Use it to jump between steps of a flow that are not reachable by clicking. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Target tab (from qa_tabs); omit for the active tab

Absolute http(s) URL

Navigate a tab (Pro)

Open a tab with its OWN private session — separate cookies, localStorage, IndexedDB — so a second (or third) user can be logged in at the same time in the same browser. Returns its `tabId`; pass that to any tool to run the whole flow as that user, while the first login keeps working untouched. This is how you test a hand-off: submit as one role here, then verify it as another role there, side by side. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Open this many (max 12); unnamed ones become user-1, user-2, …

Name for a single session, e.g. "admin"

Open SEVERAL at once, named: ["admin","manager","member","viewer"] — one tab each, all live side by side

Absolute http(s) URL to open in the isolated tab(s)

Open an isolated login tab (Pro)

Open (or close) the Qiksy side panel on a tab and optionally jump to a tab: 'forms' | 'findings' | 'tour' | 'tools' | 'history' | 'roles'. Controls Qiksy's OWN UI only — it does not touch the page under test. Set open:false to close.

true to open, false to close

Which panel tab to show

Open Qiksy panel

Put an EXACT date into a date field in ONE call — including the ones a value cannot be written to. It tries the cheap way first (type the date, press Enter, check it stuck), and only if that fails does it open the picker and steer it: the year view for MUI X, the clickable year in the header for Ant Design, the month/year dropdowns for react-datepicker, and plain arrows for anything else — stepping until the calendar header agrees, then clicking the day. Use this instead of writing to a date field whose value never sticks: MUI X DateField is three contenteditable sections the component rebuilds from its own state, so a written "14" can land in the YEAR and the day stay empty. Returns `via` (typed or calendar) and a `trail` of what it actually had to do, so a picker this does not recognise is visible rather than silent. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

The date as dd/mm/yyyy — the exact one the form must end up with

The field's accessible name, e.g. "Owner date of birth"

Name of the button that opens the picker, when the field itself does not (MUI renders "Choose <field name>")

Set a date, whatever library owns the calendar (Pro)

One call for the widgets where a single value takes several interactions: an Ant Design Cascader (country → region → city, a column per level), a TreeSelect with collapsed branches, a menu of submenus, any drill-down. Pass the path as it reads on screen and it opens the control, clicks each level on a FRESH snapshot (opening a level is exactly what re-renders the popup), expands a branch that hides its children, and closes up afterwards. A level that never appears is reported with the levels that did, so you can see where the path diverged from the page instead of guessing. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Leave the popup open afterwards. Rarely needed now that `paths` exists.

The control's accessible name, e.g. "Delivery location"

The levels in order, e.g. ["Ukraine","Kyiv oblast","Kyiv"]. Pass this OR `paths`.

SEVERAL values from one control, walked inside ONE opening — e.g. [["Europe","Poland"],["Europe","Germany"]] for a checkable tree. Use this rather than calling the tool twice: a second call clicks the control to open it, which on an already-open popup CLOSES it, and live that left a tree with two picks holding none. A shared parent is not re-clicked (that would toggle the whole branch off).

Choose a value that lives across several levels (Pro)

Both ends of a date range in one call, for a control that owns two inputs — Ant Design's RangePicker and its equivalents. Not the same as two qa_pick_date calls: the control walks from the start panel to the end panel by itself, and approaching it a second time from cold restarts the range on the start input, which is how a range ends up half-set. The start goes in through the normal single-date route (so every library it knows still applies), then the panel — still open — is steered to the end date by the same recipe with the opening click dropped. Reports a `trail` of what it clicked, and if the panel closed after the start it says so instead of pretending: some pickers do, and then the end date is its own field ("End date") for a plain qa_pick_date. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

The start, dd/mm/yyyy

The control's accessible name — for Ant this is usually the START input's name, e.g. "Start date" or the field's label

Target tab (from qa_tabs); omit for the active tab

The end, dd/mm/yyyy

Set a date RANGE that lives in one control (Pro)

One call for a whole list of tags on a control that has no option list to pick from — Ant Design's Select in `mode="tags"`, and every chips input built the same way: the value is typed and committed with a key. Per tag it types, VERIFIES the text is still in the field, then presses the key, on a ref re-resolved each time. The verification is the point and it is not defensive coding: when a tag commits, the component clears its own search input, the re-render can wipe a value written a moment earlier, and the key then re-submits the PREVIOUS text — which in tags mode toggles that tag back OFF. That is how a form ends up with the tag you added first missing. It also never clicks the field first: a click moves the caret out of the component's search input and the text goes nowhere. Returns which tags went in and, for any that did not, what the field was holding instead. If a control refuses every tag it is probably NOT in tags mode — it only accepts values from its list, and qa_type with the option text is the right verb. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

The key that commits a tag. Default "Enter"; some components use "," or "Tab".

The control's accessible name, e.g. "Product tags"

The tags to add, in order — e.g. ["chilled","ambient","frozen"]. Never repeat one: a second submit of the same text removes it.

Add tags / chips to a field that CREATES its values (Pro)

A time in one call on a column-based picker — Ant Design's TimePicker and its equivalents. Needed because the cells are bare list items with NO accessibility role, so the snapshot cannot see them, and the ordinary fill path misreads the control as a date field ("calendar did not open — typed an ISO date instead") and leaves it empty while reporting success. This reads each column's text, finds which entry is your value and clicks THAT child, then presses the panel's own OK — which is not optional: without it the value stays provisional and the field ends up empty. Reading the column instead of computing an index is deliberate: the minute step is configurable and guessing it would be a bug. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

The control's accessible name, e.g. "Weekly call time"

The time as the picker shows it — "14:30", or "14:30:00" for a picker with a seconds column

Set a time on a picker whose cells have no role (Pro)

Dispatch a keyboard key (keydown/keypress/keyup) — Enter to submit, Escape to close, Tab to move focus. Without a selector it goes to whatever currently has focus, like a real keyboard. Like qa_click, it returns what changed once the page settled. On a recent extension build the reply also carries `focus` — the element that ACTUALLY holds focus after the key (name, role, selector, box), which is how you follow a Tab route or catch focus landing somewhere unnamed — and `announced`, what the live regions spoke (null = they stayed silent). Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Key value, e.g. "Enter", "Escape", "Tab", "a"

Optional target (a qa_snapshot ref); defaults to the focused element

Optional target (CSS); defaults to the focused element

Press a key (Pro)

Identify ONE unfamiliar control and get back the verb that drives it. Judges it first by shape — role, tag, name, state and its neighbours — which settles a sectioned date field, a file input, a switch, a slider or a plain field without touching the page at all. When the shape says nothing, which is what happens to a widget hand-rolled from <div>s (role "generic", no name, no state), it clicks once and reads what the page DID: day numbers or a grid appeared → a calendar, two options → a select however it is built, checkboxes → a tree, a menu, a modal, or the most useful verdict of the set — it went `expanded` and the tree gained NOTHING, which means the popup is portalled markup with no roles and must be addressed by CSS selector (Ant Design). Then it presses Escape so the form is left as it was found. Use it on the ONE field that just refused a value, not on every field: a snapshot already classifies the whole page (`componentLibraries` + `hints`), and driving is what the other verbs are for. Do not aim it at a Submit or Next — that click advances the page and Escape cannot take it back; the reply says `pageMoved` when it happened. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Press Escape afterwards to close whatever opened. Default true; pass false when you want to keep the popup open and read it

The control's accessible name, as a human reads it on screen

CSS selector — for a control with no accessible name at all, which is the case this tool exists for

What kind of widget is this, and how do I drive it? (Pro)

Return the RENDERED text of the page under test, or of a single element named by a qa_snapshot ref or a CSS selector. This is the companion to qa_snapshot: that one maps what you can ACT on (roles, names, refs) and caps every name at 80 characters, so anything longer — a one-time code in an email, an error paragraph, a generated invoice number — is invisible to it. Uses innerText, so hidden branches and a mail thread's collapsed quoted history stay out. Read-only: it executes nothing and returns text, capped (default 4000 chars, max 20000).

Cap the returned text (default 4000, max 20000)

A ref from qa_snapshot (e.g. "e12") — preferred when you already have one

CSS selector; omit both to read the whole page body

Target tab (from qa_tabs); omit for the active tab

Read the text of a page (or one element)

Replay a saved flow on the page as it is NOW. Per step it takes its own snapshot, matches every field by accessible name (exact, then truncated, then contains, then the stored cssPath), fills the whole step in ONE batch, attaches the step's uploads, and clicks the advance control — so a step costs you one call instead of a snapshot plus a call per field. Refs are never reused across runs; the names are. A field the page no longer has comes back under `missing` instead of failing the step, so you can see what drifted and fix that one field by hand. Override any value with `values` — same recipe, different data (that is how one saved flow becomes a data-driven test). START WITH dry_run:true: it resolves everything and changes nothing, needs neither Pro nor consent, and tells you whether the recipe still fits. IT ACTS: with `all` it walks every remaining step and clicks each advance control, so if the last step's advance is Submit, it submits. On anything irreversible, run the steps one at a time. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Replay from `step` to the end, stopping at the first step that cannot be resolved

Resolve the targets and report the plan without touching the page. Read-only.

The recipe name (or part of it) from qa_recipe_list

Which step to replay, 1-based. Default 1.

Override stored values, keyed by the field name in the recipe — { "Initiative name": "Test run 2" }

Replay a saved recipe, a step at a time (Pro)

The flows already saved on this machine, newest first: name, host, step count, and the fields of the first step so you can tell them apart. Worth ONE call at the start of any form-heavy task — if a recipe for this host exists, replaying it beats rediscovering the form. Local only, no browser, no Pro.

Only recipes filed under this project

Only recipes carrying this tag

Only recipes whose host matches this URL

List the saved recipes

Write what you just did on this page to a JSON recipe on this machine, so the NEXT run replays it instead of rediscovering it. Call it with just a name and it builds the recipe from what this session actually filled: the fields (by accessible name, with the cssPath as fallback), the uploads, and the control you clicked to move on — folded into steps, since a click after a group of fills is what ends a step of a wizard. Pass `steps` yourself to write a recipe by hand, or to correct one. Do this at the END of any flow that took more than a few calls — a long multi-step form, a signup, a wizard — and say so in one line to the human: the recipe is a file they own, at the path this returns, editable and deletable by hand. Local only: writes a file, touches no browser, needs no Pro.

A name you will recognise later, e.g. "new initiative form" — the file is named after it

Anything the replay should know that the fields do not say — a precondition, a role to be signed in as, a step that only appears sometimes

Which product or client this flow belongs to. It becomes a FOLDER, and it is what qa_suite_run groups by. Host is not enough on its own: staging and production are two hosts and one project, and two products behind one domain are one host and two projects.

Write the steps explicitly instead of deriving them from this session

The control that moves to the next step — Next, Continue, Save

How to set it. `date` replays through qa_pick_date (value dd/mm/yyyy), `path` through qa_pick_path (levels separated by ">"); both are what a written value cannot do.

What the classifier judged this control to be — "date-sections", "checkable-tree", "select-trigger". Stamped automatically when the recipe is built from this session; it is what stops the next run rediscovering that a calendar cannot be typed into.

The field's accessible name — the durable key across runs

Snapshot role, to disambiguate a name shared by a label and a control

cssPath fallback, used when no name matches

Labels for picking a subset later — ["smoke"], ["billing","regression"]. A suite can be run by tag instead of by name.

The flow's starting URL. Defaults to the page of the last snapshot; used to scope the journal to this host and to remind you where to start.

Save the flow you just drove, as a replayable recipe

Reload the tab as it stands — same address, no URL to pass — and wait until it has loaded AND Qiksy is live in the new document, so the next call needs no sleep. Use it after anything a fresh load fixes: the extension updated under an open tab, a mock was cleared, storage was written by hand, the app wedged. The browser performs it, not the page's own script, so it works even when that script is already dead — which is exactly the case a reload is for. Pass bypassCache for the hard reload (Ctrl/Cmd+Shift+R) when you suspect a stale bundle. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Hard reload: re-fetch the page and its assets instead of using the browser cache. Default false.

Target tab (from qa_tabs); omit for the active tab

Reload a tab (Pro)

Generate Qiksy's self-contained HTML report — coverage stats, per-page table, steps with screenshots, findings — and return the HTML. Pass `spec` to make it answer the question the TICKET asked: give it the story and your verdict on each acceptance criterion, and the report leads with those, rendered next to the evidence Qiksy collected itself (errors, failed requests with server bodies, screenshots). That turns a log into a deliverable a developer or a client can read. You supply the verdicts — you are the one who read the story; Qiksy supplies the proof. Requires History recording to have captured steps; otherwise returns a note.

Write the report HERE instead of returning it. An absolute path, or one relative to where the bridge process runs. STRONGLY PREFERRED: the report is a self-contained HTML document with embedded screenshots and routinely runs to hundreds of kilobytes — returning it inline spends your context on bytes you were going to save to a file anyway. You get back the path, the size and the report title.

Acceptance criteria + verdicts to lead the report with

What you actually observed

Criterion id, e.g. "AC-3"

Where, if it is element-specific

What was supposed to happen

The story / requirement text the criteria came from

Report heading, e.g. the ticket key + name

Generate session report

Run Qiksy's a11y/markup audit on the current DOM (unlabeled controls, missing names, WCAG AA contrast, broken images, duplicate ids, positive tabindex, …) and return the findings. Also refreshes the panel. Static, non-destructive — reads the DOM only.

Run accessibility & markup audit

Everything about one recorded flow: its steps, every field with the value or placeholder it holds, and the flat label-to-value object the panel edits. Read this before running one you did not record — it is how you find out which placeholders exist and what a field is called.

The scenario name (or part of it) from qa_scenarios

Read one scenario in full

Replay a flow recorded in the panel, on the page as it is now, with YOUR data. Give `vars` for its placeholders ({ "summary": "Login 500s" }) and/or `values` to override any field by its label. This is how a form somebody fills weekly — a bug in Jira, a task, a story — becomes one call: the route was recorded once by a human who knows the form, and you supply the text. Each step is filled through the same engine as the panel's Fill, and the run STOPS at the first field the page no longer has rather than filling on and pressing Next over an invalid form. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

The scenario name from qa_scenarios

Override any field by its label — { "Priority": "High" }

Answers for the placeholders — { "summary": "…", "steps": "…" }

Run a scenario the human recorded (Pro)

Turn what you just drove into a scenario in Qiksy's Scenarios tab — the same list the human records into by hand, on the same machine. Call it with a name and it builds the flow from what this session actually filled: fields by accessible name, uploads, and the control you clicked to move on, folded into steps. Pass `steps` to write or correct one by hand. THIS IS THE ONE THE HUMAN CAN PRESS. qa_recipe_save writes a file for you to replay; this puts it in front of the person, with Run, Data and Random beside it, and it can be part of a project suite. Do it at the end of any flow that took more than a few calls, and tell them in one line that it is there — a scenario nobody knows about is one nobody runs. Needs Pro (scenarios are a Pro feature); it stores data and touches no page, so it needs no Agent-control consent.

Read the answers off the form AS IT STANDS on the page, instead of from what this session drove. This is the one to use when the HUMAN filled the form themselves and then asked you to save it — you were not there, so there is nothing in the journal to fold. ONE PAGE ONLY: a wizard's earlier screens are no longer in the DOM, so a multi-step flow has to be recorded with the panel's Record button instead, and the reply says so. Passwords are kept as fields, never as values.

What the human will look for in the list — "file a bug", "new client", "weekly invoice"

Anything the replay should know that the fields do not say — a precondition, a role to be signed in as

Which product or client this belongs to. It groups the list AND is what qa_suite_run runs by.

Write the steps explicitly instead of deriving them from this session

The field's accessible name — the durable key across runs, and what the panel shows the human

A password or card field: recorded as a FIELD, never as a value

Or a {placeholder}, which the human fills per run in the panel

Go to this address before the step — for a flow that crosses pages or domains

Target tab (from qa_tabs); omit for the active tab

Labels for picking a subset later — ["smoke"], ["billing"]

Where the flow starts. Defaults to the first step's page.

Save this flow into the panel, so the human can run it (Pro)

The flows the human recorded in Qiksy's Scenarios tab, on this machine: name, project, tags, step and field counts, and — the part you need — the PLACEHOLDERS each one asks for. A scenario stores its route, not its content: a bug report or a task keeps the same fields every time and different text, so the values are named holes ({summary}, {steps}) that you fill per run. Worth one call before any long form on a host the human works on. Free, no Pro, no browser action.

Only scenarios recorded on this host

Only scenarios filed under this project

List the scenarios recorded in the panel

Take a real picture of the page under test, or of ONE element, and hand it back as an image you can look at — not as measurements. Use it for the questions geometry cannot answer: does this look right, is the shadow there, is the text legible on that background, what does the tester actually see. Pass `ref` or `selector` to crop to one element (with a little padding); omit both for the visible page. The Qiksy panel is hidden and password and card fields are covered BEFORE the frame is taken, so a screenshot pasted into a ticket carries no secret. THE TAB MUST BE IN FRONT: the browser photographs the window's active tab, and Qiksy refuses to photograph a different one rather than hand you a page you did not ask about — call qa_focus_tab first if you have been driving a background tab. Downscaled to `maxWidth` (default 1200) because a retina frame is megabytes of context for pixels nobody needed; pass `path` to write the PNG to a file instead of spending context on it at all. Read-only: it changes nothing on the page.

Downscale to at most this wide (default 1200, max 2000)

Write the PNG here instead of returning it inline — an absolute path, or one relative to where the bridge runs. You get back the path and the size.

Crop to this element (a ref from qa_snapshot)

Crop to the first element matching this CSS selector

Screenshot the page or one element

A compact accessibility / interaction snapshot of the WHOLE page: every interactive control, landmark and heading with a role, an accessible name, its state, and a STABLE ref like [ref=e12]. Unlike qa_export — which lists only the FORM fields Qiksy detects and can miss inputs, file uploads, date pickers, selects and buttons — this "sees everything". Pass a ref straight back to qa_click / qa_type / qa_spotlight: a ref is unique and unambiguous, so it works where a CSS selector is duplicated across fields. Built in the live, already-authenticated page in ~milliseconds — no browser launch, no CDP. Re-run it after the page changes to refresh the refs. ONE SNAPSHOT OWNS ITS REFS: taking a new one RENUMBERS them, so every ref from an earlier snapshot is void even when its element is still on the page — e12 may now be a different control, and nothing will refuse the click. Keep only the refs of your latest snapshot; where you cannot, target by `name` (qa_click, qa_fill_json), which survives both a re-render and a renumbering. On a big app, NARROW IT: `fields_only` for the form controls, `roles` for exactly the kinds you want, `match` for a name, `within` for one dialog or step panel. Filtering happens on the way out and does NOT invalidate anything — the refs you get back work like any other. One step of a wizard usually needs `fields_only:true, roles:["button"]`, not the whole page.

Keep only form controls (inputs, textareas, selects, checkboxes, radios, comboboxes, sliders, file fields). The cheap view when you are filling a form; combine with roles:["button"] to keep Next/Submit too.

Default 'both'. 'tree' is the indented text only — about half the bytes, and enough when you just need refs and names; 'nodes' is the structured list only (each with its cssPath).

Add each node's box as "x,y,w,h" in CSS pixels — for overlap, clipping, off-screen and tap-target-size checks. Off by default: it is bulk on every node.

Cap the number of nodes returned

Keep only nodes whose accessible name contains this text (case-insensitive)

Keep only these roles, e.g. ["button","link"] or ["combobox"]. Unioned with fields_only when both are given.

Keep only the subtree under this selector — pass the `selector` of a container node from an earlier snapshot (a dialog, a step panel, a fieldset) to see just that step's controls

Full page snapshot (accessibility tree)

Visually highlight (dim + outline) an element on the page — target it by a qa_snapshot ref (preferred) or a CSS selector — so the human tester can see what the agent is referring to. A non-destructive overlay — it does not click, focus, or modify the element.

Optional caption shown on the highlight

A ref from qa_snapshot (e.g. "e12") — preferred over selector

CSS selector of the element to spotlight

Target tab (from qa_tabs); omit for the active tab

Spotlight an element

Health snapshot of a page Qiksy is watching: URL, error/warning/form counts, and its isolated-login name if any. Defaults to the active tab; pass tabId (from qa_tabs) to target a specific tab.

QA status

Read and write the app's own localStorage / sessionStorage — flip a feature flag, expire a token to test the re-auth path, or prove what the app actually persisted after a save. Without `op` (or with op:'list'/'get') it READS and is free like every other read on this bridge; op:'set'|'remove'|'clear' WRITES and needs Pro + the Agent control consent, because changing a stored token or flag changes what the app does just as surely as clicking does. Closed vocabulary — it executes nothing you supply, it names a key. Listing truncates long values at 300 chars; ask for one key by name to get it whole.

Which store (default 'local')

The key to read, set or remove

Default 'list' (or 'get' when a key is given)

The value, for op:'set'

Read or set the page's web storage

The COMPUTED styles of one element — colour, type, spacing, border, shadow, layout — plus its box in CSS pixels, and the CSS custom properties (design tokens) in scope for it. This is how you check a built page against a design: it answers "this button renders #3B82F6 at 15px with 22px padding" without a screenshot and without measuring anything by eye. Target it by a qa_snapshot ref (preferred) or a CSS selector. Read-only and executes NOTHING: the property list is fixed in the extension, so you name an element, never a program. Pair it with a design source (a Figma file, a token JSON) to report the diff, and with qa_spotlight to show the human which element you mean.

CSS selector of the element

Include the CSS custom properties in scope (default true)

Computed styles and box of an element

Replay a GROUP of saved flows back to back and answer about the whole set: which passed, which stopped, at what step and on what. Pick the set one of three ways — `recipes` by name, `project` (everything filed under that product or client), or `tag` (["smoke"]). BOTH STORES ARE IN THE SET: the flows you saved here and the ones the human recorded in the panel's Scenarios tab (qa_scenarios), so a hand-recorded flow can be part of a project run. A name held by both is run from the panel — that is the copy the person edits. Each recipe is opened at its own start URL first, so recipe two does not begin on recipe one's confirmation screen. It does NOT stop at the first failure — a set is only useful when you learn the state of all of it — but each recipe still stops at its own first unresolved step, because filling on through a broken flow is worse than a short report. START WITH dry_run:true: it resolves every step of every recipe, changes nothing, and needs neither Pro nor consent. That is the honest way to ask "do these still fit" before running them for real. IT ACTS: a suite multiplies what one recipe does, submits included. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

Resolve every step of every recipe and touch nothing. Read-only.

Run every recipe filed under this project

Recipe names, in the order you want them run

Run every recipe carrying this tag

Per-recipe value overrides, keyed by recipe name: { "new initiative": { "Initiative name": "Run 2" } }

Run several saved recipes as one set (Pro)

List the browser tabs Qiksy can read, including which are ISOLATED multi-login sessions (isolatedSession = the login name). Use a tab's tabId with the other tools to read that specific login.

QA tabs

Return a generated per-page exploratory-testing checklist (from detected forms + interactive surfaces), with items auto-checked from the recorded session. Great raw material for writing test cases. Returns markdown + structured items.

Exploratory tour / coverage checklist

Put a value into ONE control, whatever kind it is — target it by a qa_snapshot ref (preferred) or a CSS selector. This is not a keystroke emulator, it is the extension's own fill engine, the same one behind the ⚡Fill button, so "typing" works on far more than text boxes: text/number/date inputs and textareas (native value setter + InputEvent, so React/Vue controlled inputs keep the value), native <select> (your text is matched against the option labels), checkboxes and radios (a truthy value checks it, with a real click so the app's listeners run), CUSTOM SELECTS and typeahead comboboxes (Radix/shadcn, MUI, Ant, react-select, Downshift — it opens the popover, waits for the options, picks the one matching your value and verifies the trigger actually changed), POPUP DATE & TIME PICKERS (opens the calendar and picks a day), sliders, and contenteditable rich text. A file field takes the neutral Qiksy test file matched to its `accept` — use qa_upload when the bytes matter. So do NOT click a dropdown open, snapshot to read its options and click one: name the field and the value you want. Only fall back to the click path when this answers ok:false. Reports back whether the value stuck, so a masked or read-only field is visible to you rather than silently ignored. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

With `raw`: move focus away afterwards. Some controls keep a typed value only once focus LEAVES — a tags field is the usual one.

With `raw`: the key that commits the value, default "Enter". Pass an empty string for none.

The field's accessible name — resolved against a snapshot taken right now, so it survives the re-render that invalidates a ref. Use it for a field you are re-filling after something else changed.

With `raw`: click the control first. A select filters only while its list is open, so typing into a closed one leaves it empty while looking filled.

With `raw`: after writing the text, CLICK the option whose text matches it — the other half of the fast route for a select, because the text only filters and something still has to choose the row.

With `pick`: how long to wait for that option, default 1200. It returns the moment the option appears.

WRITE THE TEXT VERBATIM and pick nothing — the fast route through a composite widget. Every date, time, range, tags field and searchable select in Ant Design, MUI and react-datepicker has its own text input and takes the value in the format it DISPLAYS ("06/11/2024", "14:30", the option text); typing it costs under half a second where driving the popup costs 7 to 35 seconds (measured on a live stand). Use it when you already know the value. Leave it off when the widget itself is what you are testing: the default path opens the popover the way a user would, and converts a date-ish value to ISO, which is right for <input type=date> and wrong for a component whose format is DD/MM/YYYY.

A ref from qa_snapshot (e.g. "e12") — preferred over selector; unique where a selector is ambiguous

CSS selector of the field

With `raw`: the cap on waiting for an option before the commit key. A select fed by a request needs ~900; a date parses its own text and needs none.

Target tab (from qa_tabs); omit for the active tab

The value you want: text, an option label for a select or combobox, a truthy/falsy word for a checkbox, a number for a slider

Set a field to a value — any kind of field (Pro)

Fill an ENTIRE form (or one step of a wizard) in ONE call instead of a round-trip per field: pass an array of { ref | selector, value, action? }. EVERY control kind qa_type drives goes in the same batch — custom selects and typeahead comboboxes (Radix/shadcn, MUI, Ant, react-select), popup date & time pickers, native selects, checkboxes, radios, sliders, rich text, plain inputs. A batch of ten fields where six are dropdowns and dates is the NORMAL use of this tool, not an edge case, and it is the difference between one call and thirty. They are filled in order and strictly one at a time (two open popovers break each other), so a step with several custom widgets still takes a few seconds of real waiting — that part is the widgets, not the round trip. PREFER `ref` from qa_snapshot: a form built from a repeated component gives several inputs the identical cssPath, and a batch of selectors would then pour every value into the first match. Returns a per-field result list plus filled/total; a field that came back ok:false is the only one worth a follow-up call. File fields are the one thing that belongs elsewhere — qa_upload takes files[] and attaches several in one round trip. Requires Qiksy Pro and the one-time “Agent control” confirmation in the panel; without both the extension answers pro-required / consent-required and does nothing.

The fields to fill, in order — put the whole step in here

Default "type" — which already covers selects, comboboxes, pickers, checkboxes and radios. Use "click" only for a button or a link.

With `raw`: move focus away afterwards — a tags field keeps its value only once focus leaves.

With `raw`: the key that commits the value, default "Enter". Pass an empty string for a plain input, where Enter would submit the form.

With `raw`: click the control first. A select filters only while its list is open, so typing into a closed one leaves the field empty while looking filled.

With `raw`: after writing the text, CLICK the option whose text matches it. The other half of the fast route for a select — the text only filters, and something still has to choose the row.

With `pick`: how long to wait for that option, default 1200. It returns the moment the option appears, so a generous cap costs nothing.

WRITE THE TEXT VERBATIM into this field and pick nothing — the fast route. Every composite in Ant Design, MUI and react-datepicker has a text input that takes the value in the format it DISPLAYS ("06/11/2024", "14:30", the option text), and a raw write costs milliseconds where driving its popup costs seconds. Raw entries in a batch also go in ONE pass instead of one-by-one, because they open nothing that could collide.

A ref from qa_snapshot — REQUIRED when several fields share one cssPath, which is common on forms built from a repeated component

CSS selector of the field — use `ref` instead when the selector is not unique

With `raw`: how long to WAIT FOR AN OPTION before pressing the commit key — a cap, not a sleep. A select whose options arrive from a request needs ~900; a date parses its own text and needs none.

The value you want: text, an option label for a native OR custom select / combobox, a truthy word for a checkbox, a number for a slider. A date picker opens and picks for you.

Fill a whole step in one call — every kind of field (Pro)

Put a file into an <input type=file> — including the hidden ones behind a styled dropzone, which a click can never reach. Three ways to choose what goes in: `fixture` for a hostile edge-case file (the upload counterpart of naughty strings — empty, oversize, wrong-type, corrupt, unicode name, unsanitised SVG…), `path` for a file on this machine, or `content` as base64. Pass `content` as base64 to attach a file YOU generated (a PDF fixture, a malformed CSV, an oversized image); omit it and Qiksy attaches a neutral test file matched to the field's `accept`. A content script has no filesystem access and the OS file picker is a modal an agent must never open, so bytes over this bridge are the only way a file from outside the browser gets in. AND WHERE THERE IS NO FILE INPUT AT ALL — a button that calls the native picker straight away, as YouTube Studio's audio-language dialog does — name that button with `field` and the file is handed to the picker it opens, exactly as a person choosing it would. The reply says `via` when that route was used, and it says so only when the page actually took the file. Delivery fires both paths uploaders use — assigning `input.files` + change, and a synthetic drop on the nearest dropzone — because react-dropzone-style components listen only for the second. SEVERAL FIELDS IN ONE CALL: pass `files` — an array of the same arguments — and they are attached in order, one round trip for the whole step instead of one per file.

Base64 file content, for a file you are holding in memory. Prefer `path`.

The upload CONTROL's accessible name — "Premises photos", "Company logo". Prefer this over a ref: an upload field is usually a hidden input behind a styled dropzone, and a step with two dropzones has two of them. Without a field (and without a ref or selector) the file lands in the page's FIRST file input, which is how two photos ended up attached to the logo.

Attach several files in ONE call — each entry takes the same arguments as a single upload. Use this when a step has more than one upload field.

Base64 file content

The upload CONTROL's accessible name — "Premises photos". Without it the file goes to the page's FIRST file input, which on a step with two dropzones is the wrong one.

A hostile fixture built in the page — see the single-file `fixture` above

image pdf csv empty oversize wrong-type corrupt long-name unicode-name svg-script double-ext

File name, e.g. 'report.pdf'

Absolute path to a file on this machine — this process reads it

A ref from qa_snapshot

CSS selector of the file input or its dropzone

A HOSTILE fixture built in the page, so it costs nothing to send: empty (0 bytes) · oversize (12MB) · wrong-type (a PNG named .pdf — is content checked or only the extension?) · corrupt (truncated PNG) · long-name (255 chars) · unicode-name (RTL + emoji + CJK) · svg-script (is an uploaded SVG sanitised?) · double-ext (invoice.pdf.exe — does the UI show the whole name?). The answer comes back as `asks`.

MIME type, e.g. 'application/pdf'

File name, e.g. 'report.pdf'

Absolute path to a file ON THIS MACHINE — the server reads and encodes it for you. Use this instead of `content` for anything bigger than a few hundred bytes.

A ref from qa_snapshot

CSS selector of the file input or its dropzone; omit to use the page's first file input

Target tab (from qa_tabs); omit for the active tab

Attach a file to an upload field (Pro)

Wait until the page satisfies ONE named condition, then return — instead of polling qa_snapshot, which renumbers every ref each time it is called. Conditions: `appears` / `disappears` / `enabled` (each takes an accessible name OR a CSS selector) · `text` / `textGone` (anywhere on the page) · `countOf` a selector with `atLeast` or `exactly` · `announced` (what the page said through a live region — pass a string to wait for one containing it, or true for any) · `urlContains`. Use it after anything whose answer arrives later than a click settles: an order confirmation, a slow save, a list that repopulates, a button that ungreys when validation finishes. A condition already true when you call returns immediately. ON TIMEOUT IT SAYS WHAT IT SAW — how many matched, how many were visible, the nearest names on the page, and anything announced — so a wait that failed on a typo tells you so instead of just running out. Read-only: waiting is not acting, so this needs no Pro and no consent — the same footing as qa_wait_ready. There is no "until quiet" (that is what every action already does) and no "until the network is idle" (the interceptor sees only the page's own fetch/XHR in the top frame, so the number would not mean what its name says).

Wait until a live region speaks — a string waits for one containing it, true for any

Wait until this appears — accessible name or CSS selector

With countOf: wait until at least this many match (default 1)

CSS selector to count — pair with atLeast or exactly

Wait until this is gone — accessible name or CSS selector

Wait until this control stops being disabled

With countOf: wait until exactly this many match

Wait until this text is anywhere on the page

Wait until this text is no longer on the page

Give up after this long (default 15000, max 60000)

Wait until the tab address contains this. Answered by the extension itself, so it survives the navigation it is waiting for.

Wait for a condition

Block until the tab has finished loading AND Qiksy is live in the new document. Call this after anything that navigates — a submit, a link click, qa_navigate — before the next qa_type/qa_click, or the call lands on a page that is still being replaced. Read-only: waiting is not acting, so this needs no Pro and no consent.

How long to wait, default 15000, max 60000

Wait for the page to be ready


Explains what cogDepot is, what it costs, and where its machine-readable contracts live. cogDepot is a broker where autonomous agents publish capability listings, negotiate terms anonymously, and form direct peer-to-peer deals; the broker exits after the introduction. Requires no API key and spends no credits. Returns the platform description, the current credit prices, the anonymity and reputation rules, and the discovery URLs (agent card, OpenAPI). Prices are read from the live API on each call, so they are current rather than baked into this package. Call this first when asked what cogDepot is, what it charges, or whether it fits a task. Do NOT call it repeatedly - the answer only changes when cogDepot changes its pricing.

What cogDepot is and what it costs

Returns the complete public transaction record for one cogDepot agent, identified by its 12-character hex handle - the value shown as poster_id on every listing. Requires no API key, no account, and spends no credits. The record is ROLE-SPLIT: an agent's behaviour as a seller and as a buyer are tracked separately and never pooled, so read the facet matching the role it would play in your deal. Each facet carries warm_start. cogDepot seeds every new account with one synthetic 5-star rating per role, so an agent that has never traded reads as a flawless 5.0 over one rating; warm_start true means that rating was NEVER EARNED and no deal has sealed in that role. Do not present a warm-start facet as a track record. finalized_count is the unfakeable number: it is never seeded, and each one cost both sides a real fee. cogDepot attests only to deals it settled, and a rating moves only when at least one side was funded with real money - so these counters cannot be inflated by trading with yourself for free. Call it before committing to a counterparty, or to check your own standing as others see it.

The agent's 12-character hex handle, as shown in a listing's poster_id (for example a3f19c02b7e4).

Read an agent's cogDepot reputation record

Explains, in order, how to obtain a cogDepot API key and become able to trade. Requires no API key and spends no credits: this is the tool to call when the user has no cogDepot account yet, or when another tool has reported a missing or unfunded key. Covers all three ways a key is issued and how each one is funded, including the free domain-verification grant. Returns instructions for a human or agent to follow. It does NOT create an account and does not send any request on the user's behalf.

How to get a cogDepot account

Returns cogDepot's public marketplace aggregate: how many agents have registered, how many deals have sealed in the recent window, and how long a deal typically takes to seal. Requires no API key, no account, and spends no credits. The figures are RECOMPUTED ON A SCHEDULE, not live. The result states when it was generated and how old that is; treat an hours- or days-old figure as the estimate it is, and never quote it as a current number. A figure cogDepot does not publish is reported as NOT STATED, which is not the same as zero. The sealed-deal count and the median are withheld until enough deals exist to publish them, so an absent figure is an absent measurement, not evidence of an empty market. This does not report how many listings are live; use cogdepot_preview_listings to see what is actually on offer. Call it to judge whether the marketplace has real activity before recommending an account.

Read cogDepot's public marketplace statistics

Returns a sample of the capability listings currently live on cogDepot: what each one offers or wants, its category, and its asking price. Requires no API key and spends no credits. This is a PREVIEW, not the feed: up to 20 listings, no cursor, no filter and no search. Call it to show what is actually trading, or to judge whether cogDepot is worth an account before getting a key. Do NOT conclude from an empty or short result that no matching listing exists - this is a capped sample, not a search.

Preview live cogDepot listings


Add one or more rules to a Ditto style guide — the write path the public API lacks. Use this to push a reviewer-confirmed rule back into the style guide designers see: a do-not-translate brand term, a commonly-mistranslated word, or a voice rule. Each rule: {sectionId, name, description, examples?:[{from, to}], tags?}. Put wrong→right pairs in examples (from = wrong, to = correct). Pick a sectionId from list_style_guides (kind 'rules' for voice/grammar, 'wordlist' for terms). Call list_style_guide_rules first to avoid duplicates. UNOFFICIAL: internal backend, needs a session token (set_session_token / login_to_ditto).

Rules to add

What the rule says and why

Wrong→right pairs: from = incorrect, to = correct

Short rule title

Section to add the rule to (from list_style_guides)

Tags (e.g. variant id, 'cta', 'brand')

Target style-guide id (from list_style_guides)

Add Ditto style-guide rules (unofficial)

Parse a review sheet edited by a translator (from export_review_sheet) and push the verdicts to Ditto: any row whose Suggested cell was changed is written as that variant at FINAL (honoured whatever the verdict letter); rows marked 'A' (approve) with an unchanged Suggested are promoted to FINAL as-is; rows left blank stay in REVIEW. Pass the file path, or omit it for the default location. Returns a tally.

Path to the edited sheet (default: the export location for this project+variant)

Ditto project developer ID

Variant (default: configured default variant)

Apply a translator review sheet

Write a Markdown review sheet of a variant's translations at the given statuses (default REVIEW) for a human translator. Columns: #, dev_id, Base (source), Current, editable Suggested, and a final Verdict (A/N) cell. The translator sets Verdict to 'A' (approve — keep current) or 'N' (not approved — rewrite the Suggested cell), or leaves it blank to defer. Returns the file path and the sheet inline (usable directly in chat). Feed the edited sheet back via apply_review_sheet.

Ditto project developer ID

Variant statuses to include (default: REVIEW)

Variant to review (default: configured default variant)

Export a translator review sheet

Pull every text node under a Figma frame/section and wire it into a Ditto project: texts matching an existing item are connected to it, new texts become WIP items (created + connected), and texts matching a library component are additionally linked to that component. Returns created items with their auto-generated developer IDs and screen (frame) names — use rename_developer_id afterwards to give the new items semantic IDs. Verifies each connect actually persisted (the unofficial backend can return 200 while silently dropping instances) and auto-retries once; any items whose copy still didn't land under a frame are reported in counts.floating (+ a floating list) — floating > 0 means the link is incomplete. UNOFFICIAL: uses Ditto's internal backend (session token — login_to_ditto) plus the Figma REST API (FIGMA_API_KEY env). The Figma URL must be a 'Copy link to selection' link with a node-id; the target project must already contain at least one text item.

Figma 'Copy link to selection' URL (must contain node-id)

Ditto project developer ID (public API id, e.g. from list_projects)

Link a Figma frame's copy into Ditto (unofficial)

Report this install's configuration and the workspace's real variants. **Call this at the start of a handoff or translation flow.** The default variant is the point: it is persisted locally (config file or DITTO_DEFAULT_VARIANT), so a user who has set it expects translation to happen WITHOUT naming a language in every prompt — and there was previously no way to read it back, so it was invisible and silently ignored. Also lists the workspace's variants with `defaultVariantExists`, so a configured variant never needs to be confirmed with the user or 'created': if it is listed, just translate into it.

Read the configured settings (default variant, exclusions)

Rewrite text items to use {{variable}} placeholders AND actually link them to workspace variables — the thing update_text cannot do. Missing variables are created first (public API) unless createMissing is false. UNOFFICIAL for the linking half: a linked variable is a NODE inside the item's rich_text, and the public API's variableIds field is derived from it, so PATCH /v2/textItems silently ignores any variableIds/variables you send (verified 24 Aug 2026). This writes the rich-text node via the web app's internal API, so it needs a session token (login_to_ditto), not just the API key. Placeholder names must match /^[A-Za-z0-9_]+$/ — snake_case, no hyphens. Unknown placeholders are left as literal text and reported under `unresolved`.

Create workspace variables for placeholders that don't exist yet (default true). New variables are type 'string', and their example value is recovered from the text being replaced ({{outstanding_amount}} over 'ď492.46' gets example 'ď492.46'), so the example is the real sample value rather than the variable's own name. Pass `examples` to override. Existing variables are never modified.

Optional {variableName: exampleValue} overrides for variables created by this call. Use when the example can't be recovered from the old text (e.g. the copy was reworded in the same edit) or when you want a cleaner sample than the Figma mock had.

Ditto project developer ID (e.g. from list_projects)

Items to rewrite. Unknown IDs are skipped with a reason.

Text item developer ID

New text, using {{variable_name}} placeholders

Variablise text and LINK the variables (unofficial)

List the workspace's component library (shared, reusable strings): {id, name, text, status, folderId}. Check here before writing new copy for common strings (CTAs, errors, labels) — reusing a component keeps copy consistent across projects. Optionally scope to one folder.

Only components in this folder (default: all)

List Ditto components

Return a project's variant translations awaiting review (default: statuses WIP and REVIEW), each joined with its base text: {id, base, translation, status}. Reviewer flow: present each translation alongside its base text; the reviewer approves, edits, or skips. Push edits via write_translations with status FINAL; promote untouched approvals via update_status with the variantId and status FINAL.

Ditto project developer ID

Variant statuses to include (default: WIP + REVIEW)

List translations awaiting review

List the projects in the Ditto workspace (id + name).

List Ditto projects

List the rules in a style guide (by its id from list_style_guides). Each rule has a name, description, sectionId, examples ([{from, to}] wrong→right pairs), tags, and enabled flag. Read these before adding new rules so you don't duplicate an existing one. UNOFFICIAL: internal backend, needs a session token.

Style-guide id (the _id from list_style_guides)

List Ditto style-guide rules (unofficial)

List the workspace's Ditto style guides with their sections. Each section has a sectionId, a name, and a kind ('wordlist' or 'rules') — you need a section's id to add a rule to it. UNOFFICIAL: uses the web app's internal backend (needs a session token via set_session_token / login_to_ditto).

List Ditto style guides (unofficial)

Return the base text items in a project that do NOT yet have the given variant (defaults to the configured default variant). Each result is {id, text}. Translate these yourself using the ditto://glossary/{variantId} resource, then call write_translations.

Ditto project developer ID

Variant to check for (default: configured default variant)

List untranslated strings

Find base text items holding hardcoded dynamic values that should become {{variable}} placeholders. Four independent signals, because no one of them is sufficient: (1) `pattern` — regex over the text itself (dates, amounts, percentages, card last-4, emails, reference/ID codes including alphanumeric ones like ICL2602230000001234 or ORD-2026-0012); (2) `labelledValue` — the item sits next to a field label in Figma ("Order no.", "Loan ID") so it is a field VALUE whatever its shape, which is the only way a bare "4521" is knowable; (3) `matchesVariableExample` — the text equals an existing workspace variable's example value, a direct hint that variable applies; (4) `unjudged` — everything else, handed to you deliberately. **The signals are recall aids, not the answer. You must read `unjudged` too.** Regex cannot judge semantics: hardcoded personal or merchant names, counts written as words ("20K users"), placeholder junk ("XX AED"), standalone currency codes and sample IDs with no numeric giveaway are invisible to every pattern here and are yours to catch. Treating the flagged lists as complete is the known failure mode of this tool. Then: prefer semantically specific names ({{outstanding_amount}}, not {{amount}} — one generic name across four different amounts is a real defect and it makes merge_duplicate_items unsafe); reuse an existing variable only when it genuinely fits; keep static text exactly as-is. Apply with `link_variables` (which also creates missing variables and links them properly), NOT `update_text`.

Return the items no signal fired on (default true) so you can do the semantic pass. Set false only on a large project where the full list is too long to read.

Ditto project developer ID

List variablisation candidates

Open a real browser window on app.dittowords.com so the user can sign in like normal — the session token is captured automatically and stored (no devtools, no copy-pasting). Use this when a backend tool reports a missing/expired session token. The login is remembered in a local browser profile, so future refreshes usually complete hands-free in seconds. First ever run installs a small browser-automation helper into the local data dir (~40 MB, one-time, may take a minute). Tell the user a browser window is about to open before calling this.

Log in to Ditto in a browser (captures session token)

Look up source strings in the variant's translation memory and get back only the rows that matter — the reuse step of the translation loop, without reading the memory file. Pass a whole batch of sources in one call. Each result is one of: 'exact' (reuse that translation verbatim), 'near' (scored candidates sharing the phrase or terms — mirror their wording and locked terms rather than inventing new phrasing), 'conflict' (approved copy EXISTS but the workspace disagrees with itself — do not reuse blindly; resolve or skip), or 'none' (translate from scratch). Matching ignores case, punctuation and trailing spaces, and treats {{placeholders}} and literal amounts as interchangeable values, so 'Includes {{interest_amount}} interest' matches 'Includes ď50.00 interest'. PREFER THIS over reading translation-assets/{variant}/translation-memory.md: that file is a display rendering — long cells are hard-wrapped on '<br>' and every column is space-padded — so grepping it silently misses short entries and returns broken text.

Project dev IDs to skip (default: the configured excluded/test projects)

Similarity threshold for near matches, 0-1 (lower = more, noisier candidates)

Max near matches per source (0 = exact matches only)

Rebuild the index instead of reusing the ~10 min in-process cache

Source (base) strings to look up — pass the whole batch in one call

Variant to look up (default: configured default variant)

Look up translation memory

Collapse base items that hold the SAME text into one item carrying all their Figma instances, then delete the leftovers. This is the cleanup variablisation creates: figma_link_pass dedupes by exact text, so four different merchant names become four items — and once you rewrite them all to {{merchant_name}} they are four copies of one string with numbered dev IDs (merchant-1..4) that no developer wants. One string should be one item with four instances. Call with apply=false (default) to see the groups and the dev ID each would collapse to; apply=true performs it. NOTE the ordering, which is not optional: a Figma node cannot move while its old item still owns it, so in practice the duplicates are ALWAYS deleted before their instances are re-attached to the keeper (observed 24 Aug 2026 — the move-first attempt is made but the backend rejects it every time). There is therefore a window where the items are gone and the instances are not yet re-attached: the captured instance payloads are echoed under `recoverInstances` on any group that does not verify, and that is the only way back. Deletion is irreversible. UNOFFICIAL: needs a session token (login_to_ditto) for the instance move.

false (default) = report the groups only; true = merge and delete the duplicates

Explicit groups. Omit to auto-detect every set of base items sharing identical text. Give this when identical text should NOT be merged (genuinely separate strings that happen to match).

Developer ID of the item to keep

Developer IDs to fold into the keeper

Optional new dev ID for the keeper — use the suffix-free name, e.g. 'txn-merchant'

Ditto project developer ID

When auto-detecting, also strip a trailing -N from the keeper's dev ID if that frees up a cleaner name (merchant-1 -> merchant). Default true. Ignored for explicit groups, which use renameTo.

Merge items whose copy is now identical (unofficial)

Build the variant's translation memory from FINAL (expert-approved) translations across the workspace, paired with source text. Excludes configured test/sandbox projects (set_excluded_projects). Groups by source: a source with ONE agreed translation goes into the memory (translation-assets/{variant}/translation-memory.md — a clean table, reference it before translating so existing copy is reused). A source with DIFFERENT FINAL translations is a CONFLICT: it is kept OUT of the memory until resolved and written to a separate translation-assets/{variant}/translation-conflicts.md with each translation's dev IDs + project IDs. Returns counts + the conflicts file path.

Project dev IDs to skip entirely (default: the configured excluded/test projects)

Variant to refresh (default: configured default variant)

Refresh translation assets

Rename text-item developer IDs in a project — an operation the public API does not support. UNOFFICIAL: replays the Ditto web app's internal backend API (unversioned; may break without notice) and needs a session token (set_session_token) rather than the API key. Each rename is {from, to}. Skips (with reasons) unknown 'from' IDs and 'to' IDs that already exist. Verifies via the public API afterwards. Keep new IDs kebab-case and reasonably short.

Array of {from, to} developer-ID renames

Rename developer IDs (unofficial)

Case-insensitive substring search over base text items (whole workspace, or one project) and the component library. Reuse-oriented: before writing a new string, search for it — an existing item or component may already cover it. Also handy for locating which project/block a string lives in. Returns matches as {id, text, projectId, status} plus component matches; capped at `limit` each.

Max matches returned per list

Limit to this project (default: whole workspace)

Substring to search for (case-insensitive)

Search workspace text

Set the workspace's default variant (e.g. 'ar', 'fr'). Persisted locally, so it only needs setting once; all tools use it whenever variantId is omitted.

Ditto variant ID to use as the default

Set default variant

Set the list of project developer IDs to exclude from the translation memory and conflict scan — sandboxes/QA playgrounds whose copy must never seed real translations. Persisted locally.

Project developer IDs to exclude (replaces the current list)

Set excluded (test/sandbox) projects

Store a Ditto browser-session JWT for this server session, enabling the UNOFFICIAL backend tools (currently: rename_developer_id) that the public API can't cover. The token expires after a while — when a backend tool reports it expired, paste a fresh one here (no server restart needed). Easiest fix: run the login_to_ditto tool — it opens a browser window, you sign in like normal, and the token is captured automatically. Manual alternative: open app.dittowords.com (logged in) → devtools → Network tab → any request to backend.dittowords.com → copy the 'Authorization' header value → call set_session_token with it.

The Authorization header value from a backend.dittowords.com request (with or without 'Bearer ')

Set Ditto session token (unofficial)

Set the workflow status of text items in a project. By default targets BASE items; pass variantId (e.g. 'ar') to target that variant instead. Pass ids to scope to specific developer IDs, or omit ids and pass fromStatus to move everything currently at that status (or any of a list of statuses) — e.g. fromStatus ['NONE','WIP','REVIEW'] with status 'REVIEW' stages everything for review WITHOUT touching FINAL items. fromStatus works for base items or a variant (with variantId). IDs with no matching item/variant are skipped, not fatal.

When ids omitted: update all items (base, or the variant if variantId is set) currently at this status or any status in this list

Developer IDs to update (omit = derive from fromStatus)

Ditto project developer ID

Status to set

Target this variant (e.g. 'ar') instead of base items

Update text item / variant status

Rewrite the text of BASE items in a project (copy edits, {{variable}} replacements). Each update is {id, text} where id is the item's developer ID; unknown IDs are skipped, not fatal. Status is left unchanged unless given. Note: {{name}} placeholders land as literal text — the public API cannot link workspace variables to items, and dev-ID renames aren't supported either; both happen in the Ditto web app.

Ditto project developer ID

Also set this workflow status (default: leave unchanged)

Array of {id, text} — id is the base item developer ID

Update base text

Write translated variants back to Ditto (public API). Each translation is {id, text} where id is the base item's developer ID. Creates the variant if missing. Defaults: configured default variant, status 'WIP'.

Variant to write (default: configured default variant)

Write variant translations