@ia-qa/qa-discovery 0.4.0 → 0.5.2
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 +88 -4
- package/ROADMAP.md +26 -4
- package/TUTORIAL.md +98 -4
- package/dist/ai/classify.d.ts +13 -36
- package/dist/ai/classify.js +89 -169
- 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 +167 -0
package/dist/ai/model.js
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MODEL_DEFAULTS = exports.egressHost = exports.HOSTS = void 0;
|
|
4
|
+
exports.providerError = providerError;
|
|
5
|
+
exports.callModel = callModel;
|
|
6
|
+
exports.parseJsonReply = parseJsonReply;
|
|
7
|
+
exports.parseJsonList = parseJsonList;
|
|
8
|
+
const self_healing_1 = require("@ia-qa/self-healing");
|
|
9
|
+
Object.defineProperty(exports, "egressHost", { enumerable: true, get: function () { return self_healing_1.egressHost; } });
|
|
10
|
+
Object.defineProperty(exports, "HOSTS", { enumerable: true, get: function () { return self_healing_1.PROVIDER_HOST; } });
|
|
11
|
+
exports.MODEL_DEFAULTS = { timeoutMs: 30000, temperature: 0 };
|
|
12
|
+
function buildRequest(provider, model, apiKey, prompt, temperature, baseUrl) {
|
|
13
|
+
if (provider === 'anthropic') {
|
|
14
|
+
const body = { model, max_tokens: 4096, messages: [{ role: 'user', content: prompt }] };
|
|
15
|
+
if (temperature !== undefined)
|
|
16
|
+
body.temperature = temperature;
|
|
17
|
+
return {
|
|
18
|
+
url: `https://${self_healing_1.PROVIDER_HOST.anthropic}/v1/messages`,
|
|
19
|
+
headers: { 'content-type': 'application/json', 'x-api-key': apiKey, 'anthropic-version': '2023-06-01' },
|
|
20
|
+
body,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (provider === 'openai' || provider === 'openai-compatible') {
|
|
24
|
+
const body = { model, messages: [{ role: 'user', content: prompt }] };
|
|
25
|
+
if (temperature !== undefined)
|
|
26
|
+
body.temperature = temperature;
|
|
27
|
+
return {
|
|
28
|
+
// One shape, two destinations. `baseUrl` is taken as given — trailing slash trimmed and
|
|
29
|
+
// nothing else invented — because a gateway that does not end in `/v1` is a real
|
|
30
|
+
// configuration, not a mistake to correct. `egressHost` has already refused an absent
|
|
31
|
+
// one, so this is never asked to build a URL out of nothing.
|
|
32
|
+
url: provider === 'openai-compatible'
|
|
33
|
+
? `${(baseUrl ?? '').replace(/\/+$/, '')}/chat/completions`
|
|
34
|
+
: `https://${self_healing_1.PROVIDER_HOST.openai}/v1/chat/completions`,
|
|
35
|
+
headers: { 'content-type': 'application/json', authorization: `Bearer ${apiKey}` },
|
|
36
|
+
body,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
const body = { contents: [{ parts: [{ text: prompt }] }] };
|
|
40
|
+
if (temperature !== undefined)
|
|
41
|
+
body.generationConfig = { temperature };
|
|
42
|
+
return {
|
|
43
|
+
url: `https://${self_healing_1.PROVIDER_HOST.google}/v1beta/models/${encodeURIComponent(model)}:generateContent?key=${encodeURIComponent(apiKey)}`,
|
|
44
|
+
headers: { 'content-type': 'application/json' },
|
|
45
|
+
body,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function extractText(provider, data) {
|
|
49
|
+
if (provider === 'anthropic')
|
|
50
|
+
return data?.content?.[0]?.text ?? '';
|
|
51
|
+
if (provider === 'openai' || provider === 'openai-compatible')
|
|
52
|
+
return data?.choices?.[0]?.message?.content ?? '';
|
|
53
|
+
return data?.candidates?.[0]?.content?.parts?.[0]?.text ?? '';
|
|
54
|
+
}
|
|
55
|
+
async function attempt(opts, prompt, temperature) {
|
|
56
|
+
const fetchFn = opts.fetchFn ?? globalThis.fetch;
|
|
57
|
+
if (typeof fetchFn !== 'function')
|
|
58
|
+
return null;
|
|
59
|
+
const req = buildRequest(opts.provider, opts.model, opts.apiKey, prompt, temperature, opts.baseUrl);
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => controller.abort(), opts.timeoutMs ?? exports.MODEL_DEFAULTS.timeoutMs);
|
|
62
|
+
try {
|
|
63
|
+
return await fetchFn(req.url, {
|
|
64
|
+
method: 'POST',
|
|
65
|
+
headers: req.headers,
|
|
66
|
+
body: JSON.stringify(req.body),
|
|
67
|
+
signal: controller.signal,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null; // network error or timeout — never throws at the caller
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* What the provider itself said went wrong.
|
|
79
|
+
*
|
|
80
|
+
* Found by execution, not review: a Gemini key that is simply wrong comes back
|
|
81
|
+
* **400**, not 401 — so the previous message read `refused the call with HTTP
|
|
82
|
+
* 400`, said nothing about the key, and left the one person who could fix it in
|
|
83
|
+
* sixty seconds with nothing to go on. Mapping more status codes by hand is the
|
|
84
|
+
* wrong shape of fix (the list rots, and it is different per provider); the
|
|
85
|
+
* providers already answer the question in the body, and all three happen to
|
|
86
|
+
* use the same `{error:{message}}` envelope.
|
|
87
|
+
*
|
|
88
|
+
* Two guards. The body is untrusted text that lands in a terminal and in a
|
|
89
|
+
* committed file, so it is capped and flattened. And anything shaped like a
|
|
90
|
+
* credential is redacted before it is printed — a provider that echoes the
|
|
91
|
+
* request back would otherwise put the user's own key in a file they commit.
|
|
92
|
+
*/
|
|
93
|
+
async function providerError(res) {
|
|
94
|
+
const status = `HTTP ${res.status}`;
|
|
95
|
+
// Kept alongside the provider's own words, not replaced by them: a 401 with
|
|
96
|
+
// an empty body still has exactly one likely cause, and an existing test went
|
|
97
|
+
// red the moment this hint was dropped — correctly.
|
|
98
|
+
const hint = res.status === 401 || res.status === 403 ? ' Check the API key.' : '';
|
|
99
|
+
let body = '';
|
|
100
|
+
try {
|
|
101
|
+
body = await res.text();
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return `${status}.${hint}`;
|
|
105
|
+
}
|
|
106
|
+
let message = '';
|
|
107
|
+
try {
|
|
108
|
+
const parsed = JSON.parse(body);
|
|
109
|
+
message = parsed?.error?.message ?? parsed?.message ?? '';
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
message = body;
|
|
113
|
+
}
|
|
114
|
+
message = String(message).replace(/\s+/g, ' ').trim();
|
|
115
|
+
// sk-…, AIza…, ghp_…, and any long opaque run that could be a secret.
|
|
116
|
+
message = message.replace(/\b(?:sk-|AIza|ghp_|gsk_)[A-Za-z0-9_\-]{8,}/g, '[redacted]');
|
|
117
|
+
if (!message)
|
|
118
|
+
return `${status}.${hint}`;
|
|
119
|
+
if (message.length > 200)
|
|
120
|
+
message = message.slice(0, 200) + '…';
|
|
121
|
+
if (hint && !/[.!?…]$/.test(message))
|
|
122
|
+
message += '.';
|
|
123
|
+
return `${status} — ${message}${hint}`;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* One prompt in, the model's text out — or a reason, never a throw.
|
|
127
|
+
*
|
|
128
|
+
* Every BYOK verb in this package is optional by construction, so a broken key must not take
|
|
129
|
+
* down a deterministic capture that already succeeded. The reason is a sentence the person
|
|
130
|
+
* running it can act on, and it always names the provider: "nothing came back" and "your app
|
|
131
|
+
* has nothing there" must never be the same empty result.
|
|
132
|
+
*
|
|
133
|
+
* The newest models reject sampling params outright (Anthropic Opus 4.8 / Sonnet 5, OpenAI
|
|
134
|
+
* reasoning models) with a 400. Rather than maintain a capability matrix, send temperature
|
|
135
|
+
* and retry once without it — the same trick, and the same reasoning, as self-healing's
|
|
136
|
+
* resolver.
|
|
137
|
+
*/
|
|
138
|
+
async function callModel(opts, prompt) {
|
|
139
|
+
let res = await attempt(opts, prompt, opts.temperature ?? exports.MODEL_DEFAULTS.temperature);
|
|
140
|
+
if (res && res.status === 400)
|
|
141
|
+
res = await attempt(opts, prompt, undefined);
|
|
142
|
+
if (!res)
|
|
143
|
+
return { ok: false, reason: `No reply from ${opts.provider} — network error or timeout.` };
|
|
144
|
+
if (!res.ok)
|
|
145
|
+
return { ok: false, reason: `${opts.provider} refused the call: ${await providerError(res)}` };
|
|
146
|
+
try {
|
|
147
|
+
return { ok: true, text: extractText(opts.provider, await res.json()) };
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return { ok: false, reason: `Could not read ${opts.provider}'s reply as JSON.` };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Pull JSON out of a model reply, tolerating fences and prose around it.
|
|
155
|
+
*
|
|
156
|
+
* `key` names the array a caller expects (`classifications`); a bare array is accepted too.
|
|
157
|
+
* With no key, the first JSON *object* is returned — the shape a one-answer prompt asks for.
|
|
158
|
+
*/
|
|
159
|
+
function parseJsonReply(text) {
|
|
160
|
+
if (!text || typeof text !== 'string')
|
|
161
|
+
return null;
|
|
162
|
+
const attempts = [text];
|
|
163
|
+
const fenced = /```(?:json)?\s*([\s\S]*?)```/i.exec(text);
|
|
164
|
+
if (fenced)
|
|
165
|
+
attempts.push(fenced[1]);
|
|
166
|
+
const first = text.indexOf('{');
|
|
167
|
+
const last = text.lastIndexOf('}');
|
|
168
|
+
if (first !== -1 && last > first)
|
|
169
|
+
attempts.push(text.slice(first, last + 1));
|
|
170
|
+
const firstArray = text.indexOf('[');
|
|
171
|
+
const lastArray = text.lastIndexOf(']');
|
|
172
|
+
if (firstArray !== -1 && lastArray > firstArray)
|
|
173
|
+
attempts.push(text.slice(firstArray, lastArray + 1));
|
|
174
|
+
for (const candidate of attempts) {
|
|
175
|
+
try {
|
|
176
|
+
return JSON.parse(candidate.trim());
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
/* try the next shape */
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
/** The list form: a bare array, or `{ [key]: [...] }`. */
|
|
185
|
+
function parseJsonList(text, key) {
|
|
186
|
+
const parsed = parseJsonReply(text);
|
|
187
|
+
if (!parsed)
|
|
188
|
+
return null;
|
|
189
|
+
const list = Array.isArray(parsed) ? parsed : parsed?.[key];
|
|
190
|
+
return Array.isArray(list) ? list : null;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/ai/model.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { type AiProvider } from '@ia-qa/self-healing';
|
|
2
|
+
import type { PageCapture, Heading } from '../capture/page';
|
|
3
|
+
import { type CaptureIndex, type Citation, type CitationVerdict } from '../citations';
|
|
4
|
+
import { type ModelOptions } from './model';
|
|
5
|
+
/**
|
|
6
|
+
* The BYOK reading of a page: **what someone comes here to do**, and what they cannot do if
|
|
7
|
+
* it breaks.
|
|
8
|
+
*
|
|
9
|
+
* **Why this is not `classify` with a different prompt.** The deterministic half of this
|
|
10
|
+
* package answers what the app *accepts* — fields, types, required, what moved. `classify`
|
|
11
|
+
* answers what an entry point *is*, against a closed taxonomy. Neither can answer what a
|
|
12
|
+
* human is *trying to do*, and no amount of DOM says it: nothing in a capture states that a
|
|
13
|
+
* declined payment must show a message, or that this form is how a locked-out customer gets
|
|
14
|
+
* back in. That is the axis this file plays, and it is why the two layers are not ranked
|
|
15
|
+
* against each other — neither is a subset of the other.
|
|
16
|
+
*
|
|
17
|
+
* **It sends strictly less than `classify`, and that is a design decision, not an
|
|
18
|
+
* optimisation.** No selectors and no API calls leave for this question: they are the most
|
|
19
|
+
* sensitive half of the payload and they are technical noise for a question that is not
|
|
20
|
+
* technical. `planPayload` below IS what goes, `planEgressNotice` is derived from it, and a
|
|
21
|
+
* field added to one owes a line in the other.
|
|
22
|
+
*
|
|
23
|
+
* **It produces prose, never code.** Test code stays the deterministic generator's job, so
|
|
24
|
+
* that locators come from the contract and healing can repair them. A model sentence that
|
|
25
|
+
* became a locator would be a selector nobody can repair.
|
|
26
|
+
*
|
|
27
|
+
* **It gates nothing, and it ranks nothing.** `coverage` decides the order from what it
|
|
28
|
+
* measured; this adds a labelled line beside each page. A model reading that reordered a
|
|
29
|
+
* measured list would be exactly the confident-and-unfalsifiable number this package spends
|
|
30
|
+
* its design refusing.
|
|
31
|
+
*/
|
|
32
|
+
export declare const PLAN_SCHEMA = "qa-discovery-plan@1";
|
|
33
|
+
export declare const PLAN_FILENAME = "plan.json";
|
|
34
|
+
export interface PlanReading {
|
|
35
|
+
page: string;
|
|
36
|
+
/** What someone comes to this page to do, in the words of whoever uses the app. */
|
|
37
|
+
purpose: string;
|
|
38
|
+
/** What that person cannot do if this page breaks. */
|
|
39
|
+
impact: string;
|
|
40
|
+
/** The model's own number, kept and shown. Nothing is discarded on it. */
|
|
41
|
+
confidence: number;
|
|
42
|
+
evidence: Citation[];
|
|
43
|
+
/**
|
|
44
|
+
* The model looked and could not tell what the page is for.
|
|
45
|
+
*
|
|
46
|
+
* A result, never a gap — and never silently dropped. The capture may genuinely not say
|
|
47
|
+
* (a bare dashboard shell, a page whose whole content arrives after a click), and a
|
|
48
|
+
* proposal nobody can anchor is worth reading as long as it is labelled as one.
|
|
49
|
+
*/
|
|
50
|
+
unclear?: boolean;
|
|
51
|
+
}
|
|
52
|
+
export interface DroppedReading {
|
|
53
|
+
reading: Partial<PlanReading>;
|
|
54
|
+
reason: string;
|
|
55
|
+
rejected: Array<Extract<CitationVerdict, {
|
|
56
|
+
ok: false;
|
|
57
|
+
}>>;
|
|
58
|
+
}
|
|
59
|
+
export type PageReadingOutcome = {
|
|
60
|
+
status: 'read';
|
|
61
|
+
reading: PlanReading;
|
|
62
|
+
} | {
|
|
63
|
+
status: 'unclear';
|
|
64
|
+
reading: PlanReading;
|
|
65
|
+
} | {
|
|
66
|
+
status: 'dropped';
|
|
67
|
+
dropped: DroppedReading;
|
|
68
|
+
}
|
|
69
|
+
/** The call did not succeed. A fact about the request, never about the app. */
|
|
70
|
+
| {
|
|
71
|
+
status: 'failed';
|
|
72
|
+
reason: string;
|
|
73
|
+
};
|
|
74
|
+
/** Every heading sent. The same cap `classify` uses, for the same reason: a prompt is not a page. */
|
|
75
|
+
export declare const HEADING_LIMIT = 25;
|
|
76
|
+
/**
|
|
77
|
+
* Share of pages a heading must appear on to count as the app's shell rather than this page's
|
|
78
|
+
* content. The same 0.6 `autoLayout` uses, through the same `minPagesForLayout` — including
|
|
79
|
+
* its floor of 2, so on a two-page capture a heading cannot become "shared" by appearing
|
|
80
|
+
* once.
|
|
81
|
+
*/
|
|
82
|
+
export declare const SHELL_THRESHOLD = 0.6;
|
|
83
|
+
/**
|
|
84
|
+
* Headings that belong to the shell, not to any page.
|
|
85
|
+
*
|
|
86
|
+
* Measured on a real run: a page whose whole interest was `🗂️ Environment Manager` had its
|
|
87
|
+
* reading cited against `Legal`, `Contact` and `Tools` — the footer. Those citations resolve,
|
|
88
|
+
* so nothing rejected them, and the check that is supposed to anchor the reading was anchoring
|
|
89
|
+
* it to furniture. Removing them from the payload removes the temptation rather than judging
|
|
90
|
+
* it afterwards, which is the same move `offeredPlanPaths` makes for invented paths.
|
|
91
|
+
*
|
|
92
|
+
* This is the heading analogue of `promoteSharedCalls`, and deliberately not a new rule: a
|
|
93
|
+
* third threshold convention in one repo is a third thing to drift.
|
|
94
|
+
*/
|
|
95
|
+
export declare function shellHeadings(pages: PageCapture[]): Set<string>;
|
|
96
|
+
/**
|
|
97
|
+
* This page's own headings, with the shell dropped — and never all of them.
|
|
98
|
+
*
|
|
99
|
+
* A page whose every heading is shared keeps them: a payload with no heading at all would be
|
|
100
|
+
* a page the model cannot read, which is a worse outcome than a noisy one. The exclusion is a
|
|
101
|
+
* narrowing of the evidence, not a way to make a page unreadable.
|
|
102
|
+
*
|
|
103
|
+
* **The index is the one in the capture file, not a position in this list.** A citation
|
|
104
|
+
* resolves `pages/x.json#headings[3]` against the real array, so renumbering a filtered list
|
|
105
|
+
* would silently point every citation at a different heading — the kind of error that
|
|
106
|
+
* produces a verified-looking claim about something else entirely.
|
|
107
|
+
*/
|
|
108
|
+
export declare function ownHeadings(page: PageCapture, shell: Set<string>): Array<{
|
|
109
|
+
index: number;
|
|
110
|
+
heading: Heading;
|
|
111
|
+
}>;
|
|
112
|
+
export interface PlanPayload {
|
|
113
|
+
page: string;
|
|
114
|
+
url: string;
|
|
115
|
+
/**
|
|
116
|
+
* Carried with their `at` paths, like everything else citable.
|
|
117
|
+
*
|
|
118
|
+
* They were flat strings here, and a real run caught it within minutes: offered as citable
|
|
119
|
+
* but shown pathless, so the model cited `#description` — the shape of the *prompt*, not
|
|
120
|
+
* the shape of the capture, where it lives at `#meta.description`. The reading was dropped
|
|
121
|
+
* as an invented path, which is the right refusal for the wrong reason: the prompt was
|
|
122
|
+
* lying about the data and the model was right to trust it. `classify.ts` documents this
|
|
123
|
+
* exact failure, and this file repeated it. **If a field can be cited, it is shown with the
|
|
124
|
+
* path that addresses it.**
|
|
125
|
+
*/
|
|
126
|
+
title: {
|
|
127
|
+
at: string;
|
|
128
|
+
value: string;
|
|
129
|
+
};
|
|
130
|
+
description: {
|
|
131
|
+
at: string;
|
|
132
|
+
value: string;
|
|
133
|
+
};
|
|
134
|
+
headings: Array<{
|
|
135
|
+
at: string;
|
|
136
|
+
level: number;
|
|
137
|
+
text: string;
|
|
138
|
+
}>;
|
|
139
|
+
forms: Array<{
|
|
140
|
+
at: string;
|
|
141
|
+
submits: boolean;
|
|
142
|
+
fields: Record<string, unknown>[];
|
|
143
|
+
}>;
|
|
144
|
+
fields: Record<string, unknown>[];
|
|
145
|
+
/**
|
|
146
|
+
* The names of the other pages in this capture — no URLs, no titles, no content.
|
|
147
|
+
*
|
|
148
|
+
* A page cannot be placed in an app it cannot see: "reset" reads very differently in a
|
|
149
|
+
* capture that also holds `login` and `account` than in one that holds `admin-users`. The
|
|
150
|
+
* cheapest context that answers that, and it is a list of slugs the person already sees in
|
|
151
|
+
* their own capture directory.
|
|
152
|
+
*/
|
|
153
|
+
otherPages: string[];
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Exactly what is sent for one page. The egress notice is written from this function's
|
|
157
|
+
* output, so what it promises cannot drift from what goes.
|
|
158
|
+
*/
|
|
159
|
+
export declare function planPayload(page: PageCapture, otherPages?: string[], shell?: Set<string>): PlanPayload;
|
|
160
|
+
/**
|
|
161
|
+
* Every path the prompt offers, and therefore the only paths a citation may use.
|
|
162
|
+
*
|
|
163
|
+
* The same guardrail `classify` learned the hard way: a model given a shape invents paths
|
|
164
|
+
* that exist in the prompt and nowhere in the capture. Refusing a constructed path removes
|
|
165
|
+
* the class of error instead of catching it afterwards. Note what is NOT here — `apiCalls`,
|
|
166
|
+
* which this verb does not send and therefore may not be cited.
|
|
167
|
+
*/
|
|
168
|
+
export declare function offeredPlanPaths(page: PageCapture, shell?: Set<string>): Set<string>;
|
|
169
|
+
/**
|
|
170
|
+
* A page with nothing citable cannot produce a falsifiable reading, so it is never sent.
|
|
171
|
+
*
|
|
172
|
+
* Not a failure and never counted as one: a page that says nothing about itself is a finding
|
|
173
|
+
* about the app, and inventing a purpose for it is the one thing this layer must not do.
|
|
174
|
+
*/
|
|
175
|
+
export declare function isReadable(page: PageCapture): boolean;
|
|
176
|
+
/**
|
|
177
|
+
* The citations that actually carry the page's subject: a top-level heading, a form, or a
|
|
178
|
+
* field.
|
|
179
|
+
*
|
|
180
|
+
* `classify` learned that a citation pointing at the thing being classified proves nothing.
|
|
181
|
+
* The sibling failure here is a citation that resolves and supports nothing — a footer link,
|
|
182
|
+
* a `meta.description` shared by the whole site. Measured on a real run: every citation on
|
|
183
|
+
* one page was a heading, and half of them were the footer.
|
|
184
|
+
*
|
|
185
|
+
* So at least one citation must land here. The rule is **conditional on the page having
|
|
186
|
+
* one**: a page whose only headings are level 3 and which takes no input cannot satisfy it,
|
|
187
|
+
* and dropping its reading would punish the page for its own markup. Same shape as the
|
|
188
|
+
* offered-paths check — a rule that cannot be met is not applied.
|
|
189
|
+
*/
|
|
190
|
+
export declare function anchorPaths(page: PageCapture, shell?: Set<string>): Set<string>;
|
|
191
|
+
export declare function buildPlanPrompt(page: PageCapture, otherPages?: string[], shell?: Set<string>): string;
|
|
192
|
+
/**
|
|
193
|
+
* What leaves this machine for this verb, said before it leaves.
|
|
194
|
+
*
|
|
195
|
+
* Derived from `planPayload`: every line below is a field that function actually puts in the
|
|
196
|
+
* request, and the two "does NOT leave" lines name what `classify` sends and this does not.
|
|
197
|
+
* That difference is the reason this verb exists as its own notice rather than reusing the
|
|
198
|
+
* other — announcing more than goes is the mirror image of announcing less, and both make
|
|
199
|
+
* the announcement worthless.
|
|
200
|
+
*
|
|
201
|
+
* stderr in every mode, `--json` included: a security announcement, not decoration.
|
|
202
|
+
*/
|
|
203
|
+
export declare function planEgressNotice(provider: AiProvider, model: string, pages: number, planned?: boolean, baseUrl?: string): string;
|
|
204
|
+
/**
|
|
205
|
+
* Validate one reading against the capture.
|
|
206
|
+
*
|
|
207
|
+
* The order is the same as `classify`'s and for the same reason: shape first, then the
|
|
208
|
+
* citations. A reply that is not the requested shape is not a reply whose evidence is worth
|
|
209
|
+
* resolving.
|
|
210
|
+
*/
|
|
211
|
+
export declare function validateReading(raw: Partial<PlanReading> & Record<string, unknown>, page: PageCapture, index: CaptureIndex, offered?: Set<string>, anchors?: Set<string>): {
|
|
212
|
+
ok: true;
|
|
213
|
+
value: PlanReading;
|
|
214
|
+
} | {
|
|
215
|
+
ok: false;
|
|
216
|
+
dropped: DroppedReading;
|
|
217
|
+
};
|
|
218
|
+
/**
|
|
219
|
+
* One page in, one outcome out. Never throws: this layer is optional by construction, and a
|
|
220
|
+
* bad key must not take down a deterministic capture that already succeeded.
|
|
221
|
+
*/
|
|
222
|
+
export declare function readPage(page: PageCapture, index: CaptureIndex, opts: ModelOptions, otherPages?: string[],
|
|
223
|
+
/** Headings the whole app shares. The caller computes it once, over every captured page. */
|
|
224
|
+
shell?: Set<string>): Promise<PageReadingOutcome>;
|
package/dist/ai/plan.js
ADDED
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/ai/plan.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/args.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.KNOWN_FLAGS = void 0;
|
|
|
4
4
|
exports.rejectUnknownFlags = rejectUnknownFlags;
|
|
5
5
|
/** Flag validation for the hand-rolled CLI parser — mirrors `@ia-qa/self-healing`'s `cli/args.ts`. */
|
|
6
6
|
exports.KNOWN_FLAGS = {
|
|
7
|
+
skill: ["--install", "--user", "--dir", "--print", "--force"],
|
|
7
8
|
scan: [
|
|
8
9
|
'--depth',
|
|
9
10
|
'--max',
|
package/dist/cli/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":";;;AAyBA,gDAQC;AAjCD,sGAAsG;AACzF,QAAA,WAAW,GAAsC;IAC5D,KAAK,EAAE,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAC;IAC7D,IAAI,EAAE;QACJ,SAAS;QACT,OAAO;QACP,aAAa;QACb,eAAe;QACf,WAAW;QACX,cAAc;QACd,cAAc;QACd,QAAQ;QACR,eAAe;QACf,qBAAqB;QACrB,QAAQ;QACR,QAAQ;QACR,UAAU;QACV,QAAQ;KACT;IACD,KAAK,EAAE,CAAC,OAAO,EAAE,WAAW,CAAC;IAC7B,OAAO,EAAE,CAAC,QAAQ,EAAE,SAAS,CAAC;IAC9B,QAAQ,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,QAAQ,CAAC;IAC1C,QAAQ,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC;CACzC,CAAC;AAEF,SAAgB,kBAAkB,CAAC,OAAe,EAAE,IAAc;IAChE,MAAM,KAAK,GAAG,mBAAW,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO;IAChC,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;QACrB,IAAI,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,SAAS;QACjC,IAAI,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAE,SAAS;QAChC,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,yBAAyB,OAAO,kDAAkD,CAAC,CAAC;IACxH,CAAC;AACH,CAAC"}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
declare const HELP = "ia-qa-discover \u2014 deterministic surface reconnaissance for a web app\n\nF1 of QA Discovery: crawl an app's same-origin pages and capture what each one\nis structurally about \u2014 title, headings, forms, and observed API calls. No\nLLM, no verdict: this is recon, not a gate. Runs entirely on your machine;\nnothing is uploaded anywhere.\n\n[FOR AI AGENTS] You are driving this for a human. Run `scan` to capture the\napp's surface, then read .ia-qa-discovery/capture/surface.json (the index) and\nthe per-page files it points at \u2014 that is the evidence for anything you\nreport about this app. Never assert a page's purpose or risk from the URL\nalone; ground it in the captured meta/headings/forms/apiCalls. Never read a\npage count as coverage: if scan warns that pages still show a login form, you\nmeasured the wall, not the app. You CANNOT run `login` \u2014 it waits for a human\nat a browser; ask them to run it, then scan again.\n`coverage` answers \"what does their suite NOT test\" \u2014 but only from a suite that was\nwatched running (one `ia-qa-heal run`, or one `ia-qa-pal tour --suite`). If it says there is no\nmeasurement, report that, never \"the suite covers nothing\": a contract means someone\nconfigured a page, only a watched run means a test went there.\n\nUsage:\n ia-qa-discover login Open a visible browser, log in by hand, keep the\n session (.ia-qa-discovery/session.json) for scan\n to reuse. The general answer to a login wall: it\n models nothing, so SSO, MFA, a consent screen or\n a magic link all work \u2014 you do them.\n Needs a terminal, refuses under CI. The file holds\n live cookies: gitignored, stays on this machine.\n --url <path> page to open (default: your baseUrl)\n --session <f> where to write it\n Already have a storageState (a Playwright globalSetup\n writes one)? Skip this verb: point scan at it with\n --session <file> or \"session\" in config.json.\n ia-qa-discover scan [url] Crawl same-origin pages from <url> (or the saved\n baseUrl) and capture each page's structure.\n Writes .ia-qa-discovery/capture/, diffs against\n the previous run, appends history.jsonl.\n --depth <n> crawl link-depth (default 2)\n --max <n> page cap (default 60)\n --deep also open menus, tabs and dialogs one level\n down and capture the fields they reveal. Every\n non-GET request is blocked for the duration,\n so a click cannot mutate anything server-side.\n --deep-budget <n> max controls clicked per page (default 60)\n --no-sitemap do not read sitemap.xml / robots.txt\n --no-reveal do not open menus/dropdowns for hidden links\n --strict-host treat www./apex as different hosts\n --session <file> reuse an existing storageState (login) file\n --no-network skip API/network-call capture this run\n --network-threshold <n> shared-call promotion threshold 0-1 (default 0.6)\n --save persist this baseUrl to config.json\n --json machine-readable summary on stdout\n --report [file.html] branded ia-qa.com HTML dossier of the\n app (default ia-qa-discover-report.html)\n --open open that report in the browser\n ia-qa-discover coverage What your test suite does NOT test. Compares the\n pages this scan found against the pages your suite\n was OBSERVED visiting, ranks the gap by what each\n page takes as input, and writes coverage-map.json.\n Reads local files only \u2014 no browser, no network.\n Needs one watched run of your suite \u2014\n \"ia-qa-heal run\" or \"ia-qa-pal tour --suite\":\n without a watched run there is no measurement, and\n it says so instead of showing a number.\n Declare pages you deliberately do not test in\n \"outOfScope\" in config.json \u2014 they stay counted\n and named, never silently dropped.\n --json machine-readable map on stdout\n --report [file.html] branded HTML dossier with the gap\n (default ia-qa-discover-coverage.html)\n --open open that report in the browser\n ia-qa-discover generate [page\u2026] A starting Playwright suite for the pages nothing\n tests \u2014 one file per page, into its own directory.\n Every locator names an element the healing contract\n holds, so the file is REPAIRED when the app moves\n instead of going red. A field with no test id, id or\n name is left out and says so: its selector would be\n positional, which nobody can test reliably.\n Proposals \u2014 nothing is added to your suite, and the\n assertions stay yours (the app declares what it\n accepts, never what it promises).\n With no page, it generates for the coverage gap.\n --out <dir> where to write (default\n .ia-qa-discovery/generated/)\n --force replace files already there\n --json machine-readable summary on stdout\n ia-qa-discover history Recorded scan runs \u2014 pages/forms/API-surface trend.\n --json --limit <n> (default 500)\n ia-qa-discover version Print the installed version (also --version, -v)\n ia-qa-discover help Show this help\n\nGlobal:\n --config <dir> Resolve .ia-qa-discovery/ under <dir> instead of the\n current directory. Also IAQA_DISCOVERY_CONFIG_DIR=<dir>.\n";
|
|
2
|
+
declare const HELP = "ia-qa-discover \u2014 deterministic surface reconnaissance for a web app\n\nF1 of QA Discovery: crawl an app's same-origin pages and capture what each one\nis structurally about \u2014 title, headings, forms, and observed API calls. No\nLLM, no verdict: this is recon, not a gate. Runs entirely on your machine;\nnothing is uploaded anywhere.\n\n[FOR AI AGENTS] You are driving this for a human. Run `scan` to capture the\napp's surface, then read .ia-qa-discovery/capture/surface.json (the index) and\nthe per-page files it points at \u2014 that is the evidence for anything you\nreport about this app. Never assert a page's purpose or risk from the URL\nalone; ground it in the captured meta/headings/forms/apiCalls. Never read a\npage count as coverage: if scan warns that pages still show a login form, you\nmeasured the wall, not the app. You CANNOT run `login` \u2014 it waits for a human\nat a browser; ask them to run it, then scan again.\n`coverage` answers \"what does their suite NOT test\" \u2014 but only from a suite that was\nwatched running (one `ia-qa-heal run`, or one `ia-qa-pal tour --suite`). If it says there is no\nmeasurement, report that, never \"the suite covers nothing\": a contract means someone\nconfigured a page, only a watched run means a test went there.\n\nUsage:\n ia-qa-discover login Open a visible browser, log in by hand, keep the\n session (.ia-qa-discovery/session.json) for scan\n to reuse. The general answer to a login wall: it\n models nothing, so SSO, MFA, a consent screen or\n a magic link all work \u2014 you do them.\n Needs a terminal, refuses under CI. The file holds\n live cookies: gitignored, stays on this machine.\n --url <path> page to open (default: your baseUrl)\n --session <f> where to write it\n Already have a storageState (a Playwright globalSetup\n writes one)? Skip this verb: point scan at it with\n --session <file> or \"session\" in config.json.\n ia-qa-discover scan [url] Crawl same-origin pages from <url> (or the saved\n baseUrl) and capture each page's structure.\n Writes .ia-qa-discovery/capture/, diffs against\n the previous run, appends history.jsonl.\n --depth <n> crawl link-depth (default 2)\n --max <n> page cap (default 60)\n --deep also open menus, tabs and dialogs one level\n down and capture the fields they reveal. Every\n non-GET request is blocked for the duration,\n so a click cannot mutate anything server-side.\n --deep-budget <n> max controls clicked per page (default 60)\n --no-sitemap do not read sitemap.xml / robots.txt\n --no-reveal do not open menus/dropdowns for hidden links\n --strict-host treat www./apex as different hosts\n --session <file> reuse an existing storageState (login) file\n --no-network skip API/network-call capture this run\n --network-threshold <n> shared-call promotion threshold 0-1 (default 0.6)\n --save persist this baseUrl to config.json\n --json machine-readable summary on stdout\n --report [file.html] branded ia-qa.com HTML dossier of the\n app (default ia-qa-discover-report.html)\n --open open that report in the browser\n ia-qa-discover coverage What your test suite does NOT test. Compares the\n pages this scan found against the pages your suite\n was OBSERVED visiting, ranks the gap by what each\n page takes as input, and writes coverage-map.json.\n Reads local files only \u2014 no browser, no network.\n Needs one watched run of your suite \u2014\n \"ia-qa-heal run\" or \"ia-qa-pal tour --suite\":\n without a watched run there is no measurement, and\n it says so instead of showing a number.\n Declare pages you deliberately do not test in\n \"outOfScope\" in config.json \u2014 they stay counted\n and named, never silently dropped.\n --json machine-readable map on stdout\n --report [file.html] branded HTML dossier with the gap\n (default ia-qa-discover-coverage.html)\n --open open that report in the browser\n ia-qa-discover generate [page\u2026] A starting Playwright suite for the pages nothing\n tests \u2014 one file per page, into its own directory.\n Every locator names an element the healing contract\n holds, so the file is REPAIRED when the app moves\n instead of going red. A field with no test id, id or\n name is left out and says so: its selector would be\n positional, which nobody can test reliably.\n Proposals \u2014 nothing is added to your suite, and the\n assertions stay yours (the app declares what it\n accepts, never what it promises).\n With no page, it generates for the coverage gap.\n --out <dir> where to write (default\n .ia-qa-discovery/generated/)\n --force replace files already there\n --json machine-readable summary on stdout\n ia-qa-discover skill This CLI's agent instructions: the verb for each\n question, what every refusal means, and what an\n agent must never do. Read them before driving\n anything else here.\n --print write them to stdout and stop\n --install write .claude/skills/ia-qa-discover/\n --user into ~/ instead of this project\n --dir <d> into another project root\n --force overwrite an edited copy\n ia-qa-discover history Recorded scan runs \u2014 pages/forms/API-surface trend.\n --json --limit <n> (default 500)\n ia-qa-discover version Print the installed version (also --version, -v)\n ia-qa-discover help Show this help\n\nGlobal:\n --config <dir> Resolve .ia-qa-discovery/ under <dir> instead of the\n current directory. Also IAQA_DISCOVERY_CONFIG_DIR=<dir>.\n";
|
|
3
3
|
declare function verbHelp(command: string | undefined): string | null;
|
|
4
4
|
declare function main(): Promise<void>;
|
|
5
5
|
export { HELP, verbHelp, main };
|
package/dist/cli/index.js
CHANGED
|
@@ -11,6 +11,7 @@ const login_1 = require("./login");
|
|
|
11
11
|
const history_1 = require("./history");
|
|
12
12
|
const coverage_1 = require("./coverage");
|
|
13
13
|
const generate_1 = require("./generate");
|
|
14
|
+
const skill_1 = require("./skill");
|
|
14
15
|
// dist/cli/ → package root; npm ships package.json in every tarball.
|
|
15
16
|
const VERSION = require('../../package.json').version;
|
|
16
17
|
const HELP = `ia-qa-discover — deterministic surface reconnaissance for a web app
|
|
@@ -99,6 +100,15 @@ Usage:
|
|
|
99
100
|
.ia-qa-discovery/generated/)
|
|
100
101
|
--force replace files already there
|
|
101
102
|
--json machine-readable summary on stdout
|
|
103
|
+
ia-qa-discover skill This CLI's agent instructions: the verb for each
|
|
104
|
+
question, what every refusal means, and what an
|
|
105
|
+
agent must never do. Read them before driving
|
|
106
|
+
anything else here.
|
|
107
|
+
--print write them to stdout and stop
|
|
108
|
+
--install write .claude/skills/ia-qa-discover/
|
|
109
|
+
--user into ~/ instead of this project
|
|
110
|
+
--dir <d> into another project root
|
|
111
|
+
--force overwrite an edited copy
|
|
102
112
|
ia-qa-discover history Recorded scan runs — pages/forms/API-surface trend.
|
|
103
113
|
--json --limit <n> (default 500)
|
|
104
114
|
ia-qa-discover version Print the installed version (also --version, -v)
|
|
@@ -180,6 +190,9 @@ async function main() {
|
|
|
180
190
|
case 'history':
|
|
181
191
|
await (0, history_1.runHistory)(rest);
|
|
182
192
|
break;
|
|
193
|
+
case 'skill':
|
|
194
|
+
await (0, skill_1.runSkill)(rest);
|
|
195
|
+
break;
|
|
183
196
|
case 'version':
|
|
184
197
|
case '--version':
|
|
185
198
|
case '-v':
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ia-qa-discover skill` — install this CLI's agent instructions.
|
|
3
|
+
*
|
|
4
|
+
* The markdown is the payload; the verb is the load-bearing half. Agents load skills from
|
|
5
|
+
* `.claude/skills/`, never from `node_modules`, so a `SKILL.md` that only ships in the
|
|
6
|
+
* tarball is a copy of the doctrine nobody reads.
|
|
7
|
+
*
|
|
8
|
+
* The implementation is `@ia-qa/self-healing`'s, imported rather than copied: three packages
|
|
9
|
+
* needing the identical verb is exactly the case where a third copy starts drifting — and it
|
|
10
|
+
* would drift in the file whose whole subject is "the instructions must reach the agent".
|
|
11
|
+
* Only the name, the binary and this package's root differ.
|
|
12
|
+
*/
|
|
13
|
+
export declare const SKILL_NAME = "ia-qa-discover";
|
|
14
|
+
export declare const skillSourcePath: () => string;
|
|
15
|
+
export declare const readSkill: () => string;
|
|
16
|
+
export declare const skillTargetDir: (opts: {
|
|
17
|
+
user?: boolean;
|
|
18
|
+
dir?: string;
|
|
19
|
+
}) => string;
|
|
20
|
+
export declare const runSkill: (args: string[]) => Promise<void>;
|