qiksy-mcp 1.2.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENT-GUIDE.md CHANGED
@@ -61,31 +61,165 @@ inside the existing (even backgrounded) tab.
61
61
  consent in the popup.** A refusal there is the human's to clear; report it in one
62
62
  sentence rather than working around it.
63
63
 
64
- `qa_click` · `qa_type` · `qa_type_many` · `qa_press` · `qa_wait_ready` ·
65
- `qa_navigate` · `qa_open_isolated` · `qa_close_tab`
64
+ `qa_click` · `qa_type` · `qa_type_many` · `qa_fill_json` · `qa_pick_date` · `qa_pick_path` ·
65
+ `qa_pick_tags` · `qa_pick_range` · `qa_pick_time` · `qa_probe` · `qa_upload` · `qa_press` · `qa_wait_ready` ·
66
+ `qa_navigate` · `qa_open_isolated` · `qa_close_tab` · `qa_recipe_apply`
67
+
68
+ **Local, no browser involved.** `qa_recipe_save` · `qa_recipe_list` — the saved flows on
69
+ this machine (see *Forms* below).
66
70
 
67
71
  ---
68
72
 
73
+ ## Filling a form — one call per step, not one per field
74
+
75
+ This is where the most time gets lost here, so it is worth stating plainly.
76
+
77
+ `qa_type` is **not** a keystroke emulator. It is the extension's own fill engine, so one
78
+ call handles a text input, a native `<select>`, a checkbox, a slider, rich text — **and**
79
+ the controls that look like they need clicking: Radix/shadcn, MUI, Ant and react-select
80
+ dropdowns, typeahead comboboxes, popup date and time pickers. It opens the popover, waits
81
+ for the options, picks the one matching your value and verifies the trigger changed.
82
+ `qa_type_many` takes an array of exactly those; a batch where half the entries are
83
+ dropdowns and dates is the normal use, not an edge case.
84
+
85
+ So one step of a wizard is about three calls:
86
+
87
+ ```
88
+ qa_snapshot { fields_only: true } → the step's controls, not the app
89
+ qa_type_many { fields: [ …every field of the step… ] } → widgets and all, in order
90
+ qa_click { name: "Next" } → its `changed` says it advanced
91
+ ```
92
+
93
+ Opening a dropdown, snapshotting its options and clicking one costs three round trips and
94
+ two settle windows **per field**. Keep it for an entry the batch reported `ok: false`.
95
+ Uploads are the one exception to "one call": `qa_upload` takes `files[]` for the whole step.
96
+
97
+ **Five widget shapes need their own verb, because one value takes several interactions.**
98
+ `qa_pick_date { name, date: "14/03/1987" }` sets an exact date whatever library owns the calendar —
99
+ it types first and steers the picker only if that fails (MUI X year view, Ant's header year,
100
+ react-datepicker's dropdowns, bare arrows). `qa_pick_path { name, path: ["Ukraine","Kyiv oblast","Kyiv"] }`
101
+ walks a cascader or a drill-down level by level. `qa_pick_tags { name, tags: [...] }` adds chips to
102
+ a control that CREATES its values (Ant `mode="tags"`): it types, **verifies the text is still in the
103
+ field**, then presses Enter — because a commit clears the component's own search input, and Enter on
104
+ a wiped field re-submits the previous text, which toggles that tag back OFF. `qa_pick_range { name,
105
+ from, to }` sets both ends of a range that lives in one control, walking start → end panel without
106
+ re-opening. `qa_pick_time { name, time: "14:30" }` drives a column picker whose cells have no role at
107
+ all. All five are ONE call and all five re-snapshot between steps, which is what makes them work
108
+ where a batch cannot.
109
+
110
+ **If you already hold the data, skip the mapping.** `qa_fill_json { fields: { "Initiative
111
+ name": "Test run", "Country": "Ukraine", "I agree": true } }` — labels instead of refs, one
112
+ call, and it snapshots and resolves for you. That is the same JSON shape the panel's `{ }`
113
+ editor and its ⚡Fill button use, so a dataset you build reads the way a human would edit it.
114
+ Keys that matched nothing come back with the page's real field names, so the next call fixes
115
+ itself. Use `qa_snapshot` + `qa_type_many` instead when you must pick one specific control
116
+ among duplicates.
117
+
118
+ Custom widgets are still filled strictly one at a time — two open popovers break each
119
+ other — so a step with several dropdowns takes a few seconds of genuine waiting. That part
120
+ is the widgets, not the bridge.
121
+
122
+ ## The fast route: put the value straight in
123
+
124
+ `qa_type { raw: true }` writes the string VERBATIM into the control's own text input — through the
125
+ native value setter, which is what React's tracked `value` property requires — and commits it with a
126
+ key. Measured on a live stand: a date **0.30s this way against 26s** driven through its calendar, a
127
+ time 0.30 against 14, a range 0.60 against 35.
128
+
129
+ It is for controls that **parse** text: dates, times, range ends, tags, a creatable or async select.
130
+ Pass the value in the format the control DISPLAYS (`06/11/2024`, `14:30`, the option text), not ISO.
131
+
132
+ - `commit` — the key that commits, default `Enter`. Pass `""` for a plain input, where Enter submits
133
+ the form.
134
+ - `blur: true` — a tags field keeps its value only once focus LEAVES.
135
+ - `pick: true` — write the text to filter, then CLICK the option that matches. For a select the text
136
+ alone never chooses; this is the other half.
137
+ - `open: true` — click the control first. A select filters only while its list is open.
138
+
139
+ A select that must **choose a row from its own list** is not a raw case: leave the flag off and let
140
+ the fill engine do it (it opens the list, waits, picks the match, checks the trigger changed). Ant
141
+ Design in particular commits its selection its own way and ignores an option click from outside the
142
+ page's own context.
143
+
144
+ `qa_type_many` and `qa_fill_json` take these per entry and set them for you: raw for what parses,
145
+ the engine for what chooses. Raw entries go in ONE pass — they open nothing, so they cannot collide.
146
+
147
+ **And when the widget is what you are testing, do not use this.** Driving the popup the way a user
148
+ does is the honest thing then; the visual side belongs to a real browser driver that can see pixels.
149
+
150
+ ## You are told what a widget IS — don't work it out by trial and error
151
+
152
+ Every session before this one re-derived the same facts by failing at them. They are in the
153
+ replies now, in three places, all free:
154
+
155
+ - **`qa_snapshot`** → `componentLibraries` + `hints` (which UI library draws this page, and what
156
+ that means: an Ant day is a role-less `<td title="2026-06-28">`, a MUI date field cannot be
157
+ written to at all, a react-select value must be the option TEXT) and **`widgets`** — the notable
158
+ kinds on the page, each with the verb that drives it and the fields it applies to.
159
+ - **`qa_click` / `qa_press`** → `widget`, the verdict on what your click just opened, read off the
160
+ diff: `calendar-popup` → `qa_pick_date` · `option-list` → it is a select, so pass a value instead
161
+ of clicking · `tree-popup` → click the CHECKBOX via `qa_pick_path` · `modal` → snapshot `within`
162
+ the dialog · `navigated` → the step swapped, every ref you hold is void ·
163
+ **`portalled-invisible`** → it opened and the tree gained NOTHING, so the popup is role-less
164
+ markup in a portal: address its parts by CSS selector, which every verb accepts.
165
+ - **`qa_probe { name }`** → for the ONE control that refused a value, and the only route into a
166
+ widget hand-rolled from `<div>`s (role `generic`, no name, no state). Shape first — a sectioned
167
+ date field, a file input, a switch and a slider are settled with **no click at all** — then one
168
+ click, read the diff, Escape. Do not probe every field: the snapshot already classified the page.
169
+
170
+ **Then save what you learned.** A long flow discovered once should not be discovered twice:
171
+
172
+ ```
173
+ qa_recipe_save { name: "New initiative" } → ~/.qiksy/recipes/new-initiative.json
174
+ qa_recipe_list → what is already saved for this host
175
+ qa_recipe_apply { name: "New initiative", step: 1, dry_run: true } → does it still fit?
176
+ qa_recipe_apply { name: "New initiative", all: true, values: {...} } → replay it
177
+ ```
178
+
179
+ The recipe stores field NAMES, not refs (a ref lives for one snapshot; a name survives a
180
+ re-render and a new session), and the replay re-matches them against the page as it is now.
181
+ `values` swaps the data, so one saved flow becomes many runs. `dry_run` reads only. With
182
+ `all` it clicks each advance control — including a final Submit, so keep it stepwise on
183
+ anything irreversible. Tell the human where the file is: it is theirs to edit or delete.
184
+
69
185
  ## How to drive it well
70
186
 
71
187
  - **Target by `ref` from a fresh snapshot.** A `ref` is unique where a CSS selector is
72
188
  ambiguous. After anything that navigates or re-renders, call `qa_wait_ready` and take
73
189
  a **new** snapshot — old refs go stale.
190
+ - **A ref belongs to ONE snapshot.** Taking another renumbers them: `e12` may now be a
191
+ different control, and clicking it will *succeed* — on the wrong thing. Discard the old
192
+ refs, or target by `name` (`qa_click { name: "Next" }`, `qa_fill_json`), which survives
193
+ both a re-render and a renumbering. This pressed **Back instead of Next** the first time
194
+ it ran against a real wizard.
195
+ - **Narrow the snapshot on a big app.** `fields_only`, `roles`, `match`, `within` (the
196
+ selector of a container node, for one dialog or step), `format: "tree"`. Pulling the whole
197
+ tree on every step of a form is the other half of a slow run. Filtering never invalidates
198
+ a ref.
74
199
  - **Address a specific tab** with `tabId` from `qa_tabs`; omit it to act on the active
75
200
  one. Tabs from `qa_open_isolated` are separate logins on the same site — that is how
76
201
  you drive several accounts at once.
77
202
  - **After a submit**, `qa_wait_ready` before the next call: the old document (and Qiksy
78
203
  inside it) is gone, and the next call would land on a page still being replaced.
79
- - **Autocomplete fields need a beat.** Suggestion lists arrive asynchronously (~0.4s on
80
- travel sites). Type, wait, snapshot, then click the option you want — do not assume
81
- the first one committed.
204
+ - **Autocomplete: give `qa_type` the option text first.** It waits for the suggestion list
205
+ itself (they arrive asynchronously, ~0.4s on travel sites) and picks the match. Only when
206
+ it answers `ok: false` do you fall back to type → wait → snapshot → click the option, and
207
+ even then check the trigger's text before assuming the choice committed.
82
208
 
83
209
  ## What it cannot do — say so, don't substitute
84
210
 
85
- - **No screenshots.** `qa_snapshot` is a tree, not pixels. Judgements about spacing,
86
- colour or alignment cannot be made from here.
87
- - **No arbitrary JavaScript.** No `evaluate`, by design (a Web Store requirement). Which
88
- means **no reading or writing localStorage** and **no measuring geometry**.
211
+ - **No screenshots.** `qa_snapshot` is a tree, not pixels. A judgement that genuinely needs
212
+ to SEE the rendering — is this the right shade, does that shadow look right — cannot be
213
+ made from here.
214
+ - **No arbitrary JavaScript.** No `evaluate`, by design (a Web Store requirement). Every verb
215
+ is a closed vocabulary: you name an element, never a program.
216
+ - **Styles, geometry and web storage ARE available** — `qa_styles` (computed styles, the box,
217
+ and the CSS custom properties in scope), `qa_snapshot({geometry:true})` (every node's box in
218
+ CSS pixels), `qa_storage` (localStorage / sessionStorage, read free, write behind Agent
219
+ control). This guide used to list all three as impossible. That was a wrong inference from
220
+ the no-eval rule, and it cost real capability: an agent told a thing cannot be done does not
221
+ try it. So: no pixels, but plenty of numbers — "this button renders #3B82F6 at 15px with
222
+ 22px padding, and its box overlaps the one next to it" needs no screenshot at all.
89
223
  - **No `<iframe>` contents.** Payment forms (Stripe, 3-D Secure) live in frames the
90
224
  content script does not enter. Fill up to them, not inside them.
91
225
  - **No captcha.** Stop and hand it back.
package/README.md CHANGED
@@ -22,16 +22,19 @@ unless the licence (or the 14-day trial) is valid, so once a trial lapses *every
22
22
  tool fails — `qa_export` included — with a message saying so. Do not plan a
23
23
  free-tier integration around read-only tools.
24
24
 
25
- **npm carries `qiksy-mcp@1.0.7`** — the current build, versioned in lockstep with
26
- the extension, with all eighteen tools including a full-page `qa_snapshot`,
27
- `qa_type_many` (fill a whole form in one call), and Agent control (`qa_click`,
28
- `qa_type`, `qa_press`, `qa_navigate`, `qa_open_isolated`, `qa_close_tab`). Just
29
- `npx qiksy-mcp` — no clone, no local path, no version to pin (it always fetches
30
- `latest`). (For hacking on the bridge itself, see **Local run** at the bottom.)
25
+ **npm carries `qiksy-mcp@1.3.0`** — the current build, with all thirty-two tools
26
+ including a filterable full-page `qa_snapshot`, `qa_type_many` (a whole form —
27
+ custom dropdowns and date pickers included — in one call), recipes that make the
28
+ second run cheap (`qa_recipe_save` / `qa_recipe_list` / `qa_recipe_apply`), and
29
+ Agent control (`qa_click`, `qa_type`, `qa_press`, `qa_navigate`,
30
+ `qa_open_isolated`, `qa_close_tab`). Just `npx qiksy-mcp` — no clone, no local
31
+ path, no version to pin (it always fetches `latest`). The 1.3.0 additions are all
32
+ server-side, so they work with the extension already installed. (For hacking on
33
+ the bridge itself, see **Local run** at the bottom.)
31
34
 
32
35
  ## Tools
33
36
 
34
- Eighteen in total. **Read** — sense the page (all accept an optional `tabId` from `qa_tabs`; default = active tab):
37
+ Thirty-two in total. **Read** — sense the page (all accept an optional `tabId` from `qa_tabs`; default = the tabs you attached to the agent session, or the active tab if you attached none):
35
38
 
36
39
  | Tool | What it returns |
37
40
  |---------------|-----------------|
@@ -39,7 +42,7 @@ Eighteen in total. **Read** — sense the page (all accept an optional `tabId` f
39
42
  | `qa_status` | URL + error/warning/form counts + isolated-login name. |
40
43
  | `qa_findings` | Findings (with selectors + detail); `severity` = `all` \| `error` \| `warning`. |
41
44
  | `qa_export` | The full `qa-export/v1` bundle: findings, failed requests + bodies, form structure, repro steps, env. |
42
- | `qa_snapshot` | A full-page accessibility snapshot — interactive elements + landmarks with their roles, labels and a stable `ref` you can pass to `qa_click`/`qa_type`/`qa_press` instead of a CSS selector. Read-only. |
45
+ | `qa_snapshot` | A full-page accessibility snapshot — interactive elements + landmarks with their roles, labels and a `ref` you can pass to `qa_click`/`qa_type`/`qa_press` instead of a CSS selector. Read-only. **One snapshot owns its refs**: taking a new one renumbers them, so `e12` from an earlier snapshot may now be a different control and the click will succeed on the wrong thing — keep only the latest snapshot's refs, or target by `name`. **Narrow it** on a component-heavy app: `fields_only` (form controls only), `roles: ["button"]`, `match` on the accessible name, `within: "<selector of a container>"` for one dialog or wizard step, `limit`, `format: "tree"` for about half the bytes. Filtering happens on the way out, so the refs you get back are as valid as the full tree's. |
43
46
 
44
47
  **Command** — drive Qiksy's own UI / analysis (never the app under test; all accept `tabId`):
45
48
 
@@ -57,10 +60,66 @@ Because tools accept a `tabId`, an agent can read **several logins at once**: ca
57
60
 
58
61
  | Tool | What it does |
59
62
  |------------|--------------|
60
- | `qa_click` | Click an element by CSS selector. Sends the full pointer sequence (`pointerdown` → `mouseup` → `click`), so Radix/shadcn/MUI components that listen for `pointerdown` react like they would to a real click. Scrolls into view first; refuses a selector that matches nothing or has zero size instead of silently doing nothing. |
61
- | `qa_type` | Set an input / textarea / contenteditable value using the native setter + `InputEvent`, so React and Vue controlled inputs keep it (a plain assignment is swallowed). Reports `stuck: false` when the field rejected the value — masked, read-only or controlled. |
62
- | `qa_type_many` | Fill a WHOLE form in ONE call: an array of `{ selector, value, action? }` (`action` defaults to `type` — also handles `<select>`, checkbox, radio — or `click` for buttons/toggles). Returns a per-field result list + filled/total. Far fewer round-trips than one `qa_type` per field. |
63
+ | `qa_click` | Click an element by `ref`, by CSS selector, or by the control's accessible **`name`** (`{ name: "Next", role: "button" }`). Use the name right after a fill: a fill re-renders the step, and a ref from before it either gets refused as stale or — worse, after any new snapshot — silently means a different control. The bridge resolves a name against a snapshot it takes at that moment. Sends the full pointer sequence (`pointerdown` → `mouseup` → `click`), so Radix/shadcn/MUI components that listen for `pointerdown` react like they would to a real click. Scrolls into view first; refuses a selector that matches nothing or has zero size instead of silently doing nothing. |
64
+ | `qa_type` | Set ONE field of **any kind** to a value. This is the extension's own fill engine, not a keystroke emulator, so the same call covers text/number/date inputs and textareas (native setter + `InputEvent`, so React/Vue controlled inputs keep the value), native `<select>` (matched against the option labels), checkboxes and radios, sliders, contenteditable rich text — **and** the widgets that look like they need clicking: Radix/shadcn, MUI, Ant, react-select dropdowns, typeahead comboboxes, popup date & time pickers. It opens the popover, waits for the options, picks the match and verifies the trigger changed. Reports `stuck: false` when the field rejected the value — masked, read-only or controlled. |
65
+ | `qa_type` + `raw` | **Put the value straight in.** Writes the string verbatim through the native value setter (React swaps `value` for its own tracked property, so a plain assignment is reverted on the next render) and commits with a key. Measured: a date **0.30s against 26s** through its calendar, a time 0.30 against 14, a range 0.60 against 35. For controls that PARSE text — dates, times, range ends, tags — in the format the control DISPLAYS. `pick: true` adds the other half for a select: write to filter, then click the matching option. `open: true` opens the list first (a select filters only while it is up); `blur: true` for a tags field, which keeps its value only once focus leaves. A select that must choose from its own list keeps the ordinary engine path. |
66
+ | `qa_type_many` | Fill a WHOLE form, or one step of a wizard, in ONE call: an array of `{ ref \| selector, value, action? }`. **Every widget kind `qa_type` drives goes in the same batch** — a batch where half the entries are dropdowns and dates is the normal use, not an edge case. Prefer `ref`: a form built from a repeated component gives several inputs the identical cssPath, and a batch of selectors would pour every value into the first match. Returns a per-field result list + filled/total; only an entry that came back `ok:false` is worth a follow-up call. |
67
+ | `qa_fill_json` | Fill a form from a plain `{ "Field label": "value" }` object — a value of **`"click"`** presses the thing instead (a chip, a custom radio, a switch, any control that takes no value), and **`"Label#2"`, `"Label#3"`** address the 2nd and 3rd control sharing one label, which is how a form built from a repeated component is filled — **the same shape as the JSON behind the panel's `{ }` editor and its ⚡Fill button**. It snapshots for you, resolves the labels (exact → truncated → contains) and hands the whole lot to the batch filler, so the agent needs no refs and no snapshot of its own: one call for a form it has never seen. Keys that matched nothing come back **with the page's real field names**, so the next call fixes its own key. Keys are read in order, so on repeated labels the Nth key takes the Nth control. `dry_run` resolves without filling. |
68
+ | `qa_upload` | Attach a file to an `<input type=file>`, including the hidden one behind a styled dropzone: `path` (this process reads it, so the bytes never enter the agent's context), `content` as base64, or a hostile `fixture` built in the page. `files: [...]` attaches several in one round trip. |
69
+ | `qa_pick_date` | An EXACT date in one call, whatever library owns the calendar. Types it first (two calls, and correct for most inputs); if the value does not stick, opens the picker and steers it — MUI X's year view, Ant's clickable header year, react-datepicker's month/year dropdowns, plain arrows otherwise — stepping until the header agrees, then clicking the day. Reports `via` and a `trail`, so an unrecognised picker is visible rather than silent. **Use it for MUI X date fields**: they are three contenteditable sections the component rebuilds from its own state, so a written "14" can land in the YEAR and the day stay empty. |
70
+ | `qa_pick_path` | A value that lives across several levels, in one call: an Ant Cascader (country → region → city), a submenu chain, a drill-down. Opens the control and clicks each level on a FRESH snapshot, because opening a level is what re-renders the popup. A level that never appears is reported with the ones that did. |
71
+ | `qa_pick_tags` | A whole list of tags on a control that CREATES its values — Ant's Select in `mode="tags"`, and every chips input built the same way. 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 whole point: 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**, so the form loses the tag you added first. It also never clicks the field first (a click moves the caret out of the search input). |
72
+ | `qa_pick_time` | A time on a column-based picker (Ant's TimePicker). Needed because the cells are bare list items with NO role — invisible to the snapshot — and the ordinary fill path misreads the control as a date field, types an ISO date into it and leaves it empty *while reporting success*. This reads each column's text, finds which entry is your value, clicks that child, then presses the panel's own OK — which is not optional: without it the value stays provisional. |
73
+ | `qa_pick_range` | Both ends of a date range that live in ONE control (Ant's RangePicker). Not two `qa_pick_date` calls: the control walks from the start panel to the end panel itself, and approaching it cold a second time restarts the range on the start input. Says so plainly when the panel closes after the start — then the end is its own field. |
63
74
  | `qa_press` | Dispatch a key (`Enter`, `Escape`, `Tab`, …). Without a selector it goes to the focused element, like a real keyboard. |
75
+ | `qa_probe` | **What kind of widget is this, and how do I drive it?** — for the one control that refused a value. Judges by SHAPE first (role, tag, name, state, neighbours), which settles a sectioned date field, a file input, a switch or a slider **without touching the page**; when the shape says nothing — the case of a widget hand-rolled from `<div>`s, where role is `generic` and there is no name and no state — it clicks once, reads what the page DID, and presses Escape to put it back. Returns the `kind`, the `verb` that drives it, and `why`. The verdict worth the most is `portalled-invisible`: it opened and the accessibility tree gained NOTHING, which means role-less markup in a portal (Ant Design) and the parts must be addressed by CSS selector. |
76
+
77
+ **One step of a multi-step form is about three calls**, and this is the single biggest
78
+ difference between a fast run and a slow one:
79
+
80
+ ```
81
+ qa_snapshot { fields_only: true, roles: ["button"] } → the step's controls, not the whole app
82
+ qa_type_many { fields: [ …the entire step… ] } → dropdowns, dates and inputs together
83
+ qa_click { ref: <Next> } → its `changed` says the step advanced
84
+ ```
85
+
86
+ **When you already hold the data**, skip the mapping entirely — `qa_fill_json { fields: { "Initiative
87
+ name": "Test run", "Country": "Ukraine", "Start date": "2026-08-01", "I agree": true } }` is the whole
88
+ step in one call, labels instead of refs. Keep `qa_snapshot` + `qa_type_many` for when you must pick a
89
+ specific control among duplicates.
90
+
91
+ Opening a dropdown, snapshotting to read its options and clicking one works, but it pays
92
+ a settle window twice per field and costs three round trips where the batch costs a share
93
+ of one. Keep it for an entry the batch reported `ok:false`.
94
+
95
+ **Recipes** — so the second run is the cheap one. Refs live for one snapshot and a new
96
+ session starts with nothing, which is why a form that took twenty calls to work out used to
97
+ take twenty again. These three keep the flow on your machine, in field NAMES:
98
+
99
+ | Tool | What it does |
100
+ |------|--------------|
101
+ | `qa_recipe_save` | Writes what the agent just filled, uploaded and clicked to `~/.qiksy/recipes/<name>.json`, folded into steps (a click after a group of fills is what ends a step of a wizard). Pass `steps` to write or correct one by hand. Local only — no browser, no Pro. |
102
+ | `qa_recipe_list` | The saved flows, newest first: host, step count, the first step's field names. Worth one call before starting anything form-heavy. |
103
+ | `qa_recipe_apply` | Replays a recipe against the page as it is **now** — per step it snapshots, matches every field by accessible name (exact → truncated → contains → the stored cssPath), fills the step in one batch, attaches its uploads and clicks the advance control. `values` overrides the stored data (one flow, many datasets); `dry_run: true` resolves everything and touches nothing; `all: true` walks the remaining steps — which means it will click a final Submit, so keep it stepwise on anything irreversible. A target the page no longer has comes back under `missing` instead of failing silently. |
104
+
105
+ The recipe is a plain JSON file you own — editable, deletable, never uploaded.
106
+ `QIKSY_RECIPES_DIR` moves the folder (e.g. into the project, to share flows with a team).
107
+
108
+ ### Qiksy talking to you
109
+
110
+ Now and then a tool result carries `qiksySays` — a line for the **human**, which the agent is asked
111
+ to pass on in your language. It says hello the first time, and after that it speaks only when
112
+ something happened: your agent went quiet for a long time, the same control got clicked three times,
113
+ a couple of fields refused their values in a row, or a long form is worth saving as a recipe. It is
114
+ blunt and it swears mildly, and it is rationed on purpose — an 8-minute floor, a 10–30 minute gap
115
+ per kind of remark, five lines per session, so 40-odd calls produce at most one.
116
+
117
+ | Variable | Effect |
118
+ |---|---|
119
+ | `QIKSY_MCP_VOICE=rude` | Default. The character, as described above. |
120
+ | `QIKSY_MCP_VOICE=nice` | Same moments, polite wording. **Chosen automatically in CI** (`CI`, `GITHUB_ACTIONS`, `GITLAB_CI`, `BUILDKITE`, `TEAMCITY_VERSION`, `JENKINS_URL`) so a pipeline log and a customer demo stay strictly business. |
121
+ | `QIKSY_MCP_VOICE=off` | Nothing addressed to the human at all. |
122
+ | `QIKSY_MCP_QUIPS=0` | Silences the voice *and* the decorative `qx …` line on every result. |
64
123
 
65
124
  **Chaining** — what makes a multi-page, multi-role run reliable:
66
125
 
@@ -253,3 +312,10 @@ Logs go to **stderr** (stdout is the JSON-RPC channel). You should see `bridge l
253
312
  - **One tab at a time**: tools read the *active* tab in the last-focused Chrome window. Focus the app under test.
254
313
  - **Service-worker sleep**: if Chrome idles the extension's worker, the socket drops and reconnects automatically (≤~30 s). If a tool times out, retry.
255
314
  - **Security**: bound to `127.0.0.1` only; connections without the matching token (or from a non-extension origin) are closed before any data is sent.
315
+
316
+ ## Follow the build
317
+
318
+ We build Qiksy in the open — what shipped, what broke, what's next.
319
+
320
+ - **LinkedIn** — [linkedin.com/company/qiksy](https://www.linkedin.com/company/qiksy)
321
+ - **Site** — [qiksy.app](https://qiksy.app)
package/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "qiksy-mcp",
3
- "version": "1.2.0",
3
+ "version": "1.4.0",
4
4
  "description": "MCP bridge for the Qiksy browser extension — expose live QA findings, forms, network and session to any MCP-capable coding agent.",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "qiksy-mcp": "server.mjs"
8
8
  },
9
- "files": ["server.mjs", "README.md", "AGENT-GUIDE.md"],
9
+ "files": [
10
+ "server.mjs",
11
+ "widgets.mjs",
12
+ "README.md",
13
+ "AGENT-GUIDE.md"
14
+ ],
10
15
  "engines": {
11
16
  "node": ">=18"
12
17
  },