@ia-qa/qa-discovery 0.4.0 → 0.5.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/README.md +82 -2
- package/ROADMAP.md +26 -4
- package/TUTORIAL.md +85 -2
- package/dist/ai/classify.d.ts +13 -36
- package/dist/ai/classify.js +23 -166
- package/dist/ai/classify.js.map +1 -1
- package/dist/ai/model.d.ts +93 -0
- package/dist/ai/model.js +192 -0
- package/dist/ai/model.js.map +1 -0
- package/dist/ai/plan.d.ts +224 -0
- package/dist/ai/plan.js +0 -0
- package/dist/ai/plan.js.map +1 -0
- package/dist/cli/args.js +1 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.js +13 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/skill.d.ts +20 -0
- package/dist/cli/skill.js +63 -0
- package/dist/cli/skill.js.map +1 -0
- package/dist/cli-ai/index.d.ts +3 -2
- package/dist/cli-ai/index.js +268 -4
- package/dist/cli-ai/index.js.map +1 -1
- package/dist/htmlReport.js +20 -4
- package/dist/htmlReport.js.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.js +32 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +49 -0
- package/dist/mcp/server.js +143 -20
- package/dist/mcp/server.js.map +1 -1
- package/dist/overview.d.ts +9 -0
- package/dist/overview.js +9 -4
- package/dist/overview.js.map +1 -1
- package/dist/overviewFile.js +5 -0
- package/dist/overviewFile.js.map +1 -1
- package/dist/planView.d.ts +141 -0
- package/dist/planView.js +345 -0
- package/dist/planView.js.map +1 -0
- package/package.json +3 -2
- package/skills/ia-qa-discover/SKILL.md +166 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ia-qa/qa-discovery",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Deterministic surface reconnaissance for an unknown web app — crawl, capture, diff. No LLM, no hosting: runs entirely on your machine, with a local MCP server for agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"qa",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
},
|
|
36
36
|
"files": [
|
|
37
37
|
"dist",
|
|
38
|
+
"skills",
|
|
38
39
|
"README.md",
|
|
39
40
|
"TUTORIAL.md",
|
|
40
41
|
"ROADMAP.md"
|
|
@@ -49,7 +50,7 @@
|
|
|
49
50
|
"node": ">=18.0.0"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
|
-
"@ia-qa/self-healing": "^1.
|
|
53
|
+
"@ia-qa/self-healing": "^1.19.0"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"playwright": ">=1.40.0"
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ia-qa-discover
|
|
3
|
+
description: >-
|
|
4
|
+
Find out what a web application contains when no test suite exists yet — its pages, forms,
|
|
5
|
+
fields and the API calls they make — and state what an existing suite does NOT test. Use
|
|
6
|
+
when asked to explore an unknown app, to plan testing for one, to list its entry points, or
|
|
7
|
+
to answer "which pages are untested". Requires @ia-qa/qa-discovery (`ia-qa-discover`). Runs
|
|
8
|
+
entirely on the user's machine; the optional `-ai` binary is the only part that sends
|
|
9
|
+
anything anywhere.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# ia-qa-discover — read the capture, never re-crawl
|
|
13
|
+
|
|
14
|
+
## 1. The capture is on disk. Read it before opening anything.
|
|
15
|
+
|
|
16
|
+
`ia-qa-discover scan` writes `.ia-qa-discovery/capture/<page>.json`, one file per page:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"page": "checkout", "url": "https://app/checkout", "capturedAt": "2026-09-19T…",
|
|
21
|
+
"meta": { "title": "Checkout", "description": "…" },
|
|
22
|
+
"headings": [{ "level": 1, "text": "Your order" }],
|
|
23
|
+
"forms": [{ "method": "post", "hasSubmit": true,
|
|
24
|
+
"fields": [{ "name": "card", "type": "text", "label": "Card number",
|
|
25
|
+
"required": true, "stableSelector": true }] }],
|
|
26
|
+
"looseFields": [], "apiCalls": [{ "method": "POST", "urlPattern": "/api/pay", "status": 200 }]
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Most questions are answered by reading those files and `capture/_overview.md`. Re-running a
|
|
31
|
+
scan costs minutes and a browser; reading costs nothing. Scan again only when the app has
|
|
32
|
+
changed since `capturedAt`.
|
|
33
|
+
|
|
34
|
+
| Path | What it is |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `.ia-qa-discovery/capture/` | one JSON per page + `_overview.md` (the human index) |
|
|
37
|
+
| `.ia-qa-discovery/capture/surface.json` | the manifest, including **what the scan could not see** |
|
|
38
|
+
| `.ia-qa-discovery/coverage-map.json` | the gap, once `coverage` has run |
|
|
39
|
+
| `.ia-qa-discovery/classification.json`, `plan.json` | the optional model readings, if any |
|
|
40
|
+
| `.ia-qa-discovery/history.jsonl` | one line per scan |
|
|
41
|
+
|
|
42
|
+
## 2. Which verb answers which question
|
|
43
|
+
|
|
44
|
+
| The user's question | Verb |
|
|
45
|
+
|---|---|
|
|
46
|
+
| "what is even in this app?" | `scan <url> --save` |
|
|
47
|
+
| "most of the inputs are behind a menu or a tab" | `scan --deep` — opens disclosures one level and captures what they reveal |
|
|
48
|
+
| "the app is behind a login" | the human runs `ia-qa-heal login` (from `@ia-qa/self-healing`), then `scan --session .ia-qa/session.json`. **You cannot log in** (§4.1) |
|
|
49
|
+
| **"what does my test suite not test?"** | `coverage` — needs `@ia-qa/self-healing` **and** one watched run (§3) |
|
|
50
|
+
| "what changed since last time?" | `history` |
|
|
51
|
+
| "what is each entry point FOR?" | `ia-qa-discover-ai classify` — optional, BYOK (§5) |
|
|
52
|
+
| "what does somebody come to this page to DO?" | `ia-qa-discover-ai plan` — optional, BYOK (§5) |
|
|
53
|
+
| "write me a starting spec for an untested page" | `generate` — scaffolding only; the assertions stay human |
|
|
54
|
+
|
|
55
|
+
Exact flags: `ia-qa-discover <verb> --help`. Do not guess them from this file.
|
|
56
|
+
`scan`, `coverage`, `history` and both `-ai` verbs take `--json`. Prefer it over parsing prose.
|
|
57
|
+
|
|
58
|
+
## 3. This tool issues NO verdict, and that is the point
|
|
59
|
+
|
|
60
|
+
Nothing here exits non-zero because of what it found. A gap is a decision with a human's name
|
|
61
|
+
on it — an uncovered page may be deliberately out of scope.
|
|
62
|
+
|
|
63
|
+
**`coverage` refuses rather than guesses**, with a typed reason. Report the refusal as *no
|
|
64
|
+
measurement*, never as a finding:
|
|
65
|
+
|
|
66
|
+
| `reason` | What it means | What the human does |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `no-surface` | nobody has scanned this app | `ia-qa-discover scan` |
|
|
69
|
+
| `no-healing` | no suite is tracked here | install `@ia-qa/self-healing`, then `map` |
|
|
70
|
+
| `no-run` | contracts exist, no run was ever watched | `ia-qa-heal run`, or `ia-qa-pal tour --suite` |
|
|
71
|
+
|
|
72
|
+
`measured: false` is **not** "the suite covers nothing". Saying that is the single worst
|
|
73
|
+
sentence this tool can cause to be written.
|
|
74
|
+
|
|
75
|
+
## 4. Never do this
|
|
76
|
+
|
|
77
|
+
1. **Never run `ia-qa-discover login` or `ia-qa-heal login`.** They open a visible browser and
|
|
78
|
+
wait for a person, twice over: in your shell that is a hung command. Give the human the
|
|
79
|
+
command instead. The same goes for `ia-qa-heal ui`.
|
|
80
|
+
2. **Never present a contract, or a configured page, as proof a page is tested.** A contract
|
|
81
|
+
means somebody configured that page. Only capture-during-run proves a test went there.
|
|
82
|
+
3. **Never report a page count as coverage.** Read `surface.json` → `coverage` first: a login
|
|
83
|
+
wall (`loginWall`), an app that navigates without `<a href>` (`hrefBlind`) and the page cap
|
|
84
|
+
(`droppedByCap`) each make the surface look smaller than it is. State those beside any total.
|
|
85
|
+
4. **Never treat `writeCalls` as evidence a page writes.** A scan never submits anything, so a
|
|
86
|
+
non-GET it observed fired at page load — measured on a real corpus, 10 of 10 were analytics
|
|
87
|
+
beacons. It is in the output as a fact and is deliberately not a ranking factor.
|
|
88
|
+
5. **`named` is not `asserted`.** The depth figure says the suite *names* N contracted elements.
|
|
89
|
+
A test that clicks a button names it without checking anything.
|
|
90
|
+
6. **Never call the `-ai` layer a gate.** It emits no verdict, and `unclear` / `refused` are
|
|
91
|
+
answers needing a person, not a retry. A `failed` page means the model call did not succeed
|
|
92
|
+
— say so; it is not a finding about the app.
|
|
93
|
+
7. **Never widen `capture.safeQueryParams`** to make a scan "work". The deny rule on
|
|
94
|
+
`token`, `password`, `session`, `auth`, `jwt`, `email`, `key` exists so a reset link pasted
|
|
95
|
+
into `scan` does not end up in files the user commits.
|
|
96
|
+
8. **Never edit `config.json` to add a page you have not seen in a capture.** `discover --apply`
|
|
97
|
+
(heal) and the config are the human's; propose, do not write.
|
|
98
|
+
|
|
99
|
+
## 5. What leaves the machine
|
|
100
|
+
|
|
101
|
+
Nothing, except: requests to the app the user asked you to scan (same origin, GET only, never
|
|
102
|
+
a `/logout` or `/delete` URL), and — only if the user opted in — `ia-qa-discover-ai` calling
|
|
103
|
+
**their own** LLM provider.
|
|
104
|
+
|
|
105
|
+
The two `-ai` verbs announce what they send *before* they send it, on stderr, in every mode.
|
|
106
|
+
Read that announcement to the user before running either on a private or authenticated app.
|
|
107
|
+
`plan` sends strictly less than `classify`: no selectors, no observed API calls, and not the
|
|
108
|
+
headings the whole app repeats.
|
|
109
|
+
|
|
110
|
+
## 6. Setting up the BYOK key — ask, never invent
|
|
111
|
+
|
|
112
|
+
Both `-ai` verbs need a provider key. The key **never** goes in a file.
|
|
113
|
+
|
|
114
|
+
1. Ask the user which provider they have a key for: `anthropic`, `openai`, `google`, or
|
|
115
|
+
`openai-compatible` — that last one is a *shape*, not a vendor: DeepSeek, Groq, Mistral,
|
|
116
|
+
OpenRouter, Together, vLLM and Ollama all serve the same endpoint, and it needs a
|
|
117
|
+
`baseUrl`. A `localhost` URL there means the call never leaves their machine.
|
|
118
|
+
`ia-qa-discover-ai models` lists the models each one takes. Never pick for them.
|
|
119
|
+
2. **Prefer the machine's credential store.** Give them the command; they run it themselves,
|
|
120
|
+
in their own terminal — it asks for the value and refuses an argument or a pipe:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
npx -p @ia-qa/self-healing ia-qa-heal secret set MY_KEY_NAME
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
You can confirm it resolves without ever seeing it: `ia-qa-heal secret check MY_KEY_NAME`.
|
|
127
|
+
**There is no command that prints a stored value, and you must not go looking for one.**
|
|
128
|
+
The point of the store is that the value stops being ambient — it is no longer in the
|
|
129
|
+
environment every process you launch inherits.
|
|
130
|
+
|
|
131
|
+
3. Otherwise — or in CI, where there is no store and no person — ask them to export it in
|
|
132
|
+
their own shell and to tell you the **variable name**, never the key. If they paste the key
|
|
133
|
+
to you anyway, tell them to rotate it.
|
|
134
|
+
|
|
135
|
+
4. Write only the reference into `.ia-qa-discovery/config.json`:
|
|
136
|
+
|
|
137
|
+
```json
|
|
138
|
+
"ai": { "provider": "openai", "model": "gpt-4o-mini",
|
|
139
|
+
"apiKey": { "source": "keychain", "key": "MY_KEY_NAME" } }
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
`"source": "env"` is the same shape with the variable name, for CI.
|
|
143
|
+
|
|
144
|
+
5. `--dry-run` first: it shows exactly what would be sent, and to whom, without a key and
|
|
145
|
+
without calling anything. Show that to the user before the real run.
|
|
146
|
+
|
|
147
|
+
That file is committed. A value there is a leaked credential, and the CLI refuses it — it
|
|
148
|
+
detects a key-shaped string where a variable name belongs, prints it masked, and tells the
|
|
149
|
+
user to rotate it. Do not work around that refusal.
|
|
150
|
+
|
|
151
|
+
The CLI reads `process.env` only and never loads `.env` files. If their key lives in `.env`:
|
|
152
|
+
`npx dotenv-cli -- ia-qa-discover-ai plan`.
|
|
153
|
+
|
|
154
|
+
Through the MCP server the same rule holds: `api_key_env` takes the **name** of a variable on
|
|
155
|
+
that machine. A raw key is never a tool argument — a tool call can be logged and replayed.
|
|
156
|
+
|
|
157
|
+
## 7. The two siblings, and where the line is
|
|
158
|
+
|
|
159
|
+
`@ia-qa/self-healing` (`ia-qa-heal`) repairs an **existing** suite's locators. This package
|
|
160
|
+
describes an app that may have **no** suite. They share the session file and the page-naming
|
|
161
|
+
rule, and they hand off in exactly two places: when `ingest` finds no test file, and when
|
|
162
|
+
`coverage` needs a watched run. `@ia-qa/pal` (`ia-qa-pal`) runs the whole round for a team.
|
|
163
|
+
|
|
164
|
+
`scan` and `map` are **not** the same operation: `map` writes a contract (every interactive
|
|
165
|
+
element, to repair broken locators); `scan` writes a surface (what exists and what it takes as
|
|
166
|
+
input, to decide what to test when there is no suite yet).
|