@ia-qa/pal 0.1.0 → 0.3.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/dist/cli/index.js CHANGED
@@ -1,8 +1,44 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
3
36
  Object.defineProperty(exports, "__esModule", { value: true });
4
- const tour_1 = require("../tour");
37
+ const readline = __importStar(require("readline/promises"));
38
+ const path = __importStar(require("path"));
5
39
  const report_1 = require("../report");
40
+ const setup_1 = require("../setup");
41
+ const run_1 = require("../run");
6
42
  const VERSION = require('../../package.json').version;
7
43
  const HELP = `ia-qa-pal — a QA pal next to your team
8
44
 
@@ -13,23 +49,51 @@ Runs entirely on your machine with a local browser. No LLM, nothing uploaded.
13
49
  [FOR AI AGENTS] You are driving this for a human. Read the whole report, the
14
50
  "Not seen" section included: a clean tour over part of an app is not a clean
15
51
  app, and you must say which part was not seen. A first tour establishes the
16
- baseline — never report it as a pass. Never run \`ia-qa-heal login\`: it waits
17
- for a human at a browser; ask them to run it.
52
+ baseline — never report it as a pass. Set a project up with flags only
53
+ (\`setup --url … --login … --yes\`), with answers the human gave you — never
54
+ guessed. Never run \`ia-qa-heal login\`: it waits for a human at a browser; ask
55
+ them. Never apply the repairs a tour proposes without the human's review.
18
56
 
19
57
  Usage:
20
58
  ia-qa-pal tour First run: every page on the surface, the first
21
59
  batch in depth, checks, baseline.
22
60
  Next runs: re-capture, compare, check.
61
+ With a test suite declared: its selectors are
62
+ read, repairs are proposed (never applied).
63
+ No config yet: asks the setup questions first
64
+ (in a terminal), otherwise says what is missing.
23
65
  --more explore the next batch in depth instead
66
+ --suite also run the testCommand from config.json once,
67
+ with the capture hook, to learn which pages
68
+ the suite never visits
69
+ --background start the tour detached and return at once;
70
+ follow it with \`ia-qa-pal status\`
24
71
  --batch <n> pages per depth batch (default 10)
25
72
  --json the report as one JSON document on stdout
26
73
  --session <file> a Playwright storageState to reuse
27
74
  --config <dir> project holding .ia-qa/ (default: here)
28
75
 
29
- Exit codes: 0 nothing to decide · 1 something broke or needs a decision ·
30
- 2 could not answer (no config, nothing mapped, refused).
76
+ ia-qa-pal status The tour running now (phase, progress) or the
77
+ last one (its report).
78
+ --json as one JSON document
79
+ --config <dir> project folder (default: here)
80
+
81
+ ia-qa-pal setup Write .ia-qa/config.json: the app address, how it
82
+ logs in, its pages (from sitemap.xml, else by
83
+ following its links), and your test suite.
84
+ Never overwrites a config.
85
+ --url <https://…> the app
86
+ --login none|browser|session none; by hand in a browser (then run
87
+ ${setup_1.LOGIN_COMMAND});
88
+ or a storageState your suite already writes
89
+ --session <file> that storageState (with --login session)
90
+ --tests <dir>[,<dir>…] folders of your end-to-end suite
91
+ --test-command "<cmd>" the command that runs it (else detected)
92
+ --yes write without asking
93
+ --config <dir> project folder (default: here)
31
94
 
32
- Needs a .ia-qa/config.json with a baseUrl and pages: ia-qa-heal init.
95
+ Exit codes: 0 nothing to decide · 1 something broke or needs a decision ·
96
+ 2 could not answer (not set up, nothing mapped, refused, already running).
33
97
  `;
34
98
  function valueOf(args, flag) {
35
99
  const i = args.indexOf(flag);
@@ -40,6 +104,61 @@ function valueOf(args, flag) {
40
104
  throw new Error(`${flag} needs a value.`);
41
105
  return v;
42
106
  }
107
+ const interactive = () => !!process.stdin.isTTY && !!process.stderr.isTTY && !process.env.CI;
108
+ async function withPrompt(fn) {
109
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
110
+ try {
111
+ return await fn((q) => rl.question(q));
112
+ }
113
+ finally {
114
+ rl.close();
115
+ }
116
+ }
117
+ function loginFlag(args) {
118
+ const v = valueOf(args, '--login');
119
+ if (v === undefined)
120
+ return undefined;
121
+ if (v !== 'none' && v !== 'browser' && v !== 'session')
122
+ throw new Error('--login takes none, browser or session.');
123
+ return v;
124
+ }
125
+ function printSetup(outcome, json) {
126
+ if (json) {
127
+ process.stdout.write(`${JSON.stringify({ schema: 'ia-qa-pal/setup@1', ...outcome }, null, 2)}\n`);
128
+ return;
129
+ }
130
+ if (!outcome.written) {
131
+ for (const p of outcome.problems)
132
+ process.stderr.write(`❌ ${p}\n`);
133
+ return;
134
+ }
135
+ process.stderr.write(`\n✅ Written: ${outcome.file}\n`);
136
+ if (outcome.next === setup_1.LOGIN_COMMAND) {
137
+ process.stderr.write(`\nNext, by a person at a browser (an agent cannot do this):\n ${setup_1.LOGIN_COMMAND}\nthen:\n ia-qa-pal tour\n`);
138
+ if (outcome.pagesFrom !== 'sitemap') {
139
+ process.stderr.write('\nThe pages were found without logging in. Once logged in, list the ones behind the login:\n' +
140
+ ' npx -p @ia-qa/self-healing ia-qa-heal discover --crawl --apply\n');
141
+ }
142
+ }
143
+ else {
144
+ process.stderr.write(`\nNext:\n ia-qa-pal tour\n`);
145
+ }
146
+ }
147
+ function renderStatus(run, dir) {
148
+ if (!run)
149
+ return 'No tour has run on this project yet. Start one: ia-qa-pal tour\n';
150
+ if (run.status === 'running') {
151
+ const p = run.progress;
152
+ const where = p ? `${p.phase}${p.total ? ` ${p.done ?? 0}/${p.total}` : ''}${p.page ? ` · ${p.page}` : ''}` : 'starting';
153
+ return `⏳ A tour is running (pid ${run.pid}, since ${run.startedAt}) — ${where}\n log: ${(0, run_1.logFile)(dir)}\n`;
154
+ }
155
+ if (run.status === 'interrupted') {
156
+ return `⚠ The last tour stopped before finishing (pid ${run.pid}, started ${run.startedAt}, last seen ${run.updatedAt}). Nothing it did after that is reported. Run it again: ia-qa-pal tour\n`;
157
+ }
158
+ if (run.status === 'failed')
159
+ return `❌ The last tour failed (${run.finishedAt}): ${run.error}\n`;
160
+ return `Last tour finished ${run.finishedAt}.\n\n${run.report ? (0, report_1.renderReport)(run.report) : ''}`;
161
+ }
43
162
  async function main() {
44
163
  const args = process.argv.slice(2);
45
164
  const command = args[0];
@@ -51,32 +170,74 @@ async function main() {
51
170
  process.stdout.write(`${VERSION}\n`);
52
171
  return;
53
172
  }
54
- if (command !== 'tour') {
55
- process.stderr.write(`Unknown command "${command}". Run ia-qa-pal help.\n`);
56
- process.exitCode = 2;
57
- return;
58
- }
59
173
  const json = args.includes('--json');
60
174
  try {
175
+ const dir = path.resolve(valueOf(args, '--config') ?? process.cwd());
176
+ if (command === 'setup') {
177
+ const tests = valueOf(args, '--tests');
178
+ const options = {
179
+ dir,
180
+ url: valueOf(args, '--url'),
181
+ login: loginFlag(args),
182
+ session: valueOf(args, '--session'),
183
+ tests: tests ? tests.split(',') : undefined,
184
+ testCommand: valueOf(args, '--test-command'),
185
+ yes: args.includes('--yes'),
186
+ };
187
+ const outcome = interactive() && !json ? await withPrompt((ask) => (0, setup_1.runSetup)({ ...options, ask })) : await (0, setup_1.runSetup)(options);
188
+ printSetup(outcome, json);
189
+ process.exitCode = outcome.written ? 0 : 2;
190
+ return;
191
+ }
192
+ if (command === 'status') {
193
+ const run = (0, run_1.readRun)(dir);
194
+ process.stdout.write(json ? `${JSON.stringify({ schema: 'ia-qa-pal/status@1', run }, null, 2)}\n` : renderStatus(run, dir));
195
+ process.exitCode = run?.status === 'done' ? run.exitCode ?? 0 : run ? 0 : 2;
196
+ return;
197
+ }
198
+ if (command !== 'tour') {
199
+ process.stderr.write(`Unknown command "${command}". Run ia-qa-pal help.\n`);
200
+ process.exitCode = 2;
201
+ return;
202
+ }
61
203
  const batchRaw = valueOf(args, '--batch');
62
204
  const batch = batchRaw === undefined ? undefined : Number(batchRaw);
63
205
  if (batch !== undefined && (!Number.isInteger(batch) || batch < 1))
64
206
  throw new Error('--batch needs a positive whole number.');
65
- const result = await (0, tour_1.runTour)({
66
- dir: valueOf(args, '--config'),
67
- batch,
68
- more: args.includes('--more'),
69
- session: valueOf(args, '--session'),
70
- });
207
+ const tourOptions = { more: args.includes('--more'), suite: args.includes('--suite'), batch, session: valueOf(args, '--session') };
208
+ if (args.includes('--background')) {
209
+ const started = (0, run_1.startBackground)(dir, tourOptions);
210
+ if (json)
211
+ process.stdout.write(`${JSON.stringify({ schema: 'ia-qa-pal/background@1', ...started }, null, 2)}\n`);
212
+ else if (started.started)
213
+ process.stderr.write(`⏳ Tour started in the background (pid ${started.pid}). Follow it: ia-qa-pal status\n log: ${started.log}\n`);
214
+ else
215
+ process.stderr.write(`❌ ${started.reason}\n`);
216
+ process.exitCode = started.started ? 0 : 2;
217
+ return;
218
+ }
219
+ if (!(0, setup_1.configExists)(dir) && interactive() && !json) {
220
+ process.stderr.write('This project is not set up yet — a few questions first.\n\n');
221
+ const outcome = await withPrompt((ask) => (0, setup_1.runSetup)({ dir, session: tourOptions.session, ask }));
222
+ printSetup(outcome, false);
223
+ if (!outcome.written || outcome.next !== 'ia-qa-pal tour') {
224
+ process.exitCode = outcome.written ? 0 : 2;
225
+ return;
226
+ }
227
+ process.stderr.write('\n');
228
+ }
229
+ const result = await (0, run_1.trackedTour)({ dir, ...tourOptions });
71
230
  process.stdout.write(json ? `${JSON.stringify(result, null, 2)}\n` : (0, report_1.renderReport)(result));
72
231
  process.exitCode = result.exitCode;
73
232
  }
74
233
  catch (err) {
75
234
  const message = err instanceof Error ? err.message : String(err);
76
- if (json)
77
- process.stdout.write(`${JSON.stringify({ schema: 'ia-qa-pal/tour@1', error: message, exitCode: 2 }, null, 2)}\n`);
78
- else
235
+ if (json) {
236
+ process.stdout.write(`${JSON.stringify({ error: message, ...(err instanceof run_1.TourAlreadyRunning ? { running: err.run } : {}), exitCode: 2 }, null, 2)}\n`);
237
+ }
238
+ else {
79
239
  process.stderr.write(`❌ ${message}\n`);
240
+ }
80
241
  process.exitCode = 2;
81
242
  }
82
243
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,10 @@
1
1
  export { runTour } from './tour';
2
- export type { TourOptions, TourResult } from './tour';
2
+ export type { TourOptions, TourResult, TourEvent } from './tour';
3
+ export { trackedTour, startBackground, readRun, activeRun, TourAlreadyRunning } from './run';
4
+ export type { RunRecord, RunView, BackgroundStart } from './run';
5
+ export type { SuiteInventory, RepairPlan, SuiteAudit, SuiteRun } from './suite';
3
6
  export { renderReport } from './report';
7
+ export { runSetup, LOGIN_COMMAND } from './setup';
8
+ export type { SetupOptions, SetupOutcome, PalLogin } from './setup';
4
9
  export { selectBatch, needingDepth, estimate, splitFindings, exitCodeFor, DEFAULT_BATCH } from './plan';
5
10
  export type { Estimate, Pace, CheckSplit } from './plan';
package/dist/index.js CHANGED
@@ -1,10 +1,19 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_BATCH = exports.exitCodeFor = exports.splitFindings = exports.estimate = exports.needingDepth = exports.selectBatch = exports.renderReport = exports.runTour = void 0;
3
+ exports.DEFAULT_BATCH = exports.exitCodeFor = exports.splitFindings = exports.estimate = exports.needingDepth = exports.selectBatch = exports.LOGIN_COMMAND = exports.runSetup = exports.renderReport = exports.TourAlreadyRunning = exports.activeRun = exports.readRun = exports.startBackground = exports.trackedTour = exports.runTour = void 0;
4
4
  var tour_1 = require("./tour");
5
5
  Object.defineProperty(exports, "runTour", { enumerable: true, get: function () { return tour_1.runTour; } });
6
+ var run_1 = require("./run");
7
+ Object.defineProperty(exports, "trackedTour", { enumerable: true, get: function () { return run_1.trackedTour; } });
8
+ Object.defineProperty(exports, "startBackground", { enumerable: true, get: function () { return run_1.startBackground; } });
9
+ Object.defineProperty(exports, "readRun", { enumerable: true, get: function () { return run_1.readRun; } });
10
+ Object.defineProperty(exports, "activeRun", { enumerable: true, get: function () { return run_1.activeRun; } });
11
+ Object.defineProperty(exports, "TourAlreadyRunning", { enumerable: true, get: function () { return run_1.TourAlreadyRunning; } });
6
12
  var report_1 = require("./report");
7
13
  Object.defineProperty(exports, "renderReport", { enumerable: true, get: function () { return report_1.renderReport; } });
14
+ var setup_1 = require("./setup");
15
+ Object.defineProperty(exports, "runSetup", { enumerable: true, get: function () { return setup_1.runSetup; } });
16
+ Object.defineProperty(exports, "LOGIN_COMMAND", { enumerable: true, get: function () { return setup_1.LOGIN_COMMAND; } });
8
17
  var plan_1 = require("./plan");
9
18
  Object.defineProperty(exports, "selectBatch", { enumerable: true, get: function () { return plan_1.selectBatch; } });
10
19
  Object.defineProperty(exports, "needingDepth", { enumerable: true, get: function () { return plan_1.needingDepth; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+BAAiC;AAAxB,+FAAA,OAAO,OAAA;AAEhB,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AACrB,+BAAwG;AAA/F,mGAAA,WAAW,OAAA;AAAE,oGAAA,YAAY,OAAA;AAAE,gGAAA,QAAQ,OAAA;AAAE,qGAAA,aAAa,OAAA;AAAE,mGAAA,WAAW,OAAA;AAAE,qGAAA,aAAa,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,+BAAiC;AAAxB,+FAAA,OAAO,OAAA;AAEhB,6BAA6F;AAApF,kGAAA,WAAW,OAAA;AAAE,sGAAA,eAAe,OAAA;AAAE,8FAAA,OAAO,OAAA;AAAE,gGAAA,SAAS,OAAA;AAAE,yGAAA,kBAAkB,OAAA;AAG7E,mCAAwC;AAA/B,sGAAA,YAAY,OAAA;AACrB,iCAAkD;AAAzC,iGAAA,QAAQ,OAAA;AAAE,sGAAA,aAAa,OAAA;AAEhC,+BAAwG;AAA/F,mGAAA,WAAW,OAAA;AAAE,oGAAA,YAAY,OAAA;AAAE,gGAAA,QAAQ,OAAA;AAAE,qGAAA,aAAa,OAAA;AAAE,mGAAA,WAAW,OAAA;AAAE,qGAAA,aAAa,OAAA"}
@@ -0,0 +1,89 @@
1
+ #!/usr/bin/env node
2
+ export declare const TOOLS: ({
3
+ name: string;
4
+ description: string;
5
+ inputSchema: {
6
+ type: string;
7
+ properties: {
8
+ project_dir: {
9
+ type: string;
10
+ description: string;
11
+ };
12
+ url?: undefined;
13
+ login?: undefined;
14
+ session_file?: undefined;
15
+ tests?: undefined;
16
+ more?: undefined;
17
+ suite?: undefined;
18
+ batch?: undefined;
19
+ };
20
+ required?: undefined;
21
+ };
22
+ } | {
23
+ name: string;
24
+ description: string;
25
+ inputSchema: {
26
+ type: string;
27
+ properties: {
28
+ url: {
29
+ type: string;
30
+ description: string;
31
+ };
32
+ login: {
33
+ type: string;
34
+ enum: string[];
35
+ description: string;
36
+ };
37
+ session_file: {
38
+ type: string;
39
+ description: string;
40
+ };
41
+ tests: {
42
+ type: string;
43
+ items: {
44
+ type: string;
45
+ };
46
+ description: string;
47
+ };
48
+ project_dir: {
49
+ type: string;
50
+ description: string;
51
+ };
52
+ more?: undefined;
53
+ suite?: undefined;
54
+ batch?: undefined;
55
+ };
56
+ required: string[];
57
+ };
58
+ } | {
59
+ name: string;
60
+ description: string;
61
+ inputSchema: {
62
+ type: string;
63
+ properties: {
64
+ more: {
65
+ type: string;
66
+ description: string;
67
+ };
68
+ suite: {
69
+ type: string;
70
+ description: string;
71
+ };
72
+ batch: {
73
+ type: string;
74
+ description: string;
75
+ };
76
+ project_dir: {
77
+ type: string;
78
+ description: string;
79
+ };
80
+ url?: undefined;
81
+ login?: undefined;
82
+ session_file?: undefined;
83
+ tests?: undefined;
84
+ };
85
+ required?: undefined;
86
+ };
87
+ })[];
88
+ export declare function projectDir(given: unknown, root?: string): string;
89
+ export declare function callTool(name: string, args: Record<string, any>, root?: string): Promise<unknown>;
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ exports.TOOLS = void 0;
38
+ exports.projectDir = projectDir;
39
+ exports.callTool = callTool;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const setup_1 = require("../setup");
43
+ const run_1 = require("../run");
44
+ const report_1 = require("../report");
45
+ /**
46
+ * @ia-qa/pal — local stdio MCP server.
47
+ *
48
+ * Dependency-free JSON-RPC 2.0 over stdio, the same shape as `@ia-qa/self-healing` and
49
+ * `@ia-qa/qa-discovery`. It exposes what the CLI exposes, so a person and an agent get one
50
+ * contract. Three restraints are load-bearing:
51
+ *
52
+ * - **A tour never blocks the call.** `pal_tour` starts it in the background and returns at
53
+ * once; `pal_status` reads progress and, when done, the report. A tour takes minutes, and a
54
+ * client that times out mid-call would leave the agent believing nothing ran.
55
+ * - **No tool takes a command.** The test command comes from config.json, written by a person.
56
+ * An agent steered by content it read cannot make this server run something else.
57
+ * - **No login tool.** `ia-qa-heal login` waits for a person at a browser.
58
+ *
59
+ * stdout carries the protocol only; all logs go to stderr.
60
+ */
61
+ const PROTOCOL_VERSION = '2024-11-05';
62
+ const SERVER_INFO = { name: 'ia-qa-pal', version: require('../../package.json').version };
63
+ const CLI_ENTRY = path.join(__dirname, '..', 'cli', 'index.js');
64
+ const PROJECT_DIR = {
65
+ type: 'string',
66
+ description: 'Project folder holding .ia-qa/, relative to where this server runs. Default: the server’s own folder.',
67
+ };
68
+ exports.TOOLS = [
69
+ {
70
+ name: 'pal_project',
71
+ description: 'Where this project stands with ia-qa-pal: whether it is set up, its level (0 public pages, 1 with a login, 2 with its test suite), how many pages are configured, whether a baseline exists, and whether a tour is running. Read-only. Call it first.',
72
+ inputSchema: { type: 'object', properties: { project_dir: PROJECT_DIR } },
73
+ },
74
+ {
75
+ name: 'pal_setup',
76
+ description: 'Write .ia-qa/config.json for a project that has none: the app address, how it logs in, its pages (read from sitemap.xml, else by following its links — same host, GET only, at most 50 pages), and optionally the folders of its end-to-end suite. Never overwrites an existing config. Call it ONLY with answers the human gave you — ask them where the app lives and how it logs in; never guess. Login "browser" means the human must then run `' +
77
+ setup_1.LOGIN_COMMAND +
78
+ '` themselves: you cannot, it waits for a person at a browser. The command that runs the suite is detected from package.json or set by the human with the CLI — this tool never takes one.',
79
+ inputSchema: {
80
+ type: 'object',
81
+ properties: {
82
+ url: { type: 'string', description: 'The app, https://…' },
83
+ login: { type: 'string', enum: ['none', 'browser', 'session'], description: 'none; browser (the human logs in by hand); session (a Playwright storageState the suite already writes).' },
84
+ session_file: { type: 'string', description: 'With login "session": path of that storageState file, inside the project. Only its path is written.' },
85
+ tests: { type: 'array', items: { type: 'string' }, description: 'Folders of the end-to-end suite, relative to the project.' },
86
+ project_dir: PROJECT_DIR,
87
+ },
88
+ required: ['url', 'login'],
89
+ },
90
+ },
91
+ {
92
+ name: 'pal_tour',
93
+ description: 'Start a tour in the background and return at once — it takes minutes. First tour: every page on the surface, a batch explored in depth (menus, tabs, dialogs; every non-GET request blocked), checks, baseline. Later tours: re-capture, compare with the baseline, check; with a test suite declared, repairs to its selectors are proposed and never applied. Only one tour runs per project. Follow it with pal_status. Report the whole result to the human, including "Not seen": a first tour is a baseline, never a pass.',
94
+ inputSchema: {
95
+ type: 'object',
96
+ properties: {
97
+ more: { type: 'boolean', description: 'Explore the next batch in depth instead of a regression tour. Refused after a BLOCK.' },
98
+ suite: { type: 'boolean', description: 'Also run the test command from config.json once, with the capture hook, to learn which pages the suite never visits. It runs the human’s own suite on this machine — ask before setting it.' },
99
+ batch: { type: 'integer', description: 'Pages per depth batch (default 10).' },
100
+ project_dir: PROJECT_DIR,
101
+ },
102
+ },
103
+ },
104
+ {
105
+ name: 'pal_status',
106
+ description: 'The tour running now — phase and progress — or the last one: its full report once done, as JSON and as the text a human reads. A tour whose process died before finishing is reported as interrupted, never as running. Read-only.',
107
+ inputSchema: { type: 'object', properties: { project_dir: PROJECT_DIR } },
108
+ },
109
+ ];
110
+ function projectDir(given, root = process.cwd()) {
111
+ const candidate = typeof given === 'string' && given.trim() ? given.trim() : '.';
112
+ const resolved = path.resolve(root, candidate);
113
+ const rootWithSep = root.endsWith(path.sep) ? root : root + path.sep;
114
+ if (resolved !== root && !resolved.startsWith(rootWithSep)) {
115
+ throw new Error(`project_dir "${candidate}" resolves outside "${root}". Refusing: an MCP call may be steered by content the agent read.`);
116
+ }
117
+ return resolved;
118
+ }
119
+ function readJson(file) {
120
+ try {
121
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
122
+ }
123
+ catch {
124
+ return null;
125
+ }
126
+ }
127
+ async function callTool(name, args, root = process.cwd()) {
128
+ const dir = projectDir(args.project_dir, root);
129
+ const config = readJson(path.join(dir, '.ia-qa', 'config.json'));
130
+ if (name === 'pal_project') {
131
+ const baseline = path.join(dir, '.ia-qa', 'baseline');
132
+ const run = (0, run_1.readRun)(dir);
133
+ return {
134
+ setUp: !!config,
135
+ ...(config
136
+ ? {
137
+ baseUrl: config.baseUrl,
138
+ pages: (config.pages ?? []).length,
139
+ level: (config.testPaths ?? []).length > 0 ? 2 : config.session || config.auth || fs.existsSync(path.join(dir, '.ia-qa', 'session.json')) ? 1 : 0,
140
+ testPaths: config.testPaths ?? [],
141
+ testCommand: config.testCommand ?? null,
142
+ baseline: fs.existsSync(baseline) && fs.readdirSync(baseline).some((f) => f.endsWith('.json')),
143
+ }
144
+ : { next: 'Ask the human where the app lives and how it logs in, then call pal_setup with their answers.' }),
145
+ tour: run ? { status: run.status, startedAt: run.startedAt, finishedAt: run.finishedAt ?? null } : null,
146
+ };
147
+ }
148
+ if (name === 'pal_setup') {
149
+ const login = args.login;
150
+ if (!['none', 'browser', 'session'].includes(login))
151
+ throw new Error('login must be none, browser or session.');
152
+ const session = typeof args.session_file === 'string' && args.session_file ? path.relative(dir, projectDir(args.session_file, dir)) : undefined;
153
+ const tests = Array.isArray(args.tests) ? args.tests.map(String) : undefined;
154
+ const lines = [];
155
+ const outcome = await (0, setup_1.runSetup)({ dir, url: String(args.url ?? ''), login, session, tests, yes: true, onProgress: (l) => lines.push(l) });
156
+ return { ...outcome, progress: lines };
157
+ }
158
+ if (name === 'pal_tour') {
159
+ const batch = Number.isInteger(args.batch) && args.batch > 0 ? args.batch : undefined;
160
+ const started = (0, run_1.startBackground)(dir, { more: !!args.more, suite: !!args.suite, batch }, CLI_ENTRY);
161
+ return started.started
162
+ ? { ...started, next: 'Call pal_status to follow it. A tour takes minutes.' }
163
+ : { ...started, ...(!config ? { next: 'Not set up: ask the human where the app lives and how it logs in, then call pal_setup.' } : {}) };
164
+ }
165
+ if (name === 'pal_status') {
166
+ const run = (0, run_1.readRun)(dir);
167
+ if (!run)
168
+ return { run: null, text: 'No tour has run on this project yet. Call pal_tour.' };
169
+ const text = run.status === 'done' && run.report ? (0, report_1.renderReport)(run.report) : undefined;
170
+ return { run, log: (0, run_1.logFile)(dir), ...(text ? { text } : {}) };
171
+ }
172
+ throw new Error(`Unknown tool "${name}". Call tools/list to see available tools.`);
173
+ }
174
+ function send(message) {
175
+ process.stdout.write(JSON.stringify(message) + '\n');
176
+ }
177
+ async function handle(req) {
178
+ const { id, method, params } = req;
179
+ const isNotification = id === undefined || id === null;
180
+ try {
181
+ if (method === 'initialize') {
182
+ send({ jsonrpc: '2.0', id, result: { protocolVersion: params?.protocolVersion || PROTOCOL_VERSION, capabilities: { tools: {} }, serverInfo: SERVER_INFO } });
183
+ return;
184
+ }
185
+ if (method === 'notifications/initialized' || method === 'initialized')
186
+ return;
187
+ if (method === 'ping') {
188
+ send({ jsonrpc: '2.0', id, result: {} });
189
+ return;
190
+ }
191
+ if (method === 'tools/list') {
192
+ send({ jsonrpc: '2.0', id, result: { tools: exports.TOOLS } });
193
+ return;
194
+ }
195
+ if (method === 'tools/call') {
196
+ try {
197
+ const result = await callTool(params?.name, params?.arguments || {});
198
+ send({ jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] } });
199
+ }
200
+ catch (err) {
201
+ const message = err instanceof Error ? err.message : String(err);
202
+ send({ jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: `Error: ${message}` }], isError: true } });
203
+ }
204
+ return;
205
+ }
206
+ if (!isNotification)
207
+ send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Method not found: ${method}` } });
208
+ }
209
+ catch (err) {
210
+ const message = err instanceof Error ? err.message : String(err);
211
+ if (!isNotification)
212
+ send({ jsonrpc: '2.0', id, error: { code: -32603, message } });
213
+ else
214
+ process.stderr.write(`[ia-qa-pal] error handling ${method}: ${message}\n`);
215
+ }
216
+ }
217
+ function main() {
218
+ if (process.argv.includes('--version') || process.argv.includes('-v')) {
219
+ process.stdout.write(SERVER_INFO.version + '\n');
220
+ process.exit(0);
221
+ }
222
+ // The setup verbs print; stdout is the protocol.
223
+ console.log = console.info = console.warn = (...a) => console.error(...a);
224
+ process.stderr.write('[ia-qa-pal] MCP server ready (stdio)\n');
225
+ let buffer = '';
226
+ process.stdin.setEncoding('utf8');
227
+ process.stdin.on('data', (chunk) => {
228
+ buffer += chunk;
229
+ let nl;
230
+ while ((nl = buffer.indexOf('\n')) !== -1) {
231
+ const line = buffer.slice(0, nl).trim();
232
+ buffer = buffer.slice(nl + 1);
233
+ if (!line)
234
+ continue;
235
+ let req;
236
+ try {
237
+ req = JSON.parse(line);
238
+ }
239
+ catch {
240
+ process.stderr.write(`[ia-qa-pal] non-JSON line ignored: ${line.slice(0, 80)}\n`);
241
+ continue;
242
+ }
243
+ void handle(req);
244
+ }
245
+ });
246
+ process.stdin.on('end', () => process.exit(0));
247
+ }
248
+ if (require.main === module)
249
+ main();
250
+ //# sourceMappingURL=server.js.map