@validate.qa/mcp 1.1.0 → 1.2.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/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/init.ts","../src/index.ts","../src/config.ts","../src/api-client.ts","../src/tools/index.ts"],"sourcesContent":["/**\n * `npx @validate.qa/mcp init` - one-command MCP setup for all supported IDEs.\n *\n * Creates/updates MCP config files so the user never has to manually edit JSON.\n * Detects which IDEs are present and writes the appropriate config for each.\n */\nimport { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { resolve, join } from 'path';\n\ninterface InitArgs {\n apiUrl: string;\n token: string;\n projectId: string;\n}\n\nfunction parseInitArgs(argv: string[]): InitArgs {\n const args: Record<string, string> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith('--') && i + 1 < argv.length) {\n args[arg.slice(2)] = argv[++i];\n }\n }\n\n const apiUrl = args['api-url'] ?? process.env.VALIDATEQA_API_URL ?? 'https://shared.validate.qa';\n const token = args['token'] ?? process.env.VALIDATEQA_TOKEN;\n const projectId = args['project-id'] ?? process.env.VALIDATEQA_PROJECT_ID;\n\n if (!token) {\n console.error('❌ Missing --token. Copy the full command from your Validate.QA dashboard.');\n process.exit(1);\n }\n if (!projectId) {\n console.error('❌ Missing --project-id. Copy the full command from your Validate.QA dashboard.');\n process.exit(1);\n }\n\n return { apiUrl: apiUrl.replace(/\\/$/, ''), token, projectId };\n}\n\n/** Build the MCP server entry for config files */\nfunction buildMcpEntry(config: InitArgs) {\n return {\n command: 'npx',\n args: [\n '-y', '@validate.qa/mcp@latest',\n '--api-url', config.apiUrl,\n '--token', config.token,\n '--project-id', config.projectId,\n ],\n };\n}\n\ninterface IDETarget {\n name: string;\n configDir: string; // relative to cwd\n configFile: string; // filename inside configDir\n /** Key in the JSON where MCP servers live */\n serversKey: string;\n}\n\nconst IDE_TARGETS: IDETarget[] = [\n { name: 'Claude Code', configDir: '.', configFile: '.mcp.json', serversKey: 'mcpServers' },\n { name: 'Cursor', configDir: '.cursor', configFile: 'mcp.json', serversKey: 'mcpServers' },\n { name: 'Windsurf', configDir: '.windsurf', configFile: 'mcp.json', serversKey: 'mcpServers' },\n { name: 'VS Code', configDir: '.vscode', configFile: 'mcp.json', serversKey: 'servers' },\n];\n\nfunction writeConfigForIDE(cwd: string, target: IDETarget, entry: object): 'created' | 'updated' | 'skipped' {\n const dir = resolve(cwd, target.configDir);\n const filePath = resolve(dir, target.configFile);\n\n // For .cursor and .windsurf, only write if the directory already exists (IDE is in use)\n // For Claude Code (.mcp.json in root), always write\n if (target.configDir !== '.' && !existsSync(dir)) {\n return 'skipped';\n }\n\n let existing: Record<string, unknown> = {};\n if (existsSync(filePath)) {\n try {\n existing = JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n // Corrupted file - overwrite\n }\n }\n\n const servers = (existing[target.serversKey] ?? {}) as Record<string, unknown>;\n const hadEntry = 'validate.qa' in servers;\n servers['validate.qa'] = entry;\n existing[target.serversKey] = servers;\n\n // Ensure directory exists (for .cursor/mcp.json on first setup)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\\n', 'utf-8');\n return hadEntry ? 'updated' : 'created';\n}\n\nexport function runInit() {\n const config = parseInitArgs(process.argv.slice(3)); // skip: node, script, 'init'\n const entry = buildMcpEntry(config);\n const cwd = process.cwd();\n\n console.log('');\n console.log(' Validate.QA - MCP Setup');\n console.log(' ─────────────────────────');\n console.log('');\n\n let anyWritten = false;\n\n for (const target of IDE_TARGETS) {\n const result = writeConfigForIDE(cwd, target, entry);\n if (result === 'skipped') {\n continue;\n }\n const file = join(target.configDir, target.configFile);\n if (result === 'created') {\n console.log(` ✓ ${target.name} - created ${file}`);\n } else {\n console.log(` ✓ ${target.name} - updated ${file}`);\n }\n anyWritten = true;\n }\n\n if (!anyWritten) {\n // No IDE dirs found - create Claude Code config as default\n const target = IDE_TARGETS[0]; // Claude Code\n writeConfigForIDE(cwd, target, entry);\n console.log(` ✓ Created ${target.configFile} (works with Claude Code)`);\n console.log('');\n console.log(' Tip: Create a .cursor/ or .windsurf/ directory and re-run to set up those IDEs too.');\n }\n\n console.log('');\n console.log(' Next: restart your IDE or reload MCP servers.');\n console.log(' Then ask your AI: \"list my tests\" to verify it works.');\n console.log('');\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { resolveConfig } from './config.js';\nimport { ValidateQAClient } from './api-client.js';\nimport { registerAllTools } from './tools/index.js';\n\n// `npx @validate.qa/mcp init ...` - auto-create IDE config files\nif (process.argv[2] === 'init') {\n const { runInit } = await import('./init.js');\n runInit();\n process.exit(0);\n}\n\nconst config = resolveConfig();\nconst client = new ValidateQAClient(config.apiUrl, config.token, config.projectId);\n\nconst server = new McpServer(\n { name: 'validate.qa', version: '1.0.0' },\n { instructions: `Validate.QA - AI-powered QA platform: generates, runs, and heals Playwright E2E tests.\n\n## Capabilities\n- Setup projects via setup_project (read source code first)\n- Write tests via create_test (read routes, components, API handlers first)\n- Manage tests: list, view, edit, run, delete (DRAFT/ACTIVE/DISABLED/QUARANTINED)\n- Heal failing tests: AI replays in browser, fixes code or flags app bugs\n- Schedule recurring runs (hourly-weekly) with auto-heal\n\n## Workflows\n1. New project: READ source, setup_project (features, baseUrl, testCredentials, appBrief). Per feature: READ code, create_test, add_api_endpoints.\n2. Check health: list_features (failingTests > 0), get_test_failures per feature, edit_test_code or heal_test.\n3. Add coverage: READ code, create_feature, create_test x N (suite = feature name), add_api_endpoints.\n4. Nightly automation: create_schedule (daily, autoHeal: true).\n5. After code change: batch_run_tests (waitSeconds: 120, or then wait_for_batch), then list_features + get_test_failures.\n\n## Automatic lifecycle\nDo NOT call run_test after create_test (auto-queued) or heal_test after failures (auto-heals, unless toggle_auto_heal turned it off).\nDo NOT poll get_test_run in a loop. To wait for a run, call wait_for_run(runId) (or wait_for_batch(batchId)); it blocks until the run finishes or timeoutSeconds passes (max 120) and returns timedOut: true if it is still going - then call it again. run_test and batch_run_tests also take waitSeconds to queue and wait in one call. A FAILED run comes back with its error, consoleLog and failedSteps.\nLifecycle: create > auto-run > pass=ACTIVE, fail=auto-heal > heal fail=@needs-review. With toggle_auto_heal(false), no failure is auto-healed; call heal_test yourself when you want one.\nDO call add_api_endpoints per feature (not automatic).\nDO call run_security_audit after UI tests to analyze captured API traffic. If it errors about missing data, tell the developer to click \"Security Audit\" in the Dashboard to run a live browser capture first. Use list_security_findings to review results.\n\n## Rate limits\n50 calls/60s. Reads parallel OK, writes sequential. On 429, wait 30-60s.\n\n## Triaging failures\n- App broken: flag_as_bug(testId, description). Selector/timing: heal_test. Bad test logic: edit_test_code.\n- Unsure: heal_test first - it detects app bugs automatically.\n\n## Using the codebase\nYou have full source code access. Always READ before writing: routes for URLs, components for selectors, API handlers for endpoints.\n\n## Before writing any test - preflight check\n\nRead project://test-conventions first. Check the variables section:\n- BASE_URL must be set (the runner needs it). If missing, call setup_project or set_variable.\n- TEST_USER_EMAIL and TEST_USER_PASSWORD must be set if tests require login. If missing, ask the developer for credentials and call set_variable.\n- If starting fresh (no features, no variables), use workflow #1 (setup_project) before anything else.\n\n## Critical rules - see project://test-conventions for full details and code templates\n- URLs: relative only, never hardcoded domains - runner sets baseURL\n- Credentials: process.env.TEST_USER_EMAIL! (non-null assertion) - never ?? or || fallbacks\n- Registration: testuser-\\${Date.now()}@example.com\n- Suite name must match feature name exactly (case-sensitive)\n- @auth must NOT combine with @smoke/@regression/@needs-auth/@critical\n- Each test = own browser process; @needs-auth tests MUST login inline\n- API tests: test.describe.serial() with __QA_HTTP__ log after every request\n- UI tests: console.log() milestones after key actions\n- Selectors: getByRole(role, { name, exact: true }), getByLabel(label, { exact: true }), getByText(text, { exact: true }); CSS ids only for nodes with no accessible name\n- URL assertions: path only - await expect(page).toHaveURL(/\\\\/dashboard$/) - never a host\n- Production BASE_URL: read-only tests get the read-only guard (page.route aborts auth/mutation requests and fails the test) - template in project://test-conventions\n- Every assertion must be able to fail: no toHaveCount(0) on transient text, no toBeHidden on an element never shown, full message text instead of a toContainText fragment\n- create_test/edit_test_code validate code - fix warnings before moving on\n\n## Setting up coverage\n1. SCAN: Read router, nav, pages, API handlers - map ALL routes and features.\n2. SETUP: setup_project with features, pages, baseUrl, testCredentials, appBrief.\n3. WRITE: One feature at a time. For EACH test, read the SPECIFIC source code first (see codeReadingRequirements in test-conventions). For login blocks: read the login component. For UI tests: read the page component. For API tests: read the route handler. Templates are structure guides - selectors MUST come from the real code. Use create_test for BOTH UI and API tests. Then call add_api_endpoints.\n4. VERIFY: get_spec_coverage(featureName, specItems) - write tests for every uncovered item - then get_coverage_summary + list_features for failures + get_test_failures.\n\nTest real user flows (forms, validation, errors). \"Page loads\" tests are worthless.` },\n);\n\n// Register all QA platform tools\nregisterAllTools(server, client);\n\n// ── MCP Resources (live project context for AI assistants) ────────────\n\nserver.resource(\n 'Project Overview',\n 'project://overview',\n { description: 'Live project state: test counts, pass rates, runner status, features, coverage gaps, and configuration. Read this first to understand the current project.' },\n async () => {\n try {\n const [overview, coverage, stats] = await Promise.all([\n client.executeTool('get_project_overview'),\n client.executeTool('get_coverage_summary').catch(() => null),\n client.executeTool('get_regression_stats').catch(() => null),\n ]);\n const snapshot = {\n overview,\n coverage,\n regressionStats: stats,\n retrievedAt: new Date().toISOString(),\n };\n return { contents: [{ uri: 'project://overview', mimeType: 'application/json', text: JSON.stringify(snapshot, null, 2) }] };\n } catch (err) {\n return { contents: [{ uri: 'project://overview', mimeType: 'text/plain', text: `Failed to load project overview: ${err instanceof Error ? err.message : String(err)}` }] };\n }\n },\n);\n\nserver.resource(\n 'Test Conventions',\n 'project://test-conventions',\n { description: 'Project-specific test conventions: environment variables, credential patterns, tag meanings, and registration email rules. Read this before writing or editing any test.' },\n async () => {\n // The server builds the conventions (one copy for every transport, updated without an npm\n // release). The copy below is only a fallback for servers that predate that route.\n const served = await client.getTestConventions();\n if (served && typeof served === 'object') {\n return { contents: [{ uri: 'project://test-conventions', mimeType: 'application/json', text: JSON.stringify(served, null, 2) }] };\n }\n try {\n const vars = await client.executeTool('get_variables') as { variables: Record<string, { value: string; secret: boolean }> };\n const varNames = Object.keys(vars.variables || {});\n const hasAuth = varNames.includes('TEST_USER_EMAIL') && varNames.includes('TEST_USER_PASSWORD');\n\n const conventions = {\n codeReadingRequirements: {\n CRITICAL: 'Templates below are STRUCTURE GUIDES - every selector, route, and endpoint MUST come from reading the actual source code. Never copy a template verbatim.',\n forLogin: 'BEFORE writing any @needs-auth login block: READ the login/auth page component. Find: (1) the login route (/login, /auth/login, /signin?), (2) the flow type (single-step or multi-step with Continue/Next button?), (3) each field\\'s label text for getByLabel(label, { exact: true }) - fall back to the placeholder or input[type=password] only when the field has no label, (4) the submit button\\'s accessible name for getByRole(\\'button\\', { name, exact: true }), (5) post-login redirect URL. Every app is different - single-step, multi-step, OAuth buttons, magic links.',\n forUITests: 'BEFORE writing a UI test for a page: READ the page component. Find: headings (text + level), form fields (labels, placeholders, types), buttons (text, role), navigation elements, validation error messages, success/error states. Use the exact text from the component - do not guess.',\n forAPITests: 'BEFORE writing an API test: READ the route handler. Find: the endpoint path, HTTP method, request body schema (what fields, what types), response body shape (is it flat or wrapped in { data: {...} }?), auth requirements (Bearer token? Cookie? API key?), error response shapes.',\n forAPIMapping: 'BEFORE calling add_api_endpoints: READ ALL route handlers for the feature. Register every endpoint with accurate method, path, requestShape, responseShapes, requiresAuth.',\n },\n credentials: {\n rule: 'Never hardcode credentials in test code',\n loginFields: hasAuth\n ? 'Use process.env.TEST_USER_EMAIL! and process.env.TEST_USER_PASSWORD!'\n : 'No credentials configured - set TEST_USER_EMAIL and TEST_USER_PASSWORD via set_variable',\n registrationFields: 'MUST use unique email per run: `testuser-${Date.now()}@example.com` - reusing emails causes \"already registered\" failures',\n registrationPassword: \"Use 'TestPass123!' for new account passwords (meets typical strength rules)\",\n },\n variables: {\n configured: varNames,\n baseUrl: (vars.variables as any)?.BASE_URL?.value || 'Not set',\n },\n tags: {\n '@auth': 'Tests the login/auth flow itself. MUST NOT combine with @smoke, @regression, @needs-auth, @critical. CAN include @discovered.',\n '@needs-auth': 'Tests for authenticated pages - MUST include inline login block. CAN combine with @smoke, @regression, @critical.',\n '@smoke': 'Quick sanity checks - run on every deploy',\n '@regression': 'Full regression suite tests',\n '@critical': 'Business-critical paths (informational label - no special suite filtering)',\n '@api': 'API-level tests. Runs in its own project, excluded from suite/smoke/regression runs',\n '@discovered': 'Auto-added to tests generated by AI discovery',\n '@generated': 'Auto-added by AI discovery and API test generation',\n '@manual': 'Default tag for tests created via create_test when no tags specified',\n '@flaky': 'Auto-added after repeated failures. Excluded from suite/smoke/regression runs',\n '@bug-found': 'Added when healer detects an app bug. Test is auto-quarantined',\n '@needs-review': 'Added when DRAFT test fails healing. Requires human attention',\n '@heal-skip': 'Added by healer when test is permanently unfixable. Prevents future auto-heal',\n '@rate-limit-blocked': 'Auto-added by pre-heal classifier when test failure was a rate-limit (HTTP 429). Test skips heal until next pass clears the tag. NOT for use by test authors.',\n '@quota-exceeded': 'Auto-added when failure is a quota/daily-limit response. Skips heal; clears on next pass.',\n '@auth-lost': 'Auto-added when a signed-in test (@needs-auth, authed @api, or a stored-login body) fails on a 401/403 or falls back to the login page. Never on @auth / login-form tests. Skips heal; clears after re-auth + pass.',\n '@rate-limit-expected': 'OPT-OUT tag for tests that intentionally exercise rate-limit behavior (e.g., \"POST /api/login 6 times to trigger lockout\"). Add this tag to ANY @api test that asserts rate-limit responses, otherwise the rate-limit detector will silently downgrade your failure to ERROR. Distinct from auto-pushed @rate-limit-blocked.',\n },\n executionModel: {\n isolation: 'Each test runs as its own separate Playwright process in a fresh browser. No shared state, no storageState, no global setup.',\n authRequired: 'Every @needs-auth test MUST include its own login block. Do NOT suggest storageState or performLogin() - those only work in the regression suite.',\n concurrency: 'Tests run in parallel - runner claims up to 10 per poll, launched 3s apart (~30s per batch). Multiple batches can overlap. Use test.setTimeout(120_000) if the target app is slow under concurrent login load.',\n },\n lifecycle: {\n DRAFT: 'New or unverified test - not included in scheduled runs',\n ACTIVE: 'Verified and running - included in batch/suite executions',\n DISABLED: 'Manually paused - excluded from all runs',\n QUARANTINED: 'Auto-quarantined after repeated failures',\n },\n logging: {\n CRITICAL: 'EVERY request.get/post/put/patch/delete call MUST be followed by a __QA_HTTP__ log line. This is how the platform surfaces request/response data in the Network panel. Missing this = invisible API calls.',\n apiPattern: \"const body = await res.json();\\nconsole.log('__QA_HTTP__:' + JSON.stringify({ method: 'POST', url: '/api/path', status: res.status(), requestBody: { field: value, password: '[REDACTED]' }, responseBody: body }));\",\n rules: [\n 'Call res.json() ONCE, store in body variable - reuse for log and assertions (calling twice throws)',\n 'Log AFTER the request, BEFORE assertions',\n 'Redact sensitive fields: password: \"[REDACTED]\", token: \"[REDACTED]\"',\n 'Use the literal URL string (same as the request call), not a variable',\n 'UI tests using page fixture do NOT need __QA_HTTP__ - only request fixture tests',\n ],\n uiMilestones: \"UI tests (page fixture): add plain console.log() at key milestones - e.g. console.log('Navigated to login page') after page.goto(). These appear in the Console Output panel.\",\n uiLoggingRules: [\n 'Log AFTER the action, not before - confirms the action succeeded',\n 'Include dynamic values where useful: console.log(\"Current URL:\", page.url())',\n 'Keep messages short and human-readable - they show verbatim in the UI',\n 'Do NOT log sensitive values (passwords, tokens)',\n ],\n apiTestStructure: 'Use test.describe.serial() for API tests sharing auth. Independent validation tests (401, 404 checks) go in a SEPARATE non-serial describe block - do not mix them with the auth-dependent serial flow.',\n },\n bestPractices: [\n 'Every @needs-auth test must start with a login block (goto login, fill creds, click submit, waitForURL)',\n 'Use { timeout: 30000 } on waitForURL after login - target apps can be slow under concurrent test load',\n 'Keep login blocks minimal - no assertions on the login page (that is what @auth tests are for)',\n 'After login, page.goto() directly to the page being tested - do not navigate via UI clicks',\n 'Selectors: getByRole(role, { name, exact: true }) for buttons/links/headings, getByLabel(label, { exact: true }) for form fields, getByText(text, { exact: true }) for static copy. CSS ids/classes only for nodes with no accessible name',\n 'URL assertions: path only - await expect(page).toHaveURL(/\\\\/dashboard$/) - never a host',\n 'Registration tests must generate unique email with Date.now()',\n 'Group tests into suites matching feature names exactly (case-sensitive)',\n 'Do NOT suggest storageState or performLogin() for create_test - those only work in the regression suite',\n 'EVERY API test (request fixture) MUST include __QA_HTTP__ log after each request call - see logging section above',\n 'Avoid waitForLoadState(\"networkidle\") - apps with SSE, WebSocket, or analytics connections may never reach idle, causing 60s timeouts. Wait for specific elements instead.',\n 'For headings, use exact: true or level: 1 - getByRole(\"heading\", { name: \"X\" }) matches ALL h1-h6 containing \"X\", causing strict mode violations',\n ],\n pitfalls: {\n CRITICAL: 'These are the top causes of test failures. Read before writing ANY test.',\n strictModeViolations: \"Playwright throws when a locator resolves to 2+ elements. Common causes: getByLabel('Password') matches input AND 'Show password' toggle; getByRole('button', { name: 'Continue' }) matches 'Continue' AND 'Continue with GitHub'. Prevention: getByLabel('Password', { exact: true }) (exact keeps it from matching 'Show password'), { exact: true } for ambiguous button text, scope to a parent container (page.locator('form#login').getByLabel('Email', { exact: true })). Use input[type=\\\"password\\\"] only when the field has no label.\",\n networkIdle: \"NEVER use waitForLoadState('networkidle') - waits for 500ms of zero network activity, which is fragile and causes 30s+ timeouts in apps with polling, analytics, or WebSockets. Use waitForURL() or wait for a specific element instead.\",\n headingStrictMode: \"getByRole('heading', { name: 'Dashboard' }) can match multiple headings (h1 + h2 + breadcrumb). Add { level: 1 } or { exact: true } to disambiguate. Same fix for getByLabel('Password') matching the input AND a 'Show password' toggle: getByLabel('Password', { exact: true }).\",\n postLoginRedirect: \"NEVER assert exact URL after login (e.g. expect(page).toHaveURL('/dashboard')) - apps redirect to different routes per user/role. Use: await page.waitForURL(url => !url.pathname.startsWith('/login'), { timeout: 30000 })\",\n resJsonOnce: \"res.json() consumes the response body stream - calling it twice throws. Store result: const body = await res.json(); then reuse body for logging and assertions.\",\n hiddenInputs: \"Many UI frameworks use visually hidden <input> with custom overlays. page.check() fails because the overlay intercepts clicks. Click the <label> or visible wrapper: page.getByText('Remember me').click()\",\n apiResponseShapes: \"API endpoints often wrap data in a property ({ user: {...} }, { data: [...] }). Read the route handler's res.json() call to see the actual shape. If the handler does res.json({ ticket }), then body is { ticket: { id, ... } } - NOT { id, ... }.\",\n invalidIds: \"Not all routes validate ID format before querying the database. If a route passes the raw ID to an ORM query and gets null back, it returns 404 'not found' - not 400 'bad request'. If unsure, expect either: expect([400, 404]).toContain(res.status())\",\n hardcodedUrls: \"NEVER hardcode full URLs in test code. For UI tests: always use relative paths - page.goto('/login'). The runner sets baseURL from the project's BASE_URL variable. For API tests: use relative paths if the API is on the same domain as the frontend (request.post('/api/auth/login')). If the API is on a SEPARATE domain, use process.env.BASE_API_URL! as a test variable (set via set_variable) and construct URLs: request.post(process.env.BASE_API_URL! + '/auth/login'). NEVER hardcode 'https://example.com/...' in test code - use env vars or relative paths.\",\n credentialFallbacks: \"NEVER use process.env.TEST_USER_EMAIL ?? 'fallback' or || 'fallback'. Use ! (non-null assertion). Missing variable = test should fail immediately, not silently use hardcoded value.\",\n },\n templates: {\n needsAuth: \"test('Feature Name - Test Name', {\\n tag: ['@needs-auth', '@smoke'],\\n}, async ({ page }) => {\\n // Login block - ADAPTED from reading the app's login page component\\n // YOU MUST READ the login page component BEFORE writing this block\\n await page.goto('/login'); // read the router for actual login route\\n await page.getByLabel('Email', { exact: true }).fill(process.env.TEST_USER_EMAIL!); // label text from the component\\n await page.getByLabel('Password', { exact: true }).waitFor({ state: 'visible', timeout: 10000 });\\n await page.getByLabel('Password', { exact: true }).fill(process.env.TEST_USER_PASSWORD!); // input[type=\\\"password\\\"] only if the field has no label\\n await page.getByRole('button', { name: 'Sign in', exact: true }).click(); // the button's accessible name\\n await page.waitForURL(url => !url.pathname.startsWith('/login'), { timeout: 30000 });\\n console.log('Logged in, current URL:', page.url());\\n\\n // Actual test\\n await page.goto('/feature-path');\\n console.log('On feature page');\\n await expect(page.getByRole('heading', { name: /feature name/i })).toBeVisible();\\n});\",\n authTest: \"test('Login - Happy Path', {\\n tag: ['@auth'],\\n}, async ({ page }) => {\\n await page.goto('/auth/login');\\n // test the login form itself - selectors from reading the actual component\\n});\",\n apiSerial: \"test.describe.serial('Feature Name', () => {\\n let token: string;\\n test('authenticate', async ({ request }) => {\\n const res = await request.post('/api/auth/login', {\\n data: { email: process.env.TEST_USER_EMAIL!, password: process.env.TEST_USER_PASSWORD! }\\n });\\n const body = await res.json();\\n console.log('__QA_HTTP__:' + JSON.stringify({\\n method: 'POST', url: '/api/auth/login', status: res.status(),\\n requestBody: { email: process.env.TEST_USER_EMAIL!, password: '[REDACTED]' },\\n responseBody: body,\\n }));\\n expect(res.status()).toBe(200);\\n token = body.access_token; // ← adapt to your API (e.g. body.token, body.jwt)\\n });\\n test('get profile', async ({ request }) => {\\n const res = await request.get('/api/users/profile', {\\n headers: { Authorization: `Bearer ${token}` }\\n });\\n const body = await res.json();\\n console.log('__QA_HTTP__:' + JSON.stringify({\\n method: 'GET', url: '/api/users/profile', status: res.status(),\\n responseBody: body,\\n }));\\n expect(res.status()).toBe(200);\\n });\\n});\",\n apiEndpointMapping: '// After reading an Express route handler like:\\n// router.post(\\'/api/auth/login\\', async (req, res) => { ... })\\n// Call add_api_endpoints with:\\n{\\n featureName: \"Authentication\",\\n endpoints: [{\\n method: \"POST\",\\n path: \"/api/auth/login\",\\n requiresAuth: false,\\n requestShape: { \"email\": \"string\", \"password\": \"string\" },\\n responseShapes: { \"200\": { \"token\": \"string\", \"user\": \"object\" }, \"401\": { \"error\": \"string\" } }\\n }]\\n}',\n uiWithMilestones: \"test('Feature - Happy Path', async ({ page }) => {\\n await page.goto('/login');\\n console.log('Navigated to login page');\\n\\n await page.getByLabel('Email', { exact: true }).fill(process.env.TEST_USER_EMAIL!);\\n await page.getByLabel('Password', { exact: true }).fill(process.env.TEST_USER_PASSWORD!);\\n await page.getByRole('button', { name: 'Sign in', exact: true }).click();\\n console.log('Credentials submitted');\\n\\n await page.waitForURL(url => !url.pathname.startsWith('/login'), { timeout: 15000 });\\n console.log('Logged in, current URL:', page.url());\\n\\n await page.goto('/feature-path');\\n console.log('On feature page');\\n});\",\n },\n testQuality: [\n 'Test actual user flows, not just \"page loads\" - a test that only checks goto + heading visible is nearly worthless',\n 'Test form submission: fill fields, submit, verify success/redirect',\n 'Test validation: leave required fields empty, enter invalid data, verify error messages',\n 'Test error states: wrong password, expired session, invalid IDs',\n 'Test the happy path end-to-end: login → navigate → perform action → verify result',\n 'Each test should assert something meaningful about the application behavior',\n ],\n securityAudit: {\n workflow: 'After creating UI tests for a feature, call run_security_audit to analyze captured API traffic for auth issues, data exposure, and vulnerabilities.',\n prerequisite: 'Requires prior browser-based test execution (the auto-verification run from create_test counts). If run_security_audit errors about missing data, tell the developer to click \"Security Audit\" in the Tests dropdown on the Dashboard - this launches a live browser to capture real network traffic (HAR) and runs analysis automatically.',\n reviewing: 'Use list_security_findings to review results. Filter by severity (critical/high/medium/low/info) or status (open/acknowledged/resolved/false-positive). Critical and high findings should be flagged to the developer immediately.',\n },\n keyConcepts: {\n features: 'Group related pages and tests (e.g., \"Authentication\" covers /auth/login, /auth/register). MUST include page routes.',\n pages: 'Route URLs on a feature - used for coverage tracking and site map (e.g., [\"/login\", \"/register\"])',\n suites: 'Group tests for batch execution - suite name MUST match feature name exactly (case-sensitive)',\n runner: 'Executes tests - can be local (developer machine) or cloud',\n testData: 'Key-value variables injected into tests (e.g., credentials, URLs)',\n },\n retrievedAt: new Date().toISOString(),\n };\n return { contents: [{ uri: 'project://test-conventions', mimeType: 'application/json', text: JSON.stringify(conventions, null, 2) }] };\n } catch (err) {\n return { contents: [{ uri: 'project://test-conventions', mimeType: 'text/plain', text: `Failed to load conventions: ${err instanceof Error ? err.message : String(err)}` }] };\n }\n },\n);\n\n// Connect via stdio transport\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { readFileSync } from 'fs';\nimport { resolve } from 'path';\n\nexport interface ValidateQAConfig {\n apiUrl: string;\n token: string;\n projectId: string;\n}\n\nfunction parseArgs(argv: string[]): Record<string, string> {\n const args: Record<string, string> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith('--') && i + 1 < argv.length) {\n const key = arg.slice(2);\n args[key] = argv[++i];\n }\n }\n return args;\n}\n\nfunction loadDotFile(): Partial<ValidateQAConfig> {\n try {\n const content = readFileSync(resolve(process.cwd(), '.validateqa.json'), 'utf-8');\n const json = JSON.parse(content);\n return {\n apiUrl: typeof json.apiUrl === 'string' ? json.apiUrl : undefined,\n projectId: typeof json.projectId === 'string' ? json.projectId : undefined,\n };\n } catch {\n return {};\n }\n}\n\nexport function resolveConfig(): ValidateQAConfig {\n const cliArgs = parseArgs(process.argv.slice(2));\n const dotFile = loadDotFile();\n\n const apiUrl =\n cliArgs['api-url'] ??\n process.env.VALIDATEQA_API_URL ??\n dotFile.apiUrl;\n\n const token =\n cliArgs['token'] ??\n process.env.VALIDATEQA_TOKEN;\n\n const projectId =\n cliArgs['project-id'] ??\n process.env.VALIDATEQA_PROJECT_ID ??\n dotFile.projectId;\n\n if (!apiUrl) {\n console.error('Missing required config: api-url (--api-url, VALIDATEQA_API_URL, or .validateqa.json)');\n process.exit(1);\n }\n if (!token) {\n console.error('Missing required config: token (--token or VALIDATEQA_TOKEN)');\n process.exit(1);\n }\n if (!projectId) {\n console.error('Missing required config: project-id (--project-id, VALIDATEQA_PROJECT_ID, or .validateqa.json)');\n process.exit(1);\n }\n\n return { apiUrl: apiUrl.replace(/\\/$/, ''), token, projectId };\n}\n","/**\n * Same string as `PLAN_FEATURE_LOCKED_CODE` in `@validateqa/shared`.\n * This package does not depend on shared.\n */\nexport const PLAN_FEATURE_LOCKED_CODE = 'PLAN_FEATURE_LOCKED';\n\nexport class ApiResponseError extends Error {\n readonly status: number;\n readonly body: Record<string, unknown>;\n\n constructor(status: number, body: Record<string, unknown>) {\n const message = typeof body.error === 'string' && body.error.length > 0\n ? body.error\n : `API error ${status}`;\n super(message);\n this.name = 'ApiResponseError';\n this.status = status;\n this.body = body;\n }\n}\n\n/** Tool text for a thrown client error. A plan lock keeps the structured body. */\nexport function toolErrorText(err: unknown): string {\n if (err instanceof ApiResponseError && err.body.code === PLAN_FEATURE_LOCKED_CODE) {\n return JSON.stringify(err.body);\n }\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n}\n\nexport class ValidateQAClient {\n constructor(\n private baseUrl: string,\n private token: string,\n private projectId: string,\n ) {}\n\n async executeTool(tool: string, args: Record<string, unknown> = {}): Promise<unknown> {\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/api/mcp/execute`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'X-Project-Id': this.projectId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ tool, args }),\n signal: AbortSignal.timeout(120_000),\n });\n } catch (err) {\n // Network errors, DNS failures, timeouts\n const cause = err instanceof Error && err.cause instanceof Error ? err.cause.message : '';\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Connection failed: ${msg}${cause ? ` (${cause})` : ''}`);\n }\n\n if (!res.ok) {\n const parsed = await res.json().catch(() => ({ error: res.statusText }));\n const body = parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : { error: res.statusText };\n throw new ApiResponseError(res.status, body);\n }\n\n return res.json();\n }\n\n /** project://test-conventions from the server (GET /api/mcp/test-conventions); null when the server predates it. */\n async getTestConventions(): Promise<unknown | null> {\n try {\n const res = await fetch(`${this.baseUrl}/api/mcp/test-conventions`, {\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'X-Project-Id': this.projectId,\n },\n signal: AbortSignal.timeout(30_000),\n });\n if (!res.ok) return null;\n return await res.json();\n } catch {\n return null;\n }\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: AbortSignal.timeout(5000),\n });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { toolErrorText, type ValidateQAClient } from '../api-client.js';\n\ntype ToolAnnotations = {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n};\n\nfunction tool(\n server: McpServer,\n client: ValidateQAClient,\n name: string,\n description: string,\n schema: Record<string, z.ZodTypeAny>,\n annotations?: ToolAnnotations,\n) {\n const handler = async (args: Record<string, unknown>) => {\n try {\n const result = await client.executeTool(name, args);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: toolErrorText(err) }],\n isError: true,\n };\n }\n };\n\n if (annotations) {\n server.tool(name, description, schema, annotations, handler);\n } else {\n server.tool(name, description, schema, handler);\n }\n}\n\n// The server answers one wait call within SERVER_WAIT_MAX_SECONDS (its proxy\n// cuts requests at 60 s). A longer wait here, up to WAIT_MAX_SECONDS, is a\n// chain of calls: each continues with wait_for_run / wait_for_batch until the\n// run is done or the time is used up.\nconst SERVER_WAIT_MAX_SECONDS = 55;\nconst WAIT_DEFAULT_SECONDS = 50;\nconst WAIT_MAX_SECONDS = 120;\n\ntype WaitKind = { tool: 'wait_for_run'; idKey: 'runId' } | { tool: 'wait_for_batch'; idKey: 'batchId' };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction waitSecondsFrom(raw: unknown, fallback: number): number {\n const n = typeof raw === 'number' ? raw : Number.NaN;\n if (!Number.isFinite(n)) return fallback;\n return Math.min(Math.max(n, 0), WAIT_MAX_SECONDS);\n}\n\n/**\n * Call `name` with up to SERVER_WAIT_MAX_SECONDS of `waitKey`, then keep\n * waiting with `kind.tool` while the result says timedOut and time is left.\n */\nasync function executeWithWait(\n client: ValidateQAClient,\n name: string,\n args: Record<string, unknown>,\n waitKey: 'waitSeconds' | 'timeoutSeconds',\n totalSeconds: number,\n kind: WaitKind,\n): Promise<unknown> {\n const deadline = Date.now() + totalSeconds * 1000;\n const first = Math.min(totalSeconds, SERVER_WAIT_MAX_SECONDS);\n let result = await client.executeTool(name, { ...args, [waitKey]: first });\n for (;;) {\n if (!isRecord(result) || result.timedOut !== true) return result;\n const id = result[kind.idKey];\n const left = Math.floor((deadline - Date.now()) / 1000);\n if (typeof id !== 'string' || left < 1) return result;\n result = await client.executeTool(kind.tool, { [kind.idKey]: id, timeoutSeconds: Math.min(left, SERVER_WAIT_MAX_SECONDS) });\n }\n}\n\nfunction waitTool(\n server: McpServer,\n client: ValidateQAClient,\n name: string,\n description: string,\n schema: Record<string, z.ZodTypeAny>,\n waitKey: 'waitSeconds' | 'timeoutSeconds',\n defaultSeconds: number,\n kind: WaitKind,\n annotations?: ToolAnnotations,\n) {\n const handler = async (args: Record<string, unknown>) => {\n try {\n const seconds = waitSecondsFrom(args[waitKey], defaultSeconds);\n const result = seconds > 0\n ? await executeWithWait(client, name, args, waitKey, seconds, kind)\n : await client.executeTool(name, args);\n return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: toolErrorText(err) }],\n isError: true,\n };\n }\n };\n if (annotations) {\n server.tool(name, description, schema, annotations, handler);\n } else {\n server.tool(name, description, schema, handler);\n }\n}\n\nexport function registerAllTools(server: McpServer, client: ValidateQAClient) {\n // ── Test Management ──────────────────────────────────────────────────\n\n tool(server, client, 'list_tests',\n 'List tests in the current project. Returns test name, status, suite, tags, confidence, and the last run result. Paginated - use limit/offset to page through all tests.',\n {\n status: z.enum(['DRAFT', 'ACTIVE', 'DISABLED', 'QUARANTINED']).optional().describe('Filter by test lifecycle status'),\n suite: z.string().optional().describe('Filter by suite name'),\n search: z.string().optional().describe('Search test names (case-insensitive substring match)'),\n tag: z.string().optional().describe('Filter by tag (e.g. @regression, @smoke, @auth)'),\n limit: z.number().optional().describe('Max results to return (default: 50, max: 200)'),\n offset: z.number().optional().describe('Number of results to skip for pagination (default: 0)'),\n },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_test',\n 'Get details of a specific test: name, status, tags, steps, session, and the 5 most recent run results. Code is truncated to 200 lines - use get_test_code for the full Playwright code.',\n { testId: z.string().describe('The test case ID') },\n { readOnlyHint: true },\n );\n\n waitTool(server, client, 'run_test',\n 'Queue a test for execution. Creates a PENDING test run that will be picked up by an online runner. Returns the run ID; with waitSeconds, the finished run (as wait_for_run). For a mobile (Appium) test, pass platform to run it on the OTHER platform than it was authored on - if some steps lack a native locator for that platform, a one-time locator-discovery pass runs first (supply appId once for the target-platform build), then the test runs automatically.',\n {\n testId: z.string().describe('The test case ID to run'),\n platform: z.enum(['IOS', 'ANDROID']).optional().describe('Mobile only: run this test on the given platform (cross-platform run). Omit to run on the authoring platform.'),\n appId: z.string().optional().describe('Mobile cross-platform only: the target-platform app id (bundleId/appPackage). Only needed the first time this project runs on the other platform.'),\n waitSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`Optional. Wait up to this many seconds (max ${WAIT_MAX_SECONDS}) for the run to finish and return its final result, like wait_for_run (timedOut: true if still going). 0 or omitted: return at once with the run ID.`),\n },\n 'waitSeconds', 0, { tool: 'wait_for_run', idKey: 'runId' },\n );\n\n tool(server, client, 'delete_test',\n 'Permanently delete a test case and all its run history. Cannot be undone.',\n { testId: z.string().describe('The test case ID to delete') },\n { destructiveHint: true },\n );\n\n tool(server, client, 'heal_test',\n 'Queue an AI healing run for a failing test. The AI agent replays the test in a real browser, analyzes the failure, and either fixes the Playwright code OR detects an application bug (quarantines the test with @bug-found if the app itself is broken, not the test).',\n { testId: z.string().describe('The failing test case ID') },\n );\n\n tool(server, client, 'edit_test_code',\n 'Update an existing test case. WEB (Playwright) tests: provide the full replacement playwrightCode (read project://test-conventions for patterns; code is validated - fix any warnings returned). MOBILE (Appium) tests: provide the FULL replacement steps array (playwrightCode is rejected - mobile tests execute structured steps, not code). Read the current form first with get_test_code.',\n {\n testId: z.string().describe('The test case ID to edit'),\n playwrightCode: z.string().optional().describe('Web tests only: new Playwright test code (full file replacement)'),\n steps: z.array(z.record(z.unknown())).optional().describe('Mobile (Appium) tests only: FULL replacement structured step array (same shape as create_test steps). Include every step, not just the changed ones.'),\n name: z.string().optional().describe('New test name'),\n tags: z.array(z.string()).optional().describe('New tags array (e.g. [\"@regression\", \"@smoke\"])'),\n },\n );\n\n tool(server, client, 'update_test_status',\n 'Change the lifecycle status of one or more tests (e.g. promote DRAFT→ACTIVE, disable a flaky test). Re-activating exactly ONE bug-found test (QUARANTINED→ACTIVE) also queues a verification run and returns its testRunId; with several ids no run is queued.',\n {\n testIds: z.array(z.string()).describe('Test IDs to update'),\n status: z.enum(['DRAFT', 'ACTIVE', 'DISABLED', 'QUARANTINED']).describe('New status to set'),\n },\n );\n\n // ── Test Runs ────────────────────────────────────────────────────────\n\n tool(server, client, 'get_test_run',\n 'Get the status and result of a specific test run once (to wait for it to finish, use wait_for_run, not a loop of this). Look up by runId, or the latest run for a testId. A FAILED or ERROR run also carries its failure evidence: error (up to 4 KB), consoleLog (the last 200 lines, at most 20 KB), failedSteps, and for a mobile run domSnapshot (the page source at the failure). Screenshots: get_test_screenshots. No Playwright trace is recorded.',\n {\n runId: z.string().optional().describe('The test run ID'),\n testId: z.string().optional().describe('Get the most recent run for this test case ID'),\n },\n { readOnlyHint: true },\n );\n\n waitTool(server, client, 'wait_for_run',\n `Wait until a test run (run_test, heal_test, a verification run) is PASSED, FAILED or ERROR, then return it in the get_test_run shape, failure evidence included. Returns after at most timeoutSeconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS}) with timedOut: true and the current status if it is still pending or running; call it again to keep waiting. Use this instead of polling get_test_run.`,\n {\n runId: z.string().describe('The test run ID to wait for'),\n timeoutSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`How long to wait, in seconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS})`),\n },\n 'timeoutSeconds', WAIT_DEFAULT_SECONDS, { tool: 'wait_for_run', idKey: 'runId' },\n { readOnlyHint: true },\n );\n\n waitTool(server, client, 'wait_for_batch',\n `Wait until a batch from batch_run_tests is COMPLETED, then return its counts (total, completed, passed, failed, errored) and, when tests failed, up to 20 failures with their runId and error. Returns after at most timeoutSeconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS}) with timedOut: true and the current counts if it is still running; call it again to keep waiting. Use get_test_run on a failure's runId for its console log.`,\n {\n batchId: z.string().describe('The batch ID returned by batch_run_tests'),\n timeoutSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`How long to wait, in seconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS})`),\n },\n 'timeoutSeconds', WAIT_DEFAULT_SECONDS, { tool: 'wait_for_batch', idKey: 'batchId' },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'run_discovery',\n 'Start AI discovery for this project. For web, discovery uses the configured base URL. For mobile, pass platform and appId to explore a native app on a connected runner/device. Use survey to map features, deep with featureName to generate tests for one feature, or full for broad generation.',\n {\n mode: z.enum(['survey', 'deep', 'full']).optional().describe('Discovery mode. survey maps features/screens and generates no tests; deep generates tests for one feature; full explores and generates broadly.'),\n featureName: z.string().optional().describe('Required for deep mode: the feature name to generate tests for.'),\n focusAreas: z.array(z.string()).optional().describe('Optional routes, screens, or areas to focus discovery on.'),\n platform: z.enum(['IOS', 'ANDROID']).optional().describe('Mobile only: target platform. Requires appId.'),\n appId: z.string().optional().describe('Mobile only: bundle id/package id to explore. Requires platform.'),\n resetPolicy: z.enum(['AUTO', 'CLEAR_APP_DATA', 'PRESERVE_APP_DATA']).optional().describe('Mobile only: app-state policy for discovery/replay. AUTO uses default safe reset behavior.'),\n },\n );\n\n tool(server, client, 'get_discovery_status',\n 'Check progress for a discovery run. Omit discoveryRunId to get the latest discovery run for this project.',\n {\n discoveryRunId: z.string().optional().describe('Specific discovery run ID. Omit for latest.'),\n },\n { readOnlyHint: true },\n );\n\n // Custom handler - returns MCP image content blocks so the AI can see screenshots\n server.tool(\n 'get_test_screenshots',\n 'Get screenshots from a specific test run as images the AI can actually see. Use this to visually inspect what happened during a failed or passing run.',\n {\n runId: z.string().describe('The test run ID to fetch screenshots from'),\n limit: z.number().optional().describe('Max screenshots to return (default: 5, max: 10)'),\n },\n { readOnlyHint: true },\n async (args) => {\n try {\n const result = await client.executeTool('get_test_screenshots', args) as {\n runId: string;\n status: string;\n total: number;\n returned: number;\n screenshots: Array<{ index: number; data: string; mimeType: string }>;\n };\n\n if (!result.screenshots || result.screenshots.length === 0) {\n return {\n content: [{\n type: 'text' as const,\n text: `No screenshots available for run ${result.runId} (status: ${result.status})`,\n }],\n };\n }\n\n const content: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = [\n {\n type: 'text' as const,\n text: `Run ${result.runId} - status: ${result.status} - showing ${result.returned} of ${result.total} screenshot(s)`,\n },\n ];\n\n for (const shot of result.screenshots) {\n content.push({ type: 'image' as const, data: shot.data, mimeType: shot.mimeType });\n }\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: toolErrorText(err) }],\n isError: true,\n };\n }\n },\n );\n\n tool(server, client, 'cancel_test_run',\n 'Cancel a pending or running test run. Can cancel by run ID or cancel the latest running batch.',\n {\n runId: z.string().optional().describe('The specific test run ID to cancel'),\n cancelBatch: z.boolean().optional().describe('If true, cancel the latest batch run instead'),\n },\n );\n\n waitTool(server, client, 'batch_run_tests',\n 'Run multiple tests from the database individually. Filter by test IDs or suite name; omit both to run all ACTIVE tests. Each test executes in its own browser. Returns the batchId; follow it with wait_for_batch, or pass waitSeconds.',\n {\n testIds: z.array(z.string()).optional().describe('Specific test IDs to run'),\n suite: z.string().optional().describe('Run all active tests in this suite'),\n waitSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`Optional. Wait up to this many seconds (max ${WAIT_MAX_SECONDS}) for the batch to complete and return the wait_for_batch result (timedOut: true if still running). 0 or omitted: return at once with the batchId.`),\n },\n 'waitSeconds', 0, { tool: 'wait_for_batch', idKey: 'batchId' },\n );\n\n // ── Features & API Endpoints ──────────────────────────────────────────\n\n tool(server, client, 'list_features',\n 'List all features with test counts and failure counts (last 24h). Use failingTests > 0 to identify which features need attention, then call get_test_failures({ suite: featureName }) to drill into the full error details for that feature.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'generate_tests_for_feature',\n 'Start test generation for an existing feature by id or name. For mobile, run_discovery with mode=deep, featureName, platform, and appId gives explicit device/platform control.',\n {\n featureId: z.string().optional().describe('Feature ID. Either featureId or featureName is required.'),\n featureName: z.string().optional().describe('Feature name. Either featureId or featureName is required.'),\n },\n );\n\n tool(server, client, 'list_api_endpoints',\n 'List captured API endpoints for the current project. Optionally scope to a specific feature name.',\n {\n featureName: z.string().optional().describe('Optional feature name to scope the endpoint list'),\n },\n { readOnlyHint: true },\n );\n\n // ── Infrastructure ───────────────────────────────────────────────────\n\n tool(server, client, 'get_runner_health',\n 'Check whether the project\\'s configured runners are online, including available device capabilities.',\n {},\n { readOnlyHint: true },\n );\n\n // ── Reports & Observability ──────────────────────────────────────────\n\n tool(server, client, 'get_project_overview',\n 'Get a workspace-level QA overview: test counts, pass rates, runner readiness, features, coverage gaps, variables, schedules, and notifications.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_reports_summary',\n 'Get a high-level report: total tests (active, never-run), 7-day pass rate, today\\'s run count, and the 10 most recent test run results.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_regression_stats',\n 'Get test counts by lifecycle status (active, quarantined, draft, disabled, total; API specs count their inner tests) and the 7-day pass rate, with rate-limit, config-issue and cancelled runs left out. The same numbers as the dashboard\\'s regression stats.',\n {},\n { readOnlyHint: true },\n );\n\n // ── Schedules (Admin) ────────────────────────────────────────────────\n\n tool(server, client, 'list_schedules',\n 'List all scheduled test runs in the project.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'create_schedule',\n 'Create a new recurring test schedule. Runs all matching tests from the database on the configured interval.',\n {\n name: z.string().describe('Name for the schedule (e.g. \"Nightly smoke tests\")'),\n mode: z.enum(['batch']).describe('Run mode: batch (runs project tests from the database)'),\n intervalMinutes: z.union([z.literal(60), z.literal(360), z.literal(720), z.literal(1440), z.literal(2880), z.literal(10080)]).describe('Interval in minutes: 60 (hourly), 360 (6h), 720 (12h), 1440 (daily), 2880 (2 days), 10080 (weekly)'),\n tags: z.array(z.string()).optional().describe('Tags to filter which tests run (e.g. [\"@smoke\"]). Omit to run all ACTIVE tests.'),\n autoHeal: z.boolean().optional().describe(\"Heal this schedule's failed runs (default false). Takes effect only while the project's auto-heal (toggle_auto_heal) is on: a run is healed only when both are on.\"),\n enabled: z.boolean().optional().describe('Whether the schedule is active (default: true)'),\n runImmediately: z.boolean().optional().describe('Whether to trigger the first run immediately (default: true)'),\n },\n );\n\n tool(server, client, 'update_schedule',\n 'Update an existing test schedule.',\n {\n scheduleId: z.string().describe('The schedule ID to update'),\n name: z.string().optional().describe('New name for the schedule'),\n intervalMinutes: z.union([z.literal(60), z.literal(360), z.literal(720), z.literal(1440), z.literal(2880), z.literal(10080)]).optional().describe('New interval in minutes'),\n suite: z.string().optional().describe('New suite name to filter tests'),\n tags: z.array(z.string()).optional().describe('New tag filter'),\n autoHeal: z.boolean().optional().describe(\"Heal this schedule's failed runs. Takes effect only while the project's auto-heal (toggle_auto_heal) is on.\"),\n enabled: z.boolean().optional().describe('Enable or disable the schedule'),\n },\n );\n\n tool(server, client, 'delete_schedule',\n 'Delete (remove) a test schedule permanently.',\n { scheduleId: z.string().describe('The schedule ID to delete') },\n { destructiveHint: true },\n );\n\n tool(server, client, 'run_schedule_now',\n 'Trigger a scheduled run immediately instead of waiting for the next scheduled time.',\n { scheduleId: z.string().describe('The schedule ID to trigger') },\n );\n\n // ── Configuration ────────────────────────────────────────────────────\n\n tool(server, client, 'get_variables',\n 'Get all test variables (environment variables) for the current project. Secrets are redacted.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'set_variable',\n 'Set or update a test variable (environment variable) for the project. Used to configure base URLs, credentials, etc.',\n {\n key: z.string().describe('Variable name (e.g. BASE_URL, TEST_USER_EMAIL, API_KEY)'),\n value: z.string().describe('Variable value. Set value to empty string to remove a variable.'),\n secret: z.boolean().optional().describe('Whether this variable is a secret (will be redacted in UI)'),\n },\n );\n\n tool(server, client, 'create_test',\n 'Create a new test case. WEB projects: provide playwrightCode (read project://test-conventions for templates BEFORE writing code; on a production BASE_URL use its read-only guard template). The response may carry warnings (weak selectors, hardcoded hosts in URL assertions, vacuous checks, a missing read-only guard) - they never block, fix them with edit_test_code. MOBILE (Appium) projects: provide steps - a structured step array is the ONLY executable form for mobile (code is never executed). The test starts as DRAFT - auto-runs and auto-promotes to ACTIVE on pass. IMPORTANT: (1) Set suite to the EXACT feature name. (2) After creating all tests for a web feature, you MUST call add_api_endpoints.',\n {\n name: z.string().describe('Test name (e.g. \"Login - Happy Path\")'),\n playwrightCode: z.string().optional().describe('Web projects: full Playwright test code (required). Mobile projects: optional WebdriverIO export for humans - never executed.'),\n steps: z.array(z.record(z.unknown())).optional().describe('Mobile (Appium) projects ONLY - required. Ordered steps: { action: tap|type|clear|swipe|scroll|back|home|waitForElement|assertVisible|assertText, description, target?: { primary, fallbacks?, screenName? }, value? }. target.primary: \"css=#domId\" for WebView-rendered (hybrid) controls, resource-id/accessibility-id/text= only for genuinely native UI. type/assertText require value ({{TEST_VAR}} placeholders supported). Do not author OS permission-dialog taps. End with an assert step. Call get_feature first for mapped screens + known selectors.'),\n suite: z.string().optional().describe('MUST match an existing feature name exactly to link the test to that feature (e.g. \"Authentication\", not \"Auth Tests\")'),\n tags: z.array(z.string()).optional().describe('Tags (e.g. [\"@smoke\", \"@auth\"])'),\n },\n );\n\n // ── Features ─────────────────────────────────────────────────────────\n\n tool(server, client, 'get_feature',\n 'Get detailed info about a feature including its pages, test plans, existing tests with lastRunStatus, and AI insights.',\n { featureName: z.string().describe('The feature name (case-insensitive match)') },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_spec_coverage',\n 'Map spec items to the existing tests of a feature (heuristic: test names, tags and the copy in their code) and list the items no test covers yet. Pass the spec you were given as one item per line item, scoped by page (\"Sign up: malformed email shows an error\"); without specItems it uses the feature\\'s stored test plans. Call it before you stop writing tests for a feature, then cover every uncovered item.',\n {\n featureName: z.string().describe('The feature name (case-insensitive) - its suite holds the tests'),\n specItems: z.array(z.string()).optional().describe('Spec items, one per behaviour and page (max 200)'),\n },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'create_feature',\n 'Create a new feature with page routes. Read the developer\\'s route files to provide accurate page URLs. Pages are used for coverage tracking and site map. Idempotent, not an upsert: if an active feature with this name already exists it is returned unchanged with existing: true (use update_feature with its id to change it). Fails only when the name belongs to an archived feature (restore it with restore_feature, or pick another name).',\n {\n name: z.string().describe('Feature name (e.g. \"Checkout Flow\")'),\n description: z.string().optional().describe('What the feature does and what to test'),\n pages: z.array(z.string()).describe('Page routes this feature covers (e.g. [\"/checkout\", \"/checkout/payment\"]). Required for test generation.'),\n criticality: z.enum(['critical', 'high', 'medium', 'low']).optional().describe('Feature criticality level (default: medium)'),\n },\n );\n\n // ── Full Test Code ───────────────────────────────────────────────────\n\n tool(server, client, 'get_test_code',\n 'Get the full (untruncated) Playwright test code for a test case. Use this when you need to read, analyze, or prepare edits for the complete test code.',\n { testId: z.string().describe('The test case ID') },\n { readOnlyHint: true },\n );\n\n // ── Project Settings ─────────────────────────────────────────────────\n\n tool(server, client, 'set_runner_preference',\n 'Switch between cloud and local runner for test execution.',\n { preference: z.enum(['cloud', 'local']).describe('Runner type: cloud (shared infrastructure) or local (runs on your machine)') },\n );\n\n tool(server, client, 'toggle_auto_heal',\n 'Enable or disable automatic AI healing when tests fail: the verification run after create_test, batch and suite runs. When off, no heal is queued automatically; heal_test still works.',\n { enabled: z.boolean().describe('true to enable auto-heal, false to disable') },\n );\n\n // ── Test Data ────────────────────────────────────────────────────────\n\n tool(server, client, 'list_test_data',\n 'List all test data sets in the project. Each set contains key-value pairs injected into AI prompts and test runs.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'create_test_data',\n 'Create a new test data set with key-value pairs. Max 200 entries per set.',\n {\n name: z.string().describe('Name of the test data set (max 100 chars)'),\n description: z.string().optional().describe('Optional description (max 1000 chars)'),\n data: z.record(z.string()).describe('Key-value pairs (e.g. {\"email\": \"test@example.com\", \"password\": \"secret\"})'),\n },\n );\n\n tool(server, client, 'delete_test_data',\n 'Delete a test data set by ID.',\n { dataSetId: z.string().describe('The test data set ID to delete') },\n { destructiveHint: true },\n );\n\n // ── Feature Management ───────────────────────────────────────────────\n\n tool(server, client, 'update_feature',\n 'Update a feature\\'s name, description, pages, or criticality.',\n {\n featureId: z.string().describe('The feature ID to update'),\n name: z.string().optional().describe('New feature name'),\n description: z.string().optional().describe('New description'),\n pages: z.array(z.string()).optional().describe('Updated page routes'),\n criticality: z.enum(['critical', 'high', 'medium', 'low']).optional().describe('Feature criticality level'),\n },\n );\n\n tool(server, client, 'archive_feature',\n 'Archive a feature (soft delete). Archived features are hidden from the dashboard but can be restored with restore_feature.',\n { featureId: z.string().describe('The feature ID to archive') },\n { destructiveHint: true },\n );\n\n tool(server, client, 'restore_feature',\n 'Restore an archived feature so it shows in the dashboard again. Does nothing for a feature that is already active. Fails if an active feature now holds the same name.',\n { featureId: z.string().describe('The ID of the archived feature to restore') },\n );\n\n // ── Site Map ─────────────────────────────────────────────────────────\n\n tool(server, client, 'get_site_map_coverage',\n 'Get site map coverage statistics: total pages, pages with tests, elements covered, coverage percentage, and per-page breakdown.',\n {},\n { readOnlyHint: true },\n );\n\n // ── Issue Triage ─────────────────────────────────────────────────────\n\n tool(server, client, 'list_bugs',\n 'Get all detected bugs: app bugs (quarantined tests with @bug-found), security findings, flaky tests, and visual issues flagged as bugs.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'list_visual_issues',\n 'List visual issues found on the app\\'s pages by the layout checks that run during surveys and discovery (clipped or overflowing text, covered or cut-off controls, broken images, sideways scrolling on phones, small tap targets, low contrast). Each has the page route, viewport, CSS selector, element box and a description, so you can find and fix the element in the code. Visual issues never fail tests.',\n {\n status: z.enum(['OPEN', 'ACCEPTED', 'DISMISSED', 'GONE']).optional().describe('Filter by status (default OPEN). ACCEPTED = flagged as a bug; GONE = no longer seen on the page'),\n severity: z.enum(['high', 'medium', 'low']).optional().describe('Filter by severity'),\n viewport: z.enum(['desktop', 'phone']).optional().describe('Filter by viewport'),\n route: z.string().optional().describe('Only this page route, e.g. /pricing'),\n },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'flag_as_bug',\n 'Mark a failing test as a confirmed application bug - quarantines it with @bug-found and records the bug description. Use when the failure is caused by broken app behaviour, NOT a test script issue. Do NOT use for selector changes or timeout failures - use heal_test or edit_test_code for those.',\n {\n testId: z.string().describe('The failing test case ID'),\n description: z.string().describe('What is broken in the application (e.g. \"POST /api/checkout returns 500 when payment method is valid\")'),\n },\n );\n\n // ── Security Audit ───────────────────────────────────────────────────\n\n tool(server, client, 'run_security_audit',\n 'Run an AI security audit on captured API traffic. Requires prior browser-based test execution (discovery or capture) - analyzes real network traffic, not just registered endpoints. If no traffic exists, the developer can trigger \"Security Audit\" from the Dashboard which launches a live browser to capture it.',\n {},\n );\n\n tool(server, client, 'list_security_findings',\n 'List security audit findings with optional filters. Returns severity, category, status, and affected endpoints.',\n {\n severity: z.enum(['critical', 'high', 'medium', 'low', 'info']).optional().describe('Filter by severity'),\n status: z.enum(['open', 'acknowledged', 'resolved', 'false-positive']).optional().describe('Filter by status'),\n },\n { readOnlyHint: true },\n );\n\n // ── Project Settings ─────────────────────────────────────────────────\n\n tool(server, client, 'set_app_brief',\n 'Set or update the project\\'s application brief - a description of the app used by AI for better test generation.',\n { appBrief: z.string().describe('Application description. Set empty to clear.') },\n );\n\n // ── Coverage & Draft Verification ────────────────────────────────────\n\n tool(server, client, 'get_coverage_summary',\n 'Get feature-level test coverage: planned vs existing vs passing tests, plus API endpoint coverage. Shows uncoveredEndpoints per feature - use this to find which API endpoints still need tests.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'run_draft_tests',\n 'Run all DRAFT tests to verify them. Tests that pass auto-promote to ACTIVE. Optionally scope to a feature.',\n {\n featureName: z.string().optional().describe('Only run drafts for this feature (optional)'),\n },\n );\n\n // ── Project Setup ────────────────────────────────────────────────────\n\n tool(server, client, 'setup_project',\n 'One-shot project setup from codebase knowledge. Sets app brief, base URL, test credentials, and creates features with pages - all in one call. Use this when a developer says \"set up test coverage for my app.\"',\n {\n appBrief: z.string().optional().describe('Description of the application (what it does, key features, tech stack)'),\n baseUrl: z.string().describe('Application base URL (e.g. \"https://myapp.com\" or \"http://localhost:3000\")'),\n testCredentials: z.object({\n email: z.string().optional().describe('Test user email'),\n password: z.string().optional().describe('Test user password'),\n }).optional().describe('Login credentials for test execution'),\n features: z.array(z.object({\n name: z.string().describe('Feature name'),\n description: z.string().optional().describe('What the feature does'),\n pages: z.array(z.string()).describe('Page routes (e.g. [\"/login\", \"/register\"])'),\n criticality: z.enum(['critical', 'high', 'medium', 'low']).optional().describe('Feature criticality'),\n })).describe('Features to create with their page routes'),\n },\n );\n\n // ── API Endpoints ───────────────────────────────────────────────────\n\n tool(server, client, 'add_api_endpoints',\n 'Register API endpoints for a feature from codebase knowledge. Read the developer\\'s route handlers to extract methods, paths, request/response shapes. Populates the API intelligence dashboard.',\n {\n featureName: z.string().describe('Feature name to link these endpoints to'),\n endpoints: z.array(z.object({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).describe('HTTP method'),\n path: z.string().describe('API path (e.g. \"/api/auth/login\")'),\n requestShape: z.record(z.string()).optional().describe('Request body fields with types (e.g. { \"email\": \"string\" })'),\n responseShapes: z.record(z.record(z.string())).optional().describe('Response shapes by status code (e.g. { \"200\": { \"token\": \"string\" } })'),\n requiresAuth: z.boolean().optional().describe('Whether endpoint requires authentication'),\n })).describe('API endpoints to register'),\n },\n );\n\n // ── Failure Diagnostics ──────────────────────────────────────────────\n\n tool(server, client, 'get_test_failures',\n 'Get recent test failures with full error messages, Playwright code, step-level failures, and heal context. Use this to understand why tests are failing.',\n {\n limit: z.number().optional().describe('Max failures to return (default: 50, max: 100)'),\n suite: z.string().optional().describe('Filter by feature/suite name'),\n mode: z.enum(['run', 'heal', 'all']).optional().describe('run = regular failures, heal = heal attempt failures, all = both (default)'),\n sinceHours: z.number().optional().describe('Only failures from the last N hours (default: 24)'),\n },\n { readOnlyHint: true },\n );\n}\n"],"mappings":";;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAMA,SAAS,YAAY,WAAW,gBAAAA,eAAc,qBAAqB;AACnE,SAAS,WAAAC,UAAS,YAAY;AAQ9B,SAAS,cAAc,MAA0B;AAC/C,QAAM,OAA+B,CAAC;AACtC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,IAAI,WAAW,IAAI,KAAK,IAAI,IAAI,KAAK,QAAQ;AAC/C,WAAK,IAAI,MAAM,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC;AAAA,IAC/B;AAAA,EACF;AAEA,QAAM,SAAS,KAAK,SAAS,KAAK,QAAQ,IAAI,sBAAsB;AACpE,QAAM,QAAQ,KAAK,OAAO,KAAK,QAAQ,IAAI;AAC3C,QAAM,YAAY,KAAK,YAAY,KAAK,QAAQ,IAAI;AAEpD,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,gFAA2E;AACzF,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,WAAW;AACd,YAAQ,MAAM,qFAAgF;AAC9F,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,EAAE,QAAQ,OAAO,QAAQ,OAAO,EAAE,GAAG,OAAO,UAAU;AAC/D;AAGA,SAAS,cAAcC,SAAkB;AACvC,SAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM;AAAA,MACJ;AAAA,MAAM;AAAA,MACN;AAAA,MAAaA,QAAO;AAAA,MACpB;AAAA,MAAWA,QAAO;AAAA,MAClB;AAAA,MAAgBA,QAAO;AAAA,IACzB;AAAA,EACF;AACF;AAiBA,SAAS,kBAAkB,KAAa,QAAmB,OAAkD;AAC3G,QAAM,MAAMD,SAAQ,KAAK,OAAO,SAAS;AACzC,QAAM,WAAWA,SAAQ,KAAK,OAAO,UAAU;AAI/C,MAAI,OAAO,cAAc,OAAO,CAAC,WAAW,GAAG,GAAG;AAChD,WAAO;AAAA,EACT;AAEA,MAAI,WAAoC,CAAC;AACzC,MAAI,WAAW,QAAQ,GAAG;AACxB,QAAI;AACF,iBAAW,KAAK,MAAMD,cAAa,UAAU,OAAO,CAAC;AAAA,IACvD,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,QAAM,UAAW,SAAS,OAAO,UAAU,KAAK,CAAC;AACjD,QAAM,WAAW,iBAAiB;AAClC,UAAQ,aAAa,IAAI;AACzB,WAAS,OAAO,UAAU,IAAI;AAG9B,MAAI,CAAC,WAAW,GAAG,GAAG;AACpB,cAAU,KAAK,EAAE,WAAW,KAAK,CAAC;AAAA,EACpC;AAEA,gBAAc,UAAU,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI,MAAM,OAAO;AACzE,SAAO,WAAW,YAAY;AAChC;AAEO,SAAS,UAAU;AACxB,QAAME,UAAS,cAAc,QAAQ,KAAK,MAAM,CAAC,CAAC;AAClD,QAAM,QAAQ,cAAcA,OAAM;AAClC,QAAM,MAAM,QAAQ,IAAI;AAExB,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,2BAA2B;AACvC,UAAQ,IAAI,0JAA6B;AACzC,UAAQ,IAAI,EAAE;AAEd,MAAI,aAAa;AAEjB,aAAW,UAAU,aAAa;AAChC,UAAM,SAAS,kBAAkB,KAAK,QAAQ,KAAK;AACnD,QAAI,WAAW,WAAW;AACxB;AAAA,IACF;AACA,UAAM,OAAO,KAAK,OAAO,WAAW,OAAO,UAAU;AACrD,QAAI,WAAW,WAAW;AACxB,cAAQ,IAAI,YAAO,OAAO,IAAI,cAAc,IAAI,EAAE;AAAA,IACpD,OAAO;AACL,cAAQ,IAAI,YAAO,OAAO,IAAI,cAAc,IAAI,EAAE;AAAA,IACpD;AACA,iBAAa;AAAA,EACf;AAEA,MAAI,CAAC,YAAY;AAEf,UAAM,SAAS,YAAY,CAAC;AAC5B,sBAAkB,KAAK,QAAQ,KAAK;AACpC,YAAQ,IAAI,oBAAe,OAAO,UAAU,2BAA2B;AACvE,YAAQ,IAAI,EAAE;AACd,YAAQ,IAAI,uFAAuF;AAAA,EACrG;AAEA,UAAQ,IAAI,EAAE;AACd,UAAQ,IAAI,iDAAiD;AAC7D,UAAQ,IAAI,yDAAyD;AACrE,UAAQ,IAAI,EAAE;AAChB;AA5IA,IA6DM;AA7DN;AAAA;AAAA;AA6DA,IAAM,cAA2B;AAAA,MAC/B,EAAE,MAAM,eAAe,WAAW,KAAK,YAAY,aAAa,YAAY,aAAa;AAAA,MACzF,EAAE,MAAM,UAAU,WAAW,WAAW,YAAY,YAAY,YAAY,aAAa;AAAA,MACzF,EAAE,MAAM,YAAY,WAAW,aAAa,YAAY,YAAY,YAAY,aAAa;AAAA,MAC7F,EAAE,MAAM,WAAW,WAAW,WAAW,YAAY,YAAY,YAAY,UAAU;AAAA,IACzF;AAAA;AAAA;;;AClEA,SAAS,iBAAiB;AAC1B,SAAS,4BAA4B;;;ACDrC,SAAS,oBAAoB;AAC7B,SAAS,eAAe;AAQxB,SAAS,UAAU,MAAwC;AACzD,QAAM,OAA+B,CAAC;AACtC,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,QAAI,IAAI,WAAW,IAAI,KAAK,IAAI,IAAI,KAAK,QAAQ;AAC/C,YAAM,MAAM,IAAI,MAAM,CAAC;AACvB,WAAK,GAAG,IAAI,KAAK,EAAE,CAAC;AAAA,IACtB;AAAA,EACF;AACA,SAAO;AACT;AAEA,SAAS,cAAyC;AAChD,MAAI;AACF,UAAM,UAAU,aAAa,QAAQ,QAAQ,IAAI,GAAG,kBAAkB,GAAG,OAAO;AAChF,UAAM,OAAO,KAAK,MAAM,OAAO;AAC/B,WAAO;AAAA,MACL,QAAQ,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AAAA,MACxD,WAAW,OAAO,KAAK,cAAc,WAAW,KAAK,YAAY;AAAA,IACnE;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEO,SAAS,gBAAkC;AAChD,QAAM,UAAU,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC/C,QAAM,UAAU,YAAY;AAE5B,QAAM,SACJ,QAAQ,SAAS,KACjB,QAAQ,IAAI,sBACZ,QAAQ;AAEV,QAAM,QACJ,QAAQ,OAAO,KACf,QAAQ,IAAI;AAEd,QAAM,YACJ,QAAQ,YAAY,KACpB,QAAQ,IAAI,yBACZ,QAAQ;AAEV,MAAI,CAAC,QAAQ;AACX,YAAQ,MAAM,uFAAuF;AACrG,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,OAAO;AACV,YAAQ,MAAM,8DAA8D;AAC5E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACA,MAAI,CAAC,WAAW;AACd,YAAQ,MAAM,gGAAgG;AAC9G,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,SAAO,EAAE,QAAQ,OAAO,QAAQ,OAAO,EAAE,GAAG,OAAO,UAAU;AAC/D;;;AC9DO,IAAM,2BAA2B;AAEjC,IAAM,mBAAN,cAA+B,MAAM;AAAA,EACjC;AAAA,EACA;AAAA,EAET,YAAY,QAAgB,MAA+B;AACzD,UAAM,UAAU,OAAO,KAAK,UAAU,YAAY,KAAK,MAAM,SAAS,IAClE,KAAK,QACL,aAAa,MAAM;AACvB,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;AAGO,SAAS,cAAc,KAAsB;AAClD,MAAI,eAAe,oBAAoB,IAAI,KAAK,SAAS,0BAA0B;AACjF,WAAO,KAAK,UAAU,IAAI,IAAI;AAAA,EAChC;AACA,SAAO,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC;AACnE;AAEO,IAAM,mBAAN,MAAuB;AAAA,EAC5B,YACU,SACA,OACA,WACR;AAHQ;AACA;AACA;AAAA,EACP;AAAA,EAEH,MAAM,YAAYC,OAAc,OAAgC,CAAC,GAAqB;AACpF,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,GAAG,KAAK,OAAO,oBAAoB;AAAA,QACnD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,iBAAiB,UAAU,KAAK,KAAK;AAAA,UACrC,gBAAgB,KAAK;AAAA,UACrB,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,MAAAA,OAAM,KAAK,CAAC;AAAA,QACnC,QAAQ,YAAY,QAAQ,IAAO;AAAA,MACrC,CAAC;AAAA,IACH,SAAS,KAAK;AAEZ,YAAM,QAAQ,eAAe,SAAS,IAAI,iBAAiB,QAAQ,IAAI,MAAM,UAAU;AACvF,YAAM,MAAM,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC3D,YAAM,IAAI,MAAM,sBAAsB,GAAG,GAAG,QAAQ,KAAK,KAAK,MAAM,EAAE,EAAE;AAAA,IAC1E;AAEA,QAAI,CAAC,IAAI,IAAI;AACX,YAAM,SAAS,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE;AACvE,YAAM,OAAO,UAAU,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,IACtE,SACA,EAAE,OAAO,IAAI,WAAW;AAC5B,YAAM,IAAI,iBAAiB,IAAI,QAAQ,IAAI;AAAA,IAC7C;AAEA,WAAO,IAAI,KAAK;AAAA,EAClB;AAAA;AAAA,EAGA,MAAM,qBAA8C;AAClD,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,6BAA6B;AAAA,QAClE,SAAS;AAAA,UACP,iBAAiB,UAAU,KAAK,KAAK;AAAA,UACrC,gBAAgB,KAAK;AAAA,QACvB;AAAA,QACA,QAAQ,YAAY,QAAQ,GAAM;AAAA,MACpC,CAAC;AACD,UAAI,CAAC,IAAI,GAAI,QAAO;AACpB,aAAO,MAAM,IAAI,KAAK;AAAA,IACxB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,cAAgC;AACpC,QAAI;AACF,YAAM,MAAM,MAAM,MAAM,GAAG,KAAK,OAAO,WAAW;AAAA,QAChD,QAAQ,YAAY,QAAQ,GAAI;AAAA,MAClC,CAAC;AACD,aAAO,IAAI;AAAA,IACb,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AC9FA,SAAS,SAAS;AAWlB,SAAS,KACPC,SACAC,SACA,MACA,aACA,QACA,aACA;AACA,QAAM,UAAU,OAAO,SAAkC;AACvD,QAAI;AACF,YAAM,SAAS,MAAMA,QAAO,YAAY,MAAM,IAAI;AAClD,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC;AAAA,MAC5E;AAAA,IACF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,cAAc,GAAG,EAAE,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AAEA,MAAI,aAAa;AACf,IAAAD,QAAO,KAAK,MAAM,aAAa,QAAQ,aAAa,OAAO;AAAA,EAC7D,OAAO;AACL,IAAAA,QAAO,KAAK,MAAM,aAAa,QAAQ,OAAO;AAAA,EAChD;AACF;AAMA,IAAM,0BAA0B;AAChC,IAAM,uBAAuB;AAC7B,IAAM,mBAAmB;AAIzB,SAAS,SAAS,OAAkD;AAClE,SAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,gBAAgB,KAAc,UAA0B;AAC/D,QAAM,IAAI,OAAO,QAAQ,WAAW,MAAM,OAAO;AACjD,MAAI,CAAC,OAAO,SAAS,CAAC,EAAG,QAAO;AAChC,SAAO,KAAK,IAAI,KAAK,IAAI,GAAG,CAAC,GAAG,gBAAgB;AAClD;AAMA,eAAe,gBACbC,SACA,MACA,MACA,SACA,cACA,MACkB;AAClB,QAAM,WAAW,KAAK,IAAI,IAAI,eAAe;AAC7C,QAAM,QAAQ,KAAK,IAAI,cAAc,uBAAuB;AAC5D,MAAI,SAAS,MAAMA,QAAO,YAAY,MAAM,EAAE,GAAG,MAAM,CAAC,OAAO,GAAG,MAAM,CAAC;AACzE,aAAS;AACP,QAAI,CAAC,SAAS,MAAM,KAAK,OAAO,aAAa,KAAM,QAAO;AAC1D,UAAM,KAAK,OAAO,KAAK,KAAK;AAC5B,UAAM,OAAO,KAAK,OAAO,WAAW,KAAK,IAAI,KAAK,GAAI;AACtD,QAAI,OAAO,OAAO,YAAY,OAAO,EAAG,QAAO;AAC/C,aAAS,MAAMA,QAAO,YAAY,KAAK,MAAM,EAAE,CAAC,KAAK,KAAK,GAAG,IAAI,gBAAgB,KAAK,IAAI,MAAM,uBAAuB,EAAE,CAAC;AAAA,EAC5H;AACF;AAEA,SAAS,SACPD,SACAC,SACA,MACA,aACA,QACA,SACA,gBACA,MACA,aACA;AACA,QAAM,UAAU,OAAO,SAAkC;AACvD,QAAI;AACF,YAAM,UAAU,gBAAgB,KAAK,OAAO,GAAG,cAAc;AAC7D,YAAM,SAAS,UAAU,IACrB,MAAM,gBAAgBA,SAAQ,MAAM,MAAM,SAAS,SAAS,IAAI,IAChE,MAAMA,QAAO,YAAY,MAAM,IAAI;AACvC,aAAO,EAAE,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,IACvF,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,cAAc,GAAG,EAAE,CAAC;AAAA,QAC7D,SAAS;AAAA,MACX;AAAA,IACF;AAAA,EACF;AACA,MAAI,aAAa;AACf,IAAAD,QAAO,KAAK,MAAM,aAAa,QAAQ,aAAa,OAAO;AAAA,EAC7D,OAAO;AACL,IAAAA,QAAO,KAAK,MAAM,aAAa,QAAQ,OAAO;AAAA,EAChD;AACF;AAEO,SAAS,iBAAiBA,SAAmBC,SAA0B;AAG5E;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,KAAK,CAAC,SAAS,UAAU,YAAY,aAAa,CAAC,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,MACpH,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sBAAsB;AAAA,MAC5D,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,sDAAsD;AAAA,MAC7F,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,MACrF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,MACrF,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uDAAuD;AAAA,IAChG;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,kBAAkB,EAAE;AAAA,IAClD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAASD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACvB;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,OAAO,EAAE,SAAS,yBAAyB;AAAA,MACrD,UAAU,EAAE,KAAK,CAAC,OAAO,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,+GAA+G;AAAA,MACxK,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mJAAmJ;AAAA,MACzL,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS,EAAE,SAAS,+CAA+C,gBAAgB,uJAAuJ;AAAA,IACjS;AAAA,IACA;AAAA,IAAe;AAAA,IAAG,EAAE,MAAM,gBAAgB,OAAO,QAAQ;AAAA,EAC3D;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,4BAA4B,EAAE;AAAA,IAC5D,EAAE,iBAAiB,KAAK;AAAA,EAC1B;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,0BAA0B,EAAE;AAAA,EAC5D;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,MACtD,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kEAAkE;AAAA,MACjH,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,sJAAsJ;AAAA,MAChN,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,eAAe;AAAA,MACpD,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,IACjG;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,oBAAoB;AAAA,MAC1D,QAAQ,EAAE,KAAK,CAAC,SAAS,UAAU,YAAY,aAAa,CAAC,EAAE,SAAS,mBAAmB;AAAA,IAC7F;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iBAAiB;AAAA,MACvD,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,IACxF;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAASD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACvB,kNAAkN,oBAAoB,SAAS,gBAAgB;AAAA,IAC/P;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,6BAA6B;AAAA,MACxD,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS,EAAE,SAAS,yCAAyC,oBAAoB,SAAS,gBAAgB,GAAG;AAAA,IACvK;AAAA,IACA;AAAA,IAAkB;AAAA,IAAsB,EAAE,MAAM,gBAAgB,OAAO,QAAQ;AAAA,IAC/E,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAASD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACvB,gPAAgP,oBAAoB,SAAS,gBAAgB;AAAA,IAC7R;AAAA,MACE,SAAS,EAAE,OAAO,EAAE,SAAS,0CAA0C;AAAA,MACvE,gBAAgB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS,EAAE,SAAS,yCAAyC,oBAAoB,SAAS,gBAAgB,GAAG;AAAA,IACvK;AAAA,IACA;AAAA,IAAkB;AAAA,IAAsB,EAAE,MAAM,kBAAkB,OAAO,UAAU;AAAA,IACnF,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM,EAAE,KAAK,CAAC,UAAU,QAAQ,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,iJAAiJ;AAAA,MAC9M,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iEAAiE;AAAA,MAC7G,YAAY,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,2DAA2D;AAAA,MAC/G,UAAU,EAAE,KAAK,CAAC,OAAO,SAAS,CAAC,EAAE,SAAS,EAAE,SAAS,+CAA+C;AAAA,MACxG,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kEAAkE;AAAA,MACxG,aAAa,EAAE,KAAK,CAAC,QAAQ,kBAAkB,mBAAmB,CAAC,EAAE,SAAS,EAAE,SAAS,4FAA4F;AAAA,IACvL;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6CAA6C;AAAA,IAC9F;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAGA,EAAAD,QAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACtE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iDAAiD;AAAA,IACzF;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,IACrB,OAAO,SAAS;AACd,UAAI;AACF,cAAM,SAAS,MAAMC,QAAO,YAAY,wBAAwB,IAAI;AAQpE,YAAI,CAAC,OAAO,eAAe,OAAO,YAAY,WAAW,GAAG;AAC1D,iBAAO;AAAA,YACL,SAAS,CAAC;AAAA,cACR,MAAM;AAAA,cACN,MAAM,oCAAoC,OAAO,KAAK,aAAa,OAAO,MAAM;AAAA,YAClF,CAAC;AAAA,UACH;AAAA,QACF;AAEA,cAAM,UAAqG;AAAA,UACzG;AAAA,YACE,MAAM;AAAA,YACN,MAAM,OAAO,OAAO,KAAK,cAAc,OAAO,MAAM,cAAc,OAAO,QAAQ,OAAO,OAAO,KAAK;AAAA,UACtG;AAAA,QACF;AAEA,mBAAW,QAAQ,OAAO,aAAa;AACrC,kBAAQ,KAAK,EAAE,MAAM,SAAkB,MAAM,KAAK,MAAM,UAAU,KAAK,SAAS,CAAC;AAAA,QACnF;AAEA,eAAO,EAAE,QAAQ;AAAA,MACnB,SAAS,KAAK;AACZ,eAAO;AAAA,UACL,SAAS,CAAC,EAAE,MAAM,QAAiB,MAAM,cAAc,GAAG,EAAE,CAAC;AAAA,UAC7D,SAAS;AAAA,QACX;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oCAAoC;AAAA,MAC1E,aAAa,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,8CAA8C;AAAA,IAC7F;AAAA,EACF;AAEA;AAAA,IAASD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACvB;AAAA,IACA;AAAA,MACE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,0BAA0B;AAAA,MAC3E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oCAAoC;AAAA,MAC1E,aAAa,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,gBAAgB,EAAE,SAAS,EAAE,SAAS,+CAA+C,gBAAgB,oJAAoJ;AAAA,IAC9R;AAAA,IACA;AAAA,IAAe;AAAA,IAAG,EAAE,MAAM,kBAAkB,OAAO,UAAU;AAAA,EAC/D;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,WAAW,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,0DAA0D;AAAA,MACpG,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,4DAA4D;AAAA,IAC1G;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kDAAkD;AAAA,IAChG;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,SAAS,oDAAoD;AAAA,MAC9E,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE,SAAS,wDAAwD;AAAA,MACzF,iBAAiB,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,GAAG,GAAG,EAAE,QAAQ,GAAG,GAAG,EAAE,QAAQ,IAAI,GAAG,EAAE,QAAQ,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC,CAAC,EAAE,SAAS,oGAAoG;AAAA,MAC3O,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,iFAAiF;AAAA,MAC/H,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,oKAAoK;AAAA,MAC9M,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,MACzF,gBAAgB,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,8DAA8D;AAAA,IAChH;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,YAAY,EAAE,OAAO,EAAE,SAAS,2BAA2B;AAAA,MAC3D,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,MAChE,iBAAiB,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,QAAQ,GAAG,GAAG,EAAE,QAAQ,GAAG,GAAG,EAAE,QAAQ,IAAI,GAAG,EAAE,QAAQ,IAAI,GAAG,EAAE,QAAQ,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,yBAAyB;AAAA,MAC3K,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gCAAgC;AAAA,MACtE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,gBAAgB;AAAA,MAC9D,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,6GAA6G;AAAA,MACvJ,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,gCAAgC;AAAA,IAC3E;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,2BAA2B,EAAE;AAAA,IAC/D,EAAE,iBAAiB,KAAK;AAAA,EAC1B;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,4BAA4B,EAAE;AAAA,EAClE;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,KAAK,EAAE,OAAO,EAAE,SAAS,yDAAyD;AAAA,MAClF,OAAO,EAAE,OAAO,EAAE,SAAS,iEAAiE;AAAA,MAC5F,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,4DAA4D;AAAA,IACtG;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,SAAS,uCAAuC;AAAA,MACjE,gBAAgB,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,+HAA+H;AAAA,MAC9K,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,miBAAmiB;AAAA,MAC7lB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wHAAwH;AAAA,MAC9J,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,iCAAiC;AAAA,IACjF;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,aAAa,EAAE,OAAO,EAAE,SAAS,2CAA2C,EAAE;AAAA,IAChF,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,aAAa,EAAE,OAAO,EAAE,SAAS,iEAAiE;AAAA,MAClG,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,kDAAkD;AAAA,IACvG;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,SAAS,qCAAqC;AAAA,MAC/D,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,wCAAwC;AAAA,MACpF,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,0GAA0G;AAAA,MAC9I,aAAa,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,6CAA6C;AAAA,IAC9H;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,kBAAkB,EAAE;AAAA,IAClD,EAAE,cAAc,KAAK;AAAA,EACvB;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,YAAY,EAAE,KAAK,CAAC,SAAS,OAAO,CAAC,EAAE,SAAS,4EAA4E,EAAE;AAAA,EAClI;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS,4CAA4C,EAAE;AAAA,EAChF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM,EAAE,OAAO,EAAE,SAAS,2CAA2C;AAAA,MACrE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uCAAuC;AAAA,MACnF,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,4EAA4E;AAAA,IAClH;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,gCAAgC,EAAE;AAAA,IACnE,EAAE,iBAAiB,KAAK;AAAA,EAC1B;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,WAAW,EAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,MACzD,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,kBAAkB;AAAA,MACvD,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iBAAiB;AAAA,MAC7D,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,qBAAqB;AAAA,MACpE,aAAa,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,2BAA2B;AAAA,IAC5G;AAAA,EACF;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,2BAA2B,EAAE;AAAA,IAC9D,EAAE,iBAAiB,KAAK;AAAA,EAC1B;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,WAAW,EAAE,OAAO,EAAE,SAAS,2CAA2C,EAAE;AAAA,EAChF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,KAAK,CAAC,QAAQ,YAAY,aAAa,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,iGAAiG;AAAA,MAC/K,UAAU,EAAE,KAAK,CAAC,QAAQ,UAAU,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MACpF,UAAU,EAAE,KAAK,CAAC,WAAW,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MAC/E,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,qCAAqC;AAAA,IAC7E;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,QAAQ,EAAE,OAAO,EAAE,SAAS,0BAA0B;AAAA,MACtD,aAAa,EAAE,OAAO,EAAE,SAAS,wGAAwG;AAAA,IAC3I;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,EACH;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,UAAU,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,OAAO,MAAM,CAAC,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MACxG,QAAQ,EAAE,KAAK,CAAC,QAAQ,gBAAgB,YAAY,gBAAgB,CAAC,EAAE,SAAS,EAAE,SAAS,kBAAkB;AAAA,IAC/G;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,EAAE,UAAU,EAAE,OAAO,EAAE,SAAS,8CAA8C,EAAE;AAAA,EAClF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA,CAAC;AAAA,IACD,EAAE,cAAc,KAAK;AAAA,EACvB;AAEA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,6CAA6C;AAAA,IAC3F;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,yEAAyE;AAAA,MAClH,SAAS,EAAE,OAAO,EAAE,SAAS,4EAA4E;AAAA,MACzG,iBAAiB,EAAE,OAAO;AAAA,QACxB,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,iBAAiB;AAAA,QACvD,UAAU,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,oBAAoB;AAAA,MAC/D,CAAC,EAAE,SAAS,EAAE,SAAS,sCAAsC;AAAA,MAC7D,UAAU,EAAE,MAAM,EAAE,OAAO;AAAA,QACzB,MAAM,EAAE,OAAO,EAAE,SAAS,cAAc;AAAA,QACxC,aAAa,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,uBAAuB;AAAA,QACnE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,4CAA4C;AAAA,QAChF,aAAa,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,qBAAqB;AAAA,MACtG,CAAC,CAAC,EAAE,SAAS,2CAA2C;AAAA,IAC1D;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,aAAa,EAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,MAC1E,WAAW,EAAE,MAAM,EAAE,OAAO;AAAA,QAC1B,QAAQ,EAAE,KAAK,CAAC,OAAO,QAAQ,OAAO,SAAS,QAAQ,CAAC,EAAE,SAAS,aAAa;AAAA,QAChF,MAAM,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,QAC7D,cAAc,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,SAAS,EAAE,SAAS,6DAA6D;AAAA,QACpH,gBAAgB,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,SAAS,wEAAwE;AAAA,QAC3I,cAAc,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,0CAA0C;AAAA,MAC1F,CAAC,CAAC,EAAE,SAAS,2BAA2B;AAAA,IAC1C;AAAA,EACF;AAIA;AAAA,IAAKD;AAAA,IAAQC;AAAA,IAAQ;AAAA,IACnB;AAAA,IACA;AAAA,MACE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,gDAAgD;AAAA,MACtF,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,8BAA8B;AAAA,MACpE,MAAM,EAAE,KAAK,CAAC,OAAO,QAAQ,KAAK,CAAC,EAAE,SAAS,EAAE,SAAS,4EAA4E;AAAA,MACrI,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,mDAAmD;AAAA,IAChG;AAAA,IACA,EAAE,cAAc,KAAK;AAAA,EACvB;AACF;;;AHtnBA,IAAI,QAAQ,KAAK,CAAC,MAAM,QAAQ;AAC9B,QAAM,EAAE,SAAAC,SAAQ,IAAI,MAAM;AAC1B,EAAAA,SAAQ;AACR,UAAQ,KAAK,CAAC;AAChB;AAEA,IAAM,SAAS,cAAc;AAC7B,IAAM,SAAS,IAAI,iBAAiB,OAAO,QAAQ,OAAO,OAAO,OAAO,SAAS;AAEjF,IAAM,SAAS,IAAI;AAAA,EACjB,EAAE,MAAM,eAAe,SAAS,QAAQ;AAAA,EACxC,EAAE,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,qFA6DmE;AACrF;AAGA,iBAAiB,QAAQ,MAAM;AAI/B,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA,EAAE,aAAa,6JAA6J;AAAA,EAC5K,YAAY;AACV,QAAI;AACF,YAAM,CAAC,UAAU,UAAU,KAAK,IAAI,MAAM,QAAQ,IAAI;AAAA,QACpD,OAAO,YAAY,sBAAsB;AAAA,QACzC,OAAO,YAAY,sBAAsB,EAAE,MAAM,MAAM,IAAI;AAAA,QAC3D,OAAO,YAAY,sBAAsB,EAAE,MAAM,MAAM,IAAI;AAAA,MAC7D,CAAC;AACD,YAAM,WAAW;AAAA,QACf;AAAA,QACA;AAAA,QACA,iBAAiB;AAAA,QACjB,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACtC;AACA,aAAO,EAAE,UAAU,CAAC,EAAE,KAAK,sBAAsB,UAAU,oBAAoB,MAAM,KAAK,UAAU,UAAU,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,IAC5H,SAAS,KAAK;AACZ,aAAO,EAAE,UAAU,CAAC,EAAE,KAAK,sBAAsB,UAAU,cAAc,MAAM,oCAAoC,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE;AAAA,IAC3K;AAAA,EACF;AACF;AAEA,OAAO;AAAA,EACL;AAAA,EACA;AAAA,EACA,EAAE,aAAa,2KAA2K;AAAA,EAC1L,YAAY;AAGV,UAAM,SAAS,MAAM,OAAO,mBAAmB;AAC/C,QAAI,UAAU,OAAO,WAAW,UAAU;AACxC,aAAO,EAAE,UAAU,CAAC,EAAE,KAAK,8BAA8B,UAAU,oBAAoB,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,IAClI;AACA,QAAI;AACF,YAAM,OAAO,MAAM,OAAO,YAAY,eAAe;AACrD,YAAM,WAAW,OAAO,KAAK,KAAK,aAAa,CAAC,CAAC;AACjD,YAAM,UAAU,SAAS,SAAS,iBAAiB,KAAK,SAAS,SAAS,oBAAoB;AAE9F,YAAM,cAAc;AAAA,QAClB,yBAAyB;AAAA,UACvB,UAAU;AAAA,UACV,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,aAAa;AAAA,UACb,eAAe;AAAA,QACjB;AAAA,QACA,aAAa;AAAA,UACX,MAAM;AAAA,UACN,aAAa,UACT,yEACA;AAAA,UACJ,oBAAoB;AAAA,UACpB,sBAAsB;AAAA,QACxB;AAAA,QACA,WAAW;AAAA,UACT,YAAY;AAAA,UACZ,SAAU,KAAK,WAAmB,UAAU,SAAS;AAAA,QACvD;AAAA,QACA,MAAM;AAAA,UACJ,SAAS;AAAA,UACT,eAAe;AAAA,UACf,UAAU;AAAA,UACV,eAAe;AAAA,UACf,aAAa;AAAA,UACb,QAAQ;AAAA,UACR,eAAe;AAAA,UACf,cAAc;AAAA,UACd,WAAW;AAAA,UACX,UAAU;AAAA,UACV,cAAc;AAAA,UACd,iBAAiB;AAAA,UACjB,cAAc;AAAA,UACd,uBAAuB;AAAA,UACvB,mBAAmB;AAAA,UACnB,cAAc;AAAA,UACd,wBAAwB;AAAA,QAC1B;AAAA,QACA,gBAAgB;AAAA,UACd,WAAW;AAAA,UACX,cAAc;AAAA,UACd,aAAa;AAAA,QACf;AAAA,QACA,WAAW;AAAA,UACT,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,UAAU;AAAA,UACV,aAAa;AAAA,QACf;AAAA,QACA,SAAS;AAAA,UACP,UAAU;AAAA,UACV,YAAY;AAAA,UACZ,OAAO;AAAA,YACL;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA,cAAc;AAAA,UACd,gBAAgB;AAAA,YACd;AAAA,YACA;AAAA,YACA;AAAA,YACA;AAAA,UACF;AAAA,UACA,kBAAkB;AAAA,QACpB;AAAA,QACA,eAAe;AAAA,UACb;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,UAAU;AAAA,UACR,UAAU;AAAA,UACV,sBAAsB;AAAA,UACtB,aAAa;AAAA,UACb,mBAAmB;AAAA,UACnB,mBAAmB;AAAA,UACnB,aAAa;AAAA,UACb,cAAc;AAAA,UACd,mBAAmB;AAAA,UACnB,YAAY;AAAA,UACZ,eAAe;AAAA,UACf,qBAAqB;AAAA,QACvB;AAAA,QACA,WAAW;AAAA,UACT,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UACX,UAAU;AAAA,UACV,WAAW;AAAA,UACX,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,UACpB,kBAAkB;AAAA,QACpB;AAAA,QACA,aAAa;AAAA,UACX;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,QACF;AAAA,QACA,eAAe;AAAA,UACb,UAAU;AAAA,UACV,cAAc;AAAA,UACd,WAAW;AAAA,QACb;AAAA,QACA,aAAa;AAAA,UACX,UAAU;AAAA,UACV,OAAO;AAAA,UACP,QAAQ;AAAA,UACR,QAAQ;AAAA,UACR,UAAU;AAAA,QACZ;AAAA,QACA,cAAa,oBAAI,KAAK,GAAE,YAAY;AAAA,MACtC;AACA,aAAO,EAAE,UAAU,CAAC,EAAE,KAAK,8BAA8B,UAAU,oBAAoB,MAAM,KAAK,UAAU,aAAa,MAAM,CAAC,EAAE,CAAC,EAAE;AAAA,IACvI,SAAS,KAAK;AACZ,aAAO,EAAE,UAAU,CAAC,EAAE,KAAK,8BAA8B,UAAU,cAAc,MAAM,+BAA+B,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG,CAAC,GAAG,CAAC,EAAE;AAAA,IAC9K;AAAA,EACF;AACF;AAGA,IAAM,YAAY,IAAI,qBAAqB;AAC3C,MAAM,OAAO,QAAQ,SAAS;","names":["readFileSync","resolve","config","tool","server","client","runInit"]}
1
+ {"version":3,"sources":["../src/token-store.ts","../src/init.ts","../src/agent-rules.ts","../src/init-agents.ts","../src/index.ts","../src/config.ts","../src/api-client.ts","../src/tools/index.ts","../src/tools/agent-tools.ts"],"sourcesContent":["/**\n * Where `init` keeps the pat_ token: a file in the user's home folder, never in the repo.\n *\n * The IDE configs init writes (.mcp.json, .cursor/, .vscode/, .windsurf/) are meant to be\n * committed and shared with the team, so a token written into them reaches everyone who can\n * read the repo, and a pat_ does not expire. The configs name the API URL and project only;\n * the server finds the token here by the API's origin and the project id, so a config that\n * points at another host never receives it.\n */\nimport { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'fs';\nimport { homedir } from 'os';\nimport { join } from 'path';\n\ninterface TokenFile {\n version: 1;\n tokens: Record<string, string>;\n}\n\nexport function defaultTokenStoreDir(): string {\n return join(homedir(), '.validateqa');\n}\n\nexport function tokenStorePath(dir = defaultTokenStoreDir()): string {\n return join(dir, 'credentials.json');\n}\n\nfunction storeKey(apiUrl: string, projectId: string): string | null {\n try {\n return `${new URL(apiUrl).origin} ${projectId}`;\n } catch {\n return null;\n }\n}\n\nfunction readTokenFile(dir: string): TokenFile {\n try {\n const parsed = JSON.parse(readFileSync(tokenStorePath(dir), 'utf-8'));\n if (parsed && typeof parsed.tokens === 'object' && parsed.tokens !== null) {\n return { version: 1, tokens: parsed.tokens as Record<string, string> };\n }\n } catch {\n // Missing or unreadable: no stored tokens.\n }\n return { version: 1, tokens: {} };\n}\n\n/** The token saved for this API origin and project, if any. */\nexport function readStoredToken(apiUrl: string, projectId: string, dir = defaultTokenStoreDir()): string | undefined {\n const key = storeKey(apiUrl, projectId);\n if (!key) return undefined;\n const token = readTokenFile(dir).tokens[key];\n return typeof token === 'string' && token ? token : undefined;\n}\n\n/** Saves the token for this API origin and project, readable by the user only. Throws when it cannot. */\nexport function saveToken(apiUrl: string, projectId: string, token: string, dir = defaultTokenStoreDir()): string {\n const key = storeKey(apiUrl, projectId);\n if (!key) throw new Error(`Invalid API URL: ${apiUrl}`);\n mkdirSync(dir, { recursive: true, mode: 0o700 });\n const file = readTokenFile(dir);\n file.tokens[key] = token;\n const path = tokenStorePath(dir);\n // Write a private temp file and rename it over the old one, so the file is never\n // briefly readable by others nor left half-written.\n const tmp = `${path}.${process.pid}.tmp`;\n writeFileSync(tmp, JSON.stringify(file, null, 2) + '\\n', { encoding: 'utf-8', mode: 0o600 });\n renameSync(tmp, path);\n // A file made by an older version may have wider permissions (no effect on Windows).\n chmodSync(path, 0o600);\n return path;\n}\n","/**\n * `npx @validate.qa/mcp init` - one-command MCP setup for all supported IDEs.\n *\n * Creates/updates MCP config files so the user never has to manually edit JSON.\n * Detects which IDEs are present and writes the appropriate config for each.\n */\nimport { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';\nimport { resolve, join } from 'path';\nimport { defaultTokenStoreDir, saveToken } from './token-store.js';\n\nexport interface InitArgs {\n apiUrl: string;\n token: string;\n projectId: string;\n}\n\nexport function parseInitArgs(argv: string[]): InitArgs {\n const args: Record<string, string> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith('--') && i + 1 < argv.length) {\n args[arg.slice(2)] = argv[++i];\n }\n }\n\n const apiUrl = args['api-url'] ?? process.env.VALIDATEQA_API_URL ?? 'https://shared.validate.qa';\n const token = args['token'] ?? process.env.VALIDATEQA_TOKEN;\n const projectId = args['project-id'] ?? process.env.VALIDATEQA_PROJECT_ID;\n\n if (!token) {\n console.error('❌ Missing --token. Copy the full command from your Validate.QA dashboard.');\n process.exit(1);\n }\n if (!projectId) {\n console.error('❌ Missing --project-id. Copy the full command from your Validate.QA dashboard.');\n process.exit(1);\n }\n\n return { apiUrl: apiUrl.replace(/\\/$/, ''), token, projectId };\n}\n\n/**\n * Build the MCP server entry for config files. The token is left out: these files are meant to\n * be committed, and the server reads the token from the user's own token store (token-store.ts).\n * Only when that store cannot be written does the entry carry the token itself.\n */\nexport function buildMcpEntry(config: InitArgs, opts: { withToken: boolean }) {\n return {\n command: 'npx',\n args: [\n '-y', '@validate.qa/mcp@latest',\n '--api-url', config.apiUrl,\n ...(opts.withToken ? ['--token', config.token] : []),\n '--project-id', config.projectId,\n ],\n };\n}\n\ninterface IDETarget {\n name: string;\n configDir: string; // relative to cwd\n configFile: string; // filename inside configDir\n /** Key in the JSON where MCP servers live */\n serversKey: string;\n}\n\nconst IDE_TARGETS: IDETarget[] = [\n { name: 'Claude Code', configDir: '.', configFile: '.mcp.json', serversKey: 'mcpServers' },\n { name: 'Cursor', configDir: '.cursor', configFile: 'mcp.json', serversKey: 'mcpServers' },\n { name: 'Windsurf', configDir: '.windsurf', configFile: 'mcp.json', serversKey: 'mcpServers' },\n { name: 'VS Code', configDir: '.vscode', configFile: 'mcp.json', serversKey: 'servers' },\n];\n\nfunction writeConfigForIDE(cwd: string, target: IDETarget, entry: object): 'created' | 'updated' | 'skipped' {\n const dir = resolve(cwd, target.configDir);\n const filePath = resolve(dir, target.configFile);\n\n // For .cursor and .windsurf, only write if the directory already exists (IDE is in use)\n // For Claude Code (.mcp.json in root), always write\n if (target.configDir !== '.' && !existsSync(dir)) {\n return 'skipped';\n }\n\n let existing: Record<string, unknown> = {};\n if (existsSync(filePath)) {\n try {\n existing = JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n // Corrupted file - overwrite\n }\n }\n\n const servers = (existing[target.serversKey] ?? {}) as Record<string, unknown>;\n const hadEntry = 'validate.qa' in servers;\n servers['validate.qa'] = entry;\n existing[target.serversKey] = servers;\n\n // Ensure directory exists (for .cursor/mcp.json on first setup)\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(filePath, JSON.stringify(existing, null, 2) + '\\n', 'utf-8');\n return hadEntry ? 'updated' : 'created';\n}\n\n/** Keeps a config file that holds the token out of git: adds it to the repo's .gitignore. */\nfunction ignoreInGit(cwd: string, file: string): void {\n const gitignore = resolve(cwd, '.gitignore');\n const line = '/' + file.replace(/\\\\/g, '/').replace(/^\\.\\//, '');\n const current = existsSync(gitignore) ? readFileSync(gitignore, 'utf-8') : '';\n if (current.split(/\\r?\\n/).includes(line)) return;\n appendFileSync(gitignore, `${current && !current.endsWith('\\n') ? '\\n' : ''}${line}\\n`, 'utf-8');\n}\n\nexport interface InitResult {\n /** Config files written, relative to cwd, with how. */\n written: Array<{ name: string; file: string; result: 'created' | 'updated' }>;\n /** Where the token was saved; null when it could not be and the configs carry it instead. */\n tokenFile: string | null;\n tokenStoreError?: string;\n}\n\n/** Writes the token store and the IDE configs. Separate from runInit so tests can point it at temp folders. */\nexport function initProject(cwd: string, config: InitArgs, storeDir = defaultTokenStoreDir()): InitResult {\n let tokenFile: string | null = null;\n let tokenStoreError: string | undefined;\n try {\n tokenFile = saveToken(config.apiUrl, config.projectId, config.token, storeDir);\n } catch (err) {\n tokenStoreError = err instanceof Error ? err.message : String(err);\n }\n const entry = buildMcpEntry(config, { withToken: tokenFile === null });\n\n const written: InitResult['written'] = [];\n for (const target of IDE_TARGETS) {\n const result = writeConfigForIDE(cwd, target, entry);\n if (result === 'skipped') continue;\n written.push({ name: target.name, file: join(target.configDir, target.configFile), result });\n }\n if (written.length === 0) {\n // No IDE dirs found - create Claude Code config as default\n const target = IDE_TARGETS[0];\n const result = writeConfigForIDE(cwd, target, entry);\n if (result !== 'skipped') written.push({ name: target.name, file: target.configFile, result });\n }\n if (tokenFile === null) {\n for (const { file } of written) ignoreInGit(cwd, file);\n }\n return { written, tokenFile, ...(tokenStoreError ? { tokenStoreError } : {}) };\n}\n\nexport function runInit() {\n const config = parseInitArgs(process.argv.slice(3)); // skip: node, script, 'init'\n\n console.log('');\n console.log(' Validate.QA - MCP Setup');\n console.log(' ─────────────────────────');\n console.log('');\n\n const { written, tokenFile, tokenStoreError } = initProject(process.cwd(), config);\n for (const { name, file, result } of written) {\n console.log(` ✓ ${name} - ${result} ${file}`);\n }\n console.log('');\n if (tokenFile) {\n console.log(` Your token is saved in ${tokenFile} (only you can read it), not in these files,`);\n console.log(' so they are safe to commit. Teammates run their own init command.');\n } else {\n console.log(` ⚠ Could not save your token outside the repo (${tokenStoreError}).`);\n console.log(' The config files above hold it, so they were added to .gitignore. Do not commit them.');\n }\n\n console.log('');\n console.log(' Next: restart your IDE or reload MCP servers.');\n console.log(' Then ask your AI: \"list my tests\" to verify it works.');\n console.log('');\n}\n","export const AGENT_RULES = `## Validate.QA Rules\n\nUse the \\`validate.qa\\` MCP server alongside Playwright so explored flows and saved regression tests run and stay verified in Validate.QA.\n\n### Loop Workflow\n1. Call \\`start_here\\` first (\\`start_here({ feature })\\`, or \\`start_here({})\\` to list features) to read the feature's pages, existing tests, \\`knownSelectors\\`, credential readiness, and \\`openBugs\\`. Skip bugs already listed in \\`openBugs\\`.\n2. Save each bug or regression test with \\`save_regression_test\\` using Playwright code that asserts the **correct** behaviour (fails while the bug exists, passes once fixed). \\`save_regression_test\\` returns immediately while the run executes in the cloud — keep exploring without waiting.\n3. Call \\`collect_verdicts\\` at the end of the session. \\`reproduces\\` (the test fails on its check) means the bug is locked in: keep that test as it is. \\`passes\\` means the test misses the bug: check again in your browser and fix its assertion. \\`test_problem\\` means it broke before its check: fix the steps and keep the expected value. Fix with \\`edit_test_code\\`, then call \\`collect_verdicts\\` again.\n\n### Test Code Rules\n- Never weaken an assertion to make a test pass; always assert the exact correct behaviour.\n- One deterministic \\`expect()\\` per outcome: cover one bug or outcome per test and end with a deterministic \\`expect()\\` on the exact value or state the app should produce.\n- Relative URLs only (\\`await page.goto('/orders')\\`, path-only URL checks) — never hardcode a host or scheme; the runner sets \\`baseURL\\`.\n- Read credentials from \\`process.env\\` (\\`process.env.TEST_USER_EMAIL!\\` and \\`process.env.TEST_USER_PASSWORD!\\` with non-null assertions) — never hardcode literals or add \\`??\\` / \\`||\\` fallbacks.\n- Prefer verified locators from \\`knownSelectors\\`, \\`getByRole(role, { name, exact: true })\\`, \\`getByLabel\\`, and \\`getByTestId\\`; never use \\`waitForLoadState('networkidle')\\`.\n\n### Healer\n- A heal may change locators and waits, but never removes or loosens an \\`expect()\\`.\n- If the app is wrong, do not alter the expected value or silence the test with \\`test.fixme()\\` — flag it as a bug with \\`flag_as_bug\\` instead.`;\n","import { spawn } from 'node:child_process';\nimport { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';\nimport { dirname, join, resolve } from 'node:path';\nimport { AGENT_RULES } from './agent-rules.js';\n\nexport const VALID_LOOPS = ['claude', 'vscode', 'opencode'] as const;\nexport type AgentLoop = (typeof VALID_LOOPS)[number];\n\nexport const RULES_START_MARKER = '<!-- validate.qa:start -->';\nexport const RULES_END_MARKER = '<!-- validate.qa:end -->';\n\nconst AGENT_FILE_RE = /(?:^|[^a-z])(planner|generator|healer)(?:[^a-z]|$)/i;\n\nexport type PlaywrightInitRunner = (opts: { dir: string; loop: AgentLoop }) => Promise<void> | void;\n\nexport interface InitAgentsOptions {\n dir?: string;\n loop?: string;\n runPlaywright?: PlaywrightInitRunner;\n log?: (line: string) => void;\n}\n\nexport interface InitAgentsResult {\n loop: AgentLoop;\n dir: string;\n agentFiles: string[];\n mcpConfigFile: string;\n mcpEntryStatus: 'added' | 'preserved';\n}\n\nexport function validateLoop(loop: string): AgentLoop {\n if ((VALID_LOOPS as readonly string[]).includes(loop)) {\n return loop as AgentLoop;\n }\n throw new Error(`Unknown --loop \"${loop}\". Expected one of: ${VALID_LOOPS.join(', ')}.`);\n}\n\nexport function parseInitAgentsArgs(argv: string[], cwd = process.cwd()): { loop: AgentLoop; dir: string } {\n let rawLoop: string | undefined;\n let rawDir: string | undefined;\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg === '--loop') {\n const next = argv[i + 1];\n if (next === undefined || next.startsWith('--')) {\n throw new Error(`Missing value for --loop. Expected one of: ${VALID_LOOPS.join(', ')}.`);\n }\n rawLoop = next;\n i++;\n } else if (arg.startsWith('--loop=')) {\n rawLoop = arg.slice('--loop='.length);\n } else if (arg === '--dir') {\n const next = argv[i + 1];\n if (next === undefined || next.startsWith('--')) {\n throw new Error('Missing value for --dir.');\n }\n rawDir = next;\n i++;\n } else if (arg.startsWith('--dir=')) {\n rawDir = arg.slice('--dir='.length);\n if (!rawDir) throw new Error('Missing value for --dir.');\n } else {\n throw new Error(`Unknown argument \"${arg}\". Usage: init-agents [--loop claude|vscode|opencode] [--dir <repo>]`);\n }\n }\n\n return {\n loop: validateLoop(rawLoop ?? 'claude'),\n dir: rawDir ? resolve(cwd, rawDir) : resolve(cwd),\n };\n}\n\nexport function spawnPlaywrightInitAgents({ dir, loop }: { dir: string; loop: AgentLoop }): Promise<void> {\n const command = process.platform === 'win32' ? 'npx.cmd' : 'npx';\n const args = ['playwright', 'init-agents', `--loop=${loop}`];\n\n return new Promise((resolvePromise, rejectPromise) => {\n // Node refuses to spawn a .cmd file without a shell on Windows (EINVAL); the args are fixed strings, so the shell is safe.\n const child = spawn(command, args, { cwd: dir, stdio: 'inherit', shell: process.platform === 'win32' });\n child.on('error', (err) => {\n rejectPromise(new Error(`Failed to run npx playwright init-agents --loop=${loop}: ${err.message}`));\n });\n child.on('close', (code, signal) => {\n if (code === 0) {\n resolvePromise();\n } else {\n const detail = code !== null ? `exit code ${code}` : `signal ${signal}`;\n rejectPromise(new Error(`npx playwright init-agents --loop=${loop} failed (${detail}).`));\n }\n });\n });\n}\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function applyRulesSection(content: string, rules = AGENT_RULES): string {\n const block = `${RULES_START_MARKER}\\n${rules.trim()}\\n${RULES_END_MARKER}`;\n const delimitedPattern = new RegExp(\n `${escapeRegExp(RULES_START_MARKER)}[\\\\s\\\\S]*?${escapeRegExp(RULES_END_MARKER)}`,\n 'g',\n );\n\n if (delimitedPattern.test(content)) {\n let replacedFirst = false;\n const updated = content.replace(delimitedPattern, () => {\n if (replacedFirst) return '';\n replacedFirst = true;\n return block;\n });\n return `${updated.replace(/\\s+$/, '')}\\n`;\n }\n\n if (content.includes(RULES_START_MARKER)) {\n const start = content.indexOf(RULES_START_MARKER);\n const before = content.slice(0, start).replace(/\\s+$/, '');\n return before ? `${before}\\n\\n${block}\\n` : `${block}\\n`;\n }\n\n const trimmed = content.replace(/\\s+$/, '');\n return trimmed ? `${trimmed}\\n\\n${block}\\n` : `${block}\\n`;\n}\n\nfunction candidateAgentDirs(loop: AgentLoop): string[] {\n switch (loop) {\n case 'claude':\n return ['.claude/agents'];\n case 'vscode':\n return ['.github/agents', '.github/chatmodes'];\n case 'opencode':\n return ['.opencode/prompts', '.opencode/agents'];\n }\n}\n\nfunction findAgentFiles(dir: string, loop: AgentLoop): string[] {\n for (const relDir of candidateAgentDirs(loop)) {\n const fullDir = resolve(dir, relDir);\n if (!existsSync(fullDir) || !statSync(fullDir).isDirectory()) continue;\n const matches = readdirSync(fullDir)\n .filter((entry) => entry.endsWith('.md') && AGENT_FILE_RE.test(entry))\n .sort()\n .map((entry) => join(fullDir, entry));\n if (matches.length > 0) return matches;\n }\n return [];\n}\n\ninterface LoopMcpTarget {\n relativePath: string;\n serversKey: 'mcpServers' | 'servers' | 'mcp';\n entry: Record<string, unknown>;\n}\n\nfunction mcpTargetForLoop(loop: AgentLoop): LoopMcpTarget {\n switch (loop) {\n case 'claude':\n return {\n relativePath: '.mcp.json',\n serversKey: 'mcpServers',\n entry: {\n command: 'npx',\n args: ['-y', '@validate.qa/mcp'],\n env: { VALIDATEQA_TOOLSET: 'agent' },\n },\n };\n case 'vscode':\n return {\n relativePath: join('.vscode', 'mcp.json'),\n serversKey: 'servers',\n entry: {\n type: 'stdio',\n command: 'npx',\n args: ['-y', '@validate.qa/mcp'],\n env: { VALIDATEQA_TOOLSET: 'agent' },\n },\n };\n case 'opencode':\n return {\n relativePath: 'opencode.json',\n serversKey: 'mcp',\n entry: {\n type: 'local',\n command: ['npx', '-y', '@validate.qa/mcp'],\n environment: { VALIDATEQA_TOOLSET: 'agent' },\n enabled: true,\n },\n };\n }\n}\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\n// A config file that exists but is not a plain JSON object (JSONC comments, a trailing comma) is refused, never\n// overwritten: rewriting it would drop the user's other servers.\nfunction readJsonObject(filePath: string): Record<string, unknown> {\n if (!existsSync(filePath)) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n parsed = undefined;\n }\n if (!isPlainObject(parsed)) {\n throw new Error(\n `${filePath} is not a plain JSON object (comments and trailing commas are not supported), so init-agents left it untouched. Make it plain JSON, or move it aside, and run init-agents again.`,\n );\n }\n return parsed;\n}\n\nexport async function initAgents(options: InitAgentsOptions = {}): Promise<InitAgentsResult> {\n const loop = validateLoop(options.loop ?? 'claude');\n const dir = options.dir ? resolve(options.dir) : resolve(process.cwd());\n const runPlaywright = options.runPlaywright ?? spawnPlaywrightInitAgents;\n const log = options.log ?? console.log;\n\n if (!existsSync(dir) || !statSync(dir).isDirectory()) {\n throw new Error(`Target directory does not exist: ${dir}`);\n }\n\n const mcpTarget = mcpTargetForLoop(loop);\n const mcpConfigFile = join(dir, mcpTarget.relativePath);\n\n // Snapshot existing MCP config first because Playwright's claude/opencode generators overwrite their config file.\n const beforeDoc = readJsonObject(mcpConfigFile);\n const beforeServers = isPlainObject(beforeDoc[mcpTarget.serversKey])\n ? { ...(beforeDoc[mcpTarget.serversKey] as Record<string, unknown>) }\n : {};\n\n await runPlaywright({ dir, loop });\n\n const agentFiles = findAgentFiles(dir, loop);\n if (agentFiles.length === 0) {\n throw new Error(`No Playwright agent files (planner, generator, healer) found in ${dir} for loop \"${loop}\".`);\n }\n\n for (const filePath of agentFiles) {\n const current = readFileSync(filePath, 'utf-8');\n writeFileSync(filePath, applyRulesSection(current, AGENT_RULES), 'utf-8');\n }\n\n const afterDoc = readJsonObject(mcpConfigFile);\n const afterServers = isPlainObject(afterDoc[mcpTarget.serversKey])\n ? (afterDoc[mcpTarget.serversKey] as Record<string, unknown>)\n : {};\n\n const mergedServers: Record<string, unknown> = { ...beforeServers, ...afterServers };\n if (Object.prototype.hasOwnProperty.call(beforeServers, 'validate.qa')) {\n mergedServers['validate.qa'] = beforeServers['validate.qa'];\n }\n\n const hadValidateQa = Object.prototype.hasOwnProperty.call(mergedServers, 'validate.qa');\n if (!hadValidateQa) {\n mergedServers['validate.qa'] = mcpTarget.entry;\n }\n\n const mergedDoc: Record<string, unknown> = {\n ...beforeDoc,\n ...afterDoc,\n [mcpTarget.serversKey]: mergedServers,\n };\n\n mkdirSync(dirname(mcpConfigFile), { recursive: true });\n writeFileSync(mcpConfigFile, `${JSON.stringify(mergedDoc, null, 2)}\\n`, 'utf-8');\n\n log('No token was written. The server reads its project from .validateqa.json and its token from your token store: run the init command from your Validate.QA dashboard once, or set VALIDATEQA_TOKEN, VALIDATEQA_PROJECT_ID and VALIDATEQA_API_URL.');\n\n return {\n loop,\n dir,\n agentFiles,\n mcpConfigFile,\n mcpEntryStatus: hadValidateQa ? 'preserved' : 'added',\n };\n}\n\nexport async function runInitAgents(argv = process.argv.slice(3)): Promise<void> {\n try {\n const parsed = parseInitAgentsArgs(argv);\n await initAgents(parsed);\n } catch (err) {\n console.error(err instanceof Error ? err.message : String(err));\n process.exit(1);\n }\n}\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { resolveConfig } from './config.js';\nimport { ValidateQAClient } from './api-client.js';\nimport { registerAllTools } from './tools/index.js';\nimport { AGENT_INSTRUCTIONS } from './tools/agent-tools.js';\n\n// `npx @validate.qa/mcp init ...` - auto-create IDE config files\nif (process.argv[2] === 'init') {\n const { runInit } = await import('./init.js');\n runInit();\n process.exit(0);\n}\n\n// `npx @validate.qa/mcp init-agents ...` - layer Validate.QA onto Playwright test agents\nif (process.argv[2] === 'init-agents') {\n const { runInitAgents } = await import('./init-agents.js');\n await runInitAgents();\n process.exit(0);\n}\n\nconst config = resolveConfig();\nconst client = new ValidateQAClient(config.apiUrl, config.token, config.projectId);\n\nconst server = new McpServer(\n { name: 'validate.qa', version: '1.2.0' },\n { instructions: config.toolset === 'agent' ? AGENT_INSTRUCTIONS : `Validate.QA - AI-powered QA platform: generates, runs, and heals Playwright E2E tests.\n\n## Capabilities\n- Setup projects via setup_project (read source code first)\n- Write tests via create_test (read routes, components, API handlers first)\n- Manage tests: list, view, edit, run, delete (DRAFT/ACTIVE/DISABLED/QUARANTINED)\n- Heal failing tests: AI replays in browser, fixes code or flags app bugs\n- Schedule recurring runs (hourly-weekly) with auto-heal\n\n## Workflows\n1. New project: READ source, setup_project (features, baseUrl, testCredentials, appBrief). Per feature: READ code, create_test, add_api_endpoints.\n2. Check health: list_features (failingTests > 0), get_test_failures per feature, edit_test_code or heal_test.\n3. Add coverage: READ code, create_feature, create_test x N (suite = feature name), add_api_endpoints.\n4. Nightly automation: create_schedule (daily, autoHeal: true).\n5. After code change: batch_run_tests (waitSeconds: 120, or then wait_for_batch), then list_features + get_test_failures.\n6. Find and lock in bugs in a feature: start_here({ feature }), explore with your browser, save_regression_test for each bug (returns at once, keep exploring), then collect_verdicts.\n\n## Automatic lifecycle\nDo NOT call run_test after create_test (auto-queued) or heal_test after failures (auto-heals, unless toggle_auto_heal turned it off).\nDo NOT poll get_test_run in a loop. To wait for a run, call wait_for_run(runId) (or wait_for_batch(batchId)); it blocks until the run finishes or timeoutSeconds passes (max 120) and returns timedOut: true if it is still going - then call it again. run_test and batch_run_tests also take waitSeconds to queue and wait in one call. A FAILED run comes back with its error, consoleLog and failedSteps.\nLifecycle: create > auto-run > pass=ACTIVE, fail=auto-heal > heal fail=@needs-review. With toggle_auto_heal(false), no failure is auto-healed; call heal_test yourself when you want one.\nDO call add_api_endpoints per feature (not automatic).\nDO call run_security_audit after UI tests to analyze captured API traffic. If it errors about missing data, tell the developer to click \"Security Audit\" in the Dashboard to run a live browser capture first. Use list_security_findings to review results.\n\n## Rate limits\n50 calls/60s. Reads parallel OK, writes sequential. On 429, wait 30-60s.\n\n## Triaging failures\n- App broken: flag_as_bug(testId, description). Selector/timing: heal_test. Bad test logic: edit_test_code.\n- Unsure: heal_test first - it detects app bugs automatically.\n\n## Using the codebase\nYou have full source code access. Always READ before writing: routes for URLs, components for selectors, API handlers for endpoints.\n\n## Before writing any test - preflight check\n\nRead project://test-conventions first. Check the variables section:\n- BASE_URL must be set (the runner needs it). If missing, call setup_project or set_variable.\n- TEST_USER_EMAIL and TEST_USER_PASSWORD must be set if tests require login. If missing, ask the developer for credentials and call set_variable.\n- If starting fresh (no features, no variables), use workflow #1 (setup_project) before anything else.\n\n## Critical rules - see project://test-conventions for full details and code templates\n- URLs: relative only, never hardcoded domains - runner sets baseURL\n- Credentials: process.env.TEST_USER_EMAIL! (non-null assertion) - never ?? or || fallbacks\n- Registration: testuser-\\${Date.now()}@example.test when the sign-up sends no email; if it emails a code or link, register with process.env.VQA_TEST_INBOX_EMAIL! / process.env.VQA_TEST_ACCOUNT_PASSWORD! and read it with await __vqaWaitForEmail('code' | 'link'), first asserting the app's 'email sent' state (code field visible / 'check your email') before each call (runner-injected; see test-conventions)\n- Suite name must match feature name exactly (case-sensitive)\n- @auth must NOT combine with @smoke/@regression/@needs-auth/@critical\n- Each test = own browser process; @needs-auth tests MUST login inline\n- API tests: test.describe.serial() with __QA_HTTP__ log after every request\n- UI tests: console.log() milestones after key actions\n- Selectors: getByRole(role, { name, exact: true }), getByLabel(label, { exact: true }), getByText(text, { exact: true }); CSS ids only for nodes with no accessible name\n- URL assertions: path only - await expect(page).toHaveURL(/\\\\/dashboard$/) - never a host\n- Production BASE_URL: read-only tests get the read-only guard (page.route aborts auth/mutation requests and fails the test) - template in project://test-conventions\n- Every assertion must be able to fail: no toHaveCount(0) on transient text, no toBeHidden on an element never shown, full message text instead of a toContainText fragment\n- create_test/edit_test_code validate code - fix warnings before moving on\n\n## Setting up coverage\n1. SCAN: Read router, nav, pages, API handlers - map ALL routes and features.\n2. SETUP: setup_project with features, pages, baseUrl, testCredentials, appBrief.\n3. WRITE: One feature at a time. For EACH test, read the SPECIFIC source code first (see codeReadingRequirements in test-conventions). For login blocks: read the login component. For UI tests: read the page component. For API tests: read the route handler. Templates are structure guides - selectors MUST come from the real code. Use create_test for BOTH UI and API tests. Then call add_api_endpoints.\n4. VERIFY: get_spec_coverage(featureName, specItems) - write tests for every uncovered item - then get_coverage_summary + list_features for failures + get_test_failures.\n\nTest real user flows (forms, validation, errors). \"Page loads\" tests are worthless.` },\n);\n\n// Register all QA platform tools\nregisterAllTools(server, client, config.toolset);\n\n// ── MCP Resources (live project context for AI assistants) ────────────\n\nserver.resource(\n 'Project Overview',\n 'project://overview',\n { description: 'Live project state: test counts, pass rates, runner status, features, coverage gaps, and configuration. Read this first to understand the current project.' },\n async () => {\n try {\n const [overview, coverage, stats] = await Promise.all([\n client.executeTool('get_project_overview'),\n client.executeTool('get_coverage_summary').catch(() => null),\n client.executeTool('get_regression_stats').catch(() => null),\n ]);\n const snapshot = {\n overview,\n coverage,\n regressionStats: stats,\n retrievedAt: new Date().toISOString(),\n };\n return { contents: [{ uri: 'project://overview', mimeType: 'application/json', text: JSON.stringify(snapshot, null, 2) }] };\n } catch (err) {\n return { contents: [{ uri: 'project://overview', mimeType: 'text/plain', text: `Failed to load project overview: ${err instanceof Error ? err.message : String(err)}` }] };\n }\n },\n);\n\nserver.resource(\n 'Test Conventions',\n 'project://test-conventions',\n { description: 'Project-specific test conventions: environment variables, credential patterns, tag meanings, and registration email rules. Read this before writing or editing any test.' },\n async () => {\n // The server builds the conventions (one copy for every transport, updated without an npm\n // release). The copy below is only a fallback for servers that predate that route.\n const served = await client.getTestConventions();\n if (served && typeof served === 'object') {\n return { contents: [{ uri: 'project://test-conventions', mimeType: 'application/json', text: JSON.stringify(served, null, 2) }] };\n }\n try {\n const vars = await client.executeTool('get_variables') as { variables: Record<string, { value: string; secret: boolean }> };\n const varNames = Object.keys(vars.variables || {});\n const hasAuth = varNames.includes('TEST_USER_EMAIL') && varNames.includes('TEST_USER_PASSWORD');\n\n const conventions = {\n codeReadingRequirements: {\n CRITICAL: 'Templates below are STRUCTURE GUIDES - every selector, route, and endpoint MUST come from reading the actual source code. Never copy a template verbatim.',\n forLogin: 'BEFORE writing any @needs-auth login block: READ the login/auth page component. Find: (1) the login route (/login, /auth/login, /signin?), (2) the flow type (single-step or multi-step with Continue/Next button?), (3) each field\\'s label text for getByLabel(label, { exact: true }) - fall back to the placeholder or input[type=password] only when the field has no label, (4) the submit button\\'s accessible name for getByRole(\\'button\\', { name, exact: true }), (5) post-login redirect URL. Every app is different - single-step, multi-step, OAuth buttons, magic links.',\n forUITests: 'BEFORE writing a UI test for a page: READ the page component. Find: headings (text + level), form fields (labels, placeholders, types), buttons (text, role), navigation elements, validation error messages, success/error states. Use the exact text from the component - do not guess.',\n forAPITests: 'BEFORE writing an API test: READ the route handler. Find: the endpoint path, HTTP method, request body schema (what fields, what types), response body shape (is it flat or wrapped in { data: {...} }?), auth requirements (Bearer token? Cookie? API key?), error response shapes.',\n forAPIMapping: 'BEFORE calling add_api_endpoints: READ ALL route handlers for the feature. Register every endpoint with accurate method, path, requestShape, responseShapes, requiresAuth.',\n },\n credentials: {\n rule: 'Never hardcode credentials in test code',\n loginFields: hasAuth\n ? 'Use process.env.TEST_USER_EMAIL! and process.env.TEST_USER_PASSWORD!'\n : 'No credentials configured - set TEST_USER_EMAIL and TEST_USER_PASSWORD via set_variable',\n registrationFields: 'MUST use unique email per run: `testuser-${Date.now()}@example.test` - reusing emails causes \"already registered\" failures. When the sign-up emails a verification code or link, register with `process.env.VQA_TEST_INBOX_EMAIL!` / `process.env.VQA_TEST_ACCOUNT_PASSWORD!` (a fresh real inbox address the runner injects per run) instead',\n emailedCodesAndLinks: \"Read emails at runtime with the runner-injected helpers (never define them): `const code = await __vqaWaitForEmail('code'); await __vqaEnterVerificationCode(page, code);` or `await page.goto(await __vqaWaitForEmail('link'));` for the run's sign-up identity; `await __vqaWaitForEmail('code', { to: process.env.TEST_USER_EMAIL })` for the saved account's sign-in code or magic link (works only when TEST_USER_EMAIL is the project's Validate.QA inbox address). Each call returns an email no earlier call returned. Assert the app's 'email sent' state (code field visible / 'check your email') before each __vqaWaitForEmail call, so a send error fails at once instead of after the inbox wait. Never hardcode or guess a code.\",\n registrationPassword: \"Use 'TestPass123!' for new account passwords (meets typical strength rules)\",\n },\n variables: {\n configured: varNames,\n baseUrl: (vars.variables as any)?.BASE_URL?.value || 'Not set',\n },\n tags: {\n '@auth': 'Tests the login/auth flow itself. MUST NOT combine with @smoke, @regression, @needs-auth, @critical. CAN include @discovered.',\n '@needs-auth': 'Tests for authenticated pages - MUST include inline login block. CAN combine with @smoke, @regression, @critical.',\n '@smoke': 'Quick sanity checks - run on every deploy',\n '@regression': 'Full regression suite tests',\n '@critical': 'Business-critical paths (informational label - no special suite filtering)',\n '@api': 'API-level tests. Runs in its own project, excluded from suite/smoke/regression runs',\n '@discovered': 'Auto-added to tests generated by AI discovery',\n '@generated': 'Auto-added by AI discovery and API test generation',\n '@manual': 'Default tag for tests created via create_test when no tags specified',\n '@flaky': 'Auto-added after repeated failures. Excluded from suite/smoke/regression runs',\n '@bug-found': 'Added when healer detects an app bug. Test is auto-quarantined',\n '@needs-review': 'Added when DRAFT test fails healing. Requires human attention',\n '@heal-skip': 'Added by healer when test is permanently unfixable. Prevents future auto-heal',\n '@rate-limit-blocked': 'Auto-added by pre-heal classifier when test failure was a rate-limit (HTTP 429). Test skips heal until next pass clears the tag. NOT for use by test authors.',\n '@quota-exceeded': 'Auto-added when failure is a quota/daily-limit response. Skips heal; clears on next pass.',\n '@auth-lost': 'Auto-added when a signed-in test (@needs-auth, authed @api, or a stored-login body) fails on a 401/403 or falls back to the login page. Never on @auth / login-form tests. Skips heal; clears after re-auth + pass.',\n '@rate-limit-expected': 'OPT-OUT tag for tests that intentionally exercise rate-limit behavior (e.g., \"POST /api/login 6 times to trigger lockout\"). Add this tag to ANY @api test that asserts rate-limit responses, otherwise the rate-limit detector will silently downgrade your failure to ERROR. Distinct from auto-pushed @rate-limit-blocked.',\n },\n executionModel: {\n isolation: 'Each test runs as its own separate Playwright process in a fresh browser. No shared state, no storageState, no global setup.',\n authRequired: 'Every @needs-auth test MUST include its own login block. Do NOT suggest storageState or performLogin() - those only work in the regression suite.',\n concurrency: 'Tests run in parallel - runner claims up to 10 per poll, launched 3s apart (~30s per batch). Multiple batches can overlap. Use test.setTimeout(120_000) if the target app is slow under concurrent login load.',\n },\n lifecycle: {\n DRAFT: 'New or unverified test - not included in scheduled runs',\n ACTIVE: 'Verified and running - included in batch/suite executions',\n DISABLED: 'Manually paused - excluded from all runs',\n QUARANTINED: 'Auto-quarantined after repeated failures',\n },\n logging: {\n CRITICAL: 'EVERY request.get/post/put/patch/delete call MUST be followed by a __QA_HTTP__ log line. This is how the platform surfaces request/response data in the Network panel. Missing this = invisible API calls.',\n apiPattern: \"const body = await res.json();\\nconsole.log('__QA_HTTP__:' + JSON.stringify({ method: 'POST', url: '/api/path', status: res.status(), requestBody: { field: value, password: '[REDACTED]' }, responseBody: body }));\",\n rules: [\n 'Call res.json() ONCE, store in body variable - reuse for log and assertions (calling twice throws)',\n 'Log AFTER the request, BEFORE assertions',\n 'Redact sensitive fields: password: \"[REDACTED]\", token: \"[REDACTED]\"',\n 'Use the literal URL string (same as the request call), not a variable',\n 'UI tests using page fixture do NOT need __QA_HTTP__ - only request fixture tests',\n ],\n uiMilestones: \"UI tests (page fixture): add plain console.log() at key milestones - e.g. console.log('Navigated to login page') after page.goto(). These appear in the Console Output panel.\",\n uiLoggingRules: [\n 'Log AFTER the action, not before - confirms the action succeeded',\n 'Include dynamic values where useful: console.log(\"Current URL:\", page.url())',\n 'Keep messages short and human-readable - they show verbatim in the UI',\n 'Do NOT log sensitive values (passwords, tokens)',\n ],\n apiTestStructure: 'Use test.describe.serial() for API tests sharing auth. Independent validation tests (401, 404 checks) go in a SEPARATE non-serial describe block - do not mix them with the auth-dependent serial flow.',\n },\n bestPractices: [\n 'Every @needs-auth test must start with a login block (goto login, fill creds, click submit, waitForURL)',\n 'Use { timeout: 30000 } on waitForURL after login - target apps can be slow under concurrent test load',\n 'Keep login blocks minimal - no assertions on the login page (that is what @auth tests are for)',\n 'After login, page.goto() directly to the page being tested - do not navigate via UI clicks',\n 'Selectors: getByRole(role, { name, exact: true }) for buttons/links/headings, getByLabel(label, { exact: true }) for form fields, getByText(text, { exact: true }) for static copy. CSS ids/classes only for nodes with no accessible name',\n 'URL assertions: path only - await expect(page).toHaveURL(/\\\\/dashboard$/) - never a host',\n 'Registration tests must generate unique email with Date.now()',\n 'Group tests into suites matching feature names exactly (case-sensitive)',\n 'Do NOT suggest storageState or performLogin() for create_test - those only work in the regression suite',\n 'EVERY API test (request fixture) MUST include __QA_HTTP__ log after each request call - see logging section above',\n 'Avoid waitForLoadState(\"networkidle\") - apps with SSE, WebSocket, or analytics connections may never reach idle, causing 60s timeouts. Wait for specific elements instead.',\n 'For headings, use exact: true or level: 1 - getByRole(\"heading\", { name: \"X\" }) matches ALL h1-h6 containing \"X\", causing strict mode violations',\n ],\n pitfalls: {\n CRITICAL: 'These are the top causes of test failures. Read before writing ANY test.',\n strictModeViolations: \"Playwright throws when a locator resolves to 2+ elements. Common causes: getByLabel('Password') matches input AND 'Show password' toggle; getByRole('button', { name: 'Continue' }) matches 'Continue' AND 'Continue with GitHub'. Prevention: getByLabel('Password', { exact: true }) (exact keeps it from matching 'Show password'), { exact: true } for ambiguous button text, scope to a parent container (page.locator('form#login').getByLabel('Email', { exact: true })). Use input[type=\\\"password\\\"] only when the field has no label.\",\n networkIdle: \"NEVER use waitForLoadState('networkidle') - waits for 500ms of zero network activity, which is fragile and causes 30s+ timeouts in apps with polling, analytics, or WebSockets. Use waitForURL() or wait for a specific element instead.\",\n headingStrictMode: \"getByRole('heading', { name: 'Dashboard' }) can match multiple headings (h1 + h2 + breadcrumb). Add { level: 1 } or { exact: true } to disambiguate. Same fix for getByLabel('Password') matching the input AND a 'Show password' toggle: getByLabel('Password', { exact: true }).\",\n postLoginRedirect: \"NEVER assert exact URL after login (e.g. expect(page).toHaveURL('/dashboard')) - apps redirect to different routes per user/role. Use: await page.waitForURL(url => !url.pathname.startsWith('/login'), { timeout: 30000 })\",\n resJsonOnce: \"res.json() consumes the response body stream - calling it twice throws. Store result: const body = await res.json(); then reuse body for logging and assertions.\",\n hiddenInputs: \"Many UI frameworks use visually hidden <input> with custom overlays. page.check() fails because the overlay intercepts clicks. Click the <label> or visible wrapper: page.getByText('Remember me').click()\",\n apiResponseShapes: \"API endpoints often wrap data in a property ({ user: {...} }, { data: [...] }). Read the route handler's res.json() call to see the actual shape. If the handler does res.json({ ticket }), then body is { ticket: { id, ... } } - NOT { id, ... }.\",\n invalidIds: \"Not all routes validate ID format before querying the database. If a route passes the raw ID to an ORM query and gets null back, it returns 404 'not found' - not 400 'bad request'. If unsure, expect either: expect([400, 404]).toContain(res.status())\",\n hardcodedUrls: \"NEVER hardcode full URLs in test code. For UI tests: always use relative paths - page.goto('/login'). The runner sets baseURL from the project's BASE_URL variable. For API tests: use relative paths if the API is on the same domain as the frontend (request.post('/api/auth/login')). If the API is on a SEPARATE domain, use process.env.BASE_API_URL! as a test variable (set via set_variable) and construct URLs: request.post(process.env.BASE_API_URL! + '/auth/login'). NEVER hardcode 'https://example.com/...' in test code - use env vars or relative paths.\",\n credentialFallbacks: \"NEVER use process.env.TEST_USER_EMAIL ?? 'fallback' or || 'fallback'. Use ! (non-null assertion). Missing variable = test should fail immediately, not silently use hardcoded value.\",\n },\n templates: {\n needsAuth: \"test('Feature Name - Test Name', {\\n tag: ['@needs-auth', '@smoke'],\\n}, async ({ page }) => {\\n // Login block - ADAPTED from reading the app's login page component\\n // YOU MUST READ the login page component BEFORE writing this block\\n await page.goto('/login'); // read the router for actual login route\\n await page.getByLabel('Email', { exact: true }).fill(process.env.TEST_USER_EMAIL!); // label text from the component\\n await page.getByLabel('Password', { exact: true }).waitFor({ state: 'visible', timeout: 10000 });\\n await page.getByLabel('Password', { exact: true }).fill(process.env.TEST_USER_PASSWORD!); // input[type=\\\"password\\\"] only if the field has no label\\n await page.getByRole('button', { name: 'Sign in', exact: true }).click(); // the button's accessible name\\n await page.waitForURL(url => !url.pathname.startsWith('/login'), { timeout: 30000 });\\n console.log('Logged in, current URL:', page.url());\\n\\n // Actual test\\n await page.goto('/feature-path');\\n console.log('On feature page');\\n await expect(page.getByRole('heading', { name: /feature name/i })).toBeVisible();\\n});\",\n authTest: \"test('Login - Happy Path', {\\n tag: ['@auth'],\\n}, async ({ page }) => {\\n await page.goto('/auth/login');\\n // test the login form itself - selectors from reading the actual component\\n});\",\n apiSerial: \"test.describe.serial('Feature Name', () => {\\n let token: string;\\n test('authenticate', async ({ request }) => {\\n const res = await request.post('/api/auth/login', {\\n data: { email: process.env.TEST_USER_EMAIL!, password: process.env.TEST_USER_PASSWORD! }\\n });\\n const body = await res.json();\\n console.log('__QA_HTTP__:' + JSON.stringify({\\n method: 'POST', url: '/api/auth/login', status: res.status(),\\n requestBody: { email: process.env.TEST_USER_EMAIL!, password: '[REDACTED]' },\\n responseBody: body,\\n }));\\n expect(res.status()).toBe(200);\\n token = body.access_token; // ← adapt to your API (e.g. body.token, body.jwt)\\n });\\n test('get profile', async ({ request }) => {\\n const res = await request.get('/api/users/profile', {\\n headers: { Authorization: `Bearer ${token}` }\\n });\\n const body = await res.json();\\n console.log('__QA_HTTP__:' + JSON.stringify({\\n method: 'GET', url: '/api/users/profile', status: res.status(),\\n responseBody: body,\\n }));\\n expect(res.status()).toBe(200);\\n });\\n});\",\n apiEndpointMapping: '// After reading an Express route handler like:\\n// router.post(\\'/api/auth/login\\', async (req, res) => { ... })\\n// Call add_api_endpoints with:\\n{\\n featureName: \"Authentication\",\\n endpoints: [{\\n method: \"POST\",\\n path: \"/api/auth/login\",\\n requiresAuth: false,\\n requestShape: { \"email\": \"string\", \"password\": \"string\" },\\n responseShapes: { \"200\": { \"token\": \"string\", \"user\": \"object\" }, \"401\": { \"error\": \"string\" } }\\n }]\\n}',\n uiWithMilestones: \"test('Feature - Happy Path', async ({ page }) => {\\n await page.goto('/login');\\n console.log('Navigated to login page');\\n\\n await page.getByLabel('Email', { exact: true }).fill(process.env.TEST_USER_EMAIL!);\\n await page.getByLabel('Password', { exact: true }).fill(process.env.TEST_USER_PASSWORD!);\\n await page.getByRole('button', { name: 'Sign in', exact: true }).click();\\n console.log('Credentials submitted');\\n\\n await page.waitForURL(url => !url.pathname.startsWith('/login'), { timeout: 15000 });\\n console.log('Logged in, current URL:', page.url());\\n\\n await page.goto('/feature-path');\\n console.log('On feature page');\\n});\",\n },\n testQuality: [\n 'Test actual user flows, not just \"page loads\" - a test that only checks goto + heading visible is nearly worthless',\n 'Test form submission: fill fields, submit, verify success/redirect',\n 'Test validation: leave required fields empty, enter invalid data, verify error messages',\n 'Test error states: wrong password, expired session, invalid IDs',\n 'Test the happy path end-to-end: login → navigate → perform action → verify result',\n 'Each test should assert something meaningful about the application behavior',\n ],\n securityAudit: {\n workflow: 'After creating UI tests for a feature, call run_security_audit to analyze captured API traffic for auth issues, data exposure, and vulnerabilities.',\n prerequisite: 'Requires prior browser-based test execution (the auto-verification run from create_test counts). If run_security_audit errors about missing data, tell the developer to click \"Security Audit\" in the Tests dropdown on the Dashboard - this launches a live browser to capture real network traffic (HAR) and runs analysis automatically.',\n reviewing: 'Use list_security_findings to review results. Filter by severity (critical/high/medium/low/info) or status (open/acknowledged/resolved/false-positive). Critical and high findings should be flagged to the developer immediately.',\n },\n keyConcepts: {\n features: 'Group related pages and tests (e.g., \"Authentication\" covers /auth/login, /auth/register). MUST include page routes.',\n pages: 'Route URLs on a feature - used for coverage tracking and site map (e.g., [\"/login\", \"/register\"])',\n suites: 'Group tests for batch execution - suite name MUST match feature name exactly (case-sensitive)',\n runner: 'Executes tests - can be local (developer machine) or cloud',\n testData: 'Key-value variables injected into tests (e.g., credentials, URLs)',\n },\n retrievedAt: new Date().toISOString(),\n };\n return { contents: [{ uri: 'project://test-conventions', mimeType: 'application/json', text: JSON.stringify(conventions, null, 2) }] };\n } catch (err) {\n return { contents: [{ uri: 'project://test-conventions', mimeType: 'text/plain', text: `Failed to load conventions: ${err instanceof Error ? err.message : String(err)}` }] };\n }\n },\n);\n\n// Connect via stdio transport\nconst transport = new StdioServerTransport();\nawait server.connect(transport);\n","import { readFileSync } from 'fs';\nimport { resolve } from 'path';\nimport { readStoredToken } from './token-store.js';\nimport type { Toolset } from './tools/agent-tools.js';\n\nexport interface ValidateQAConfig {\n apiUrl: string;\n token: string;\n projectId: string;\n /** 'agent': the dozen tools for finding and locking in bugs (VALIDATEQA_TOOLSET=agent); 'full' (default): all of them. */\n toolset: Toolset;\n}\n\nfunction parseArgs(argv: string[]): Record<string, string> {\n const args: Record<string, string> = {};\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n if (arg.startsWith('--') && i + 1 < argv.length) {\n const key = arg.slice(2);\n args[key] = argv[++i];\n }\n }\n return args;\n}\n\nfunction loadDotFile(): Partial<ValidateQAConfig> {\n try {\n const content = readFileSync(resolve(process.cwd(), '.validateqa.json'), 'utf-8');\n const json = JSON.parse(content);\n return {\n apiUrl: typeof json.apiUrl === 'string' ? json.apiUrl : undefined,\n projectId: typeof json.projectId === 'string' ? json.projectId : undefined,\n };\n } catch {\n return {};\n }\n}\n\n/** The toolset a --toolset / VALIDATEQA_TOOLSET value names; an unknown one is reported and serves every tool. */\nexport function toolsetFrom(value: string | undefined): Toolset {\n if (value === undefined || value === '' || value === 'full') return 'full';\n if (value === 'agent') return 'agent';\n console.error(`Unknown toolset \"${value}\" (--toolset, VALIDATEQA_TOOLSET): expected \"agent\" or \"full\". Serving every tool.`);\n return 'full';\n}\n\nexport function resolveConfig(): ValidateQAConfig {\n const cliArgs = parseArgs(process.argv.slice(2));\n const dotFile = loadDotFile();\n\n const apiUrl =\n cliArgs['api-url'] ??\n process.env.VALIDATEQA_API_URL ??\n dotFile.apiUrl;\n\n const projectId =\n cliArgs['project-id'] ??\n process.env.VALIDATEQA_PROJECT_ID ??\n dotFile.projectId;\n\n // `init` saves the token in the user's home folder, keyed by API origin and project\n // (token-store.ts), so committed IDE configs never hold it.\n const token =\n cliArgs['token'] ??\n process.env.VALIDATEQA_TOKEN ??\n (apiUrl && projectId ? readStoredToken(apiUrl, projectId) : undefined);\n\n if (!apiUrl) {\n console.error('Missing required config: api-url (--api-url, VALIDATEQA_API_URL, or .validateqa.json)');\n process.exit(1);\n }\n if (!token) {\n console.error('Missing required config: token (--token, VALIDATEQA_TOKEN, or run the init command from your Validate.QA dashboard again)');\n process.exit(1);\n }\n if (!projectId) {\n console.error('Missing required config: project-id (--project-id, VALIDATEQA_PROJECT_ID, or .validateqa.json)');\n process.exit(1);\n }\n\n const toolset = toolsetFrom(cliArgs['toolset'] ?? process.env.VALIDATEQA_TOOLSET);\n\n return { apiUrl: apiUrl.replace(/\\/$/, ''), token, projectId, toolset };\n}\n","/**\n * Same string as `PLAN_FEATURE_LOCKED_CODE` in `@validateqa/shared`.\n * This package does not depend on shared.\n */\nexport const PLAN_FEATURE_LOCKED_CODE = 'PLAN_FEATURE_LOCKED';\n\nexport class ApiResponseError extends Error {\n readonly status: number;\n readonly body: Record<string, unknown>;\n\n constructor(status: number, body: Record<string, unknown>) {\n const message = typeof body.error === 'string' && body.error.length > 0\n ? body.error\n : `API error ${status}`;\n super(message);\n this.name = 'ApiResponseError';\n this.status = status;\n this.body = body;\n }\n}\n\n/** Tool text for a thrown client error. A plan lock keeps the structured body. */\nexport function toolErrorText(err: unknown): string {\n if (err instanceof ApiResponseError && err.body.code === PLAN_FEATURE_LOCKED_CODE) {\n return JSON.stringify(err.body);\n }\n return `Error: ${err instanceof Error ? err.message : String(err)}`;\n}\n\nexport class ValidateQAClient {\n constructor(\n private baseUrl: string,\n private token: string,\n private projectId: string,\n ) {}\n\n async executeTool(tool: string, args: Record<string, unknown> = {}): Promise<unknown> {\n let res: Response;\n try {\n res = await fetch(`${this.baseUrl}/api/mcp/execute`, {\n method: 'POST',\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'X-Project-Id': this.projectId,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({ tool, args }),\n signal: AbortSignal.timeout(120_000),\n });\n } catch (err) {\n // Network errors, DNS failures, timeouts\n const cause = err instanceof Error && err.cause instanceof Error ? err.cause.message : '';\n const msg = err instanceof Error ? err.message : String(err);\n throw new Error(`Connection failed: ${msg}${cause ? ` (${cause})` : ''}`);\n }\n\n if (!res.ok) {\n const parsed = await res.json().catch(() => ({ error: res.statusText }));\n const body = parsed && typeof parsed === 'object' && !Array.isArray(parsed)\n ? parsed as Record<string, unknown>\n : { error: res.statusText };\n throw new ApiResponseError(res.status, body);\n }\n\n return res.json();\n }\n\n /** project://test-conventions from the server (GET /api/mcp/test-conventions); null when the server predates it. */\n async getTestConventions(): Promise<unknown | null> {\n try {\n const res = await fetch(`${this.baseUrl}/api/mcp/test-conventions`, {\n headers: {\n 'Authorization': `Bearer ${this.token}`,\n 'X-Project-Id': this.projectId,\n },\n signal: AbortSignal.timeout(30_000),\n });\n if (!res.ok) return null;\n return await res.json();\n } catch {\n return null;\n }\n }\n\n async healthCheck(): Promise<boolean> {\n try {\n const res = await fetch(`${this.baseUrl}/health`, {\n signal: AbortSignal.timeout(5000),\n });\n return res.ok;\n } catch {\n return false;\n }\n }\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { toolErrorText, type ValidateQAClient } from '../api-client.js';\nimport { AGENT_TOOL_NAMES, AgentSession, limitTools, registerAgentTools, type Toolset } from './agent-tools.js';\n\ntype ToolAnnotations = {\n readOnlyHint?: boolean;\n destructiveHint?: boolean;\n idempotentHint?: boolean;\n openWorldHint?: boolean;\n};\n\nfunction tool(\n server: McpServer,\n client: ValidateQAClient,\n name: string,\n description: string,\n schema: Record<string, z.ZodTypeAny>,\n annotations?: ToolAnnotations,\n) {\n const handler = async (args: Record<string, unknown>) => {\n try {\n const result = await client.executeTool(name, args);\n return {\n content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }],\n };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: toolErrorText(err) }],\n isError: true,\n };\n }\n };\n\n if (annotations) {\n server.tool(name, description, schema, annotations, handler);\n } else {\n server.tool(name, description, schema, handler);\n }\n}\n\n// The server answers one wait call within SERVER_WAIT_MAX_SECONDS (its proxy\n// cuts requests at 60 s). A longer wait here, up to WAIT_MAX_SECONDS, is a\n// chain of calls: each continues with wait_for_run / wait_for_batch until the\n// run is done or the time is used up.\nconst SERVER_WAIT_MAX_SECONDS = 55;\nconst WAIT_DEFAULT_SECONDS = 50;\nconst WAIT_MAX_SECONDS = 120;\n\ntype WaitKind = { tool: 'wait_for_run'; idKey: 'runId' } | { tool: 'wait_for_batch'; idKey: 'batchId' };\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction waitSecondsFrom(raw: unknown, fallback: number): number {\n const n = typeof raw === 'number' ? raw : Number.NaN;\n if (!Number.isFinite(n)) return fallback;\n return Math.min(Math.max(n, 0), WAIT_MAX_SECONDS);\n}\n\n/**\n * Call `name` with up to SERVER_WAIT_MAX_SECONDS of `waitKey`, then keep\n * waiting with `kind.tool` while the result says timedOut and time is left.\n */\nasync function executeWithWait(\n client: ValidateQAClient,\n name: string,\n args: Record<string, unknown>,\n waitKey: 'waitSeconds' | 'timeoutSeconds',\n totalSeconds: number,\n kind: WaitKind,\n): Promise<unknown> {\n const deadline = Date.now() + totalSeconds * 1000;\n const first = Math.min(totalSeconds, SERVER_WAIT_MAX_SECONDS);\n let result = await client.executeTool(name, { ...args, [waitKey]: first });\n // wait_for_run answers in the get_test_run shape (`id`, no `runId`), so keep\n // the id from the arguments or the first answer for every later call.\n let id: unknown = args[kind.idKey];\n for (;;) {\n if (!isRecord(result) || result.timedOut !== true) return result;\n id = result[kind.idKey] ?? id;\n const left = Math.floor((deadline - Date.now()) / 1000);\n if (typeof id !== 'string' || left < 1) return result;\n result = await client.executeTool(kind.tool, { [kind.idKey]: id, timeoutSeconds: Math.min(left, SERVER_WAIT_MAX_SECONDS) });\n }\n}\n\nfunction waitTool(\n server: McpServer,\n client: ValidateQAClient,\n name: string,\n description: string,\n schema: Record<string, z.ZodTypeAny>,\n waitKey: 'waitSeconds' | 'timeoutSeconds',\n defaultSeconds: number,\n kind: WaitKind,\n annotations?: ToolAnnotations,\n) {\n const handler = async (args: Record<string, unknown>) => {\n try {\n const seconds = waitSecondsFrom(args[waitKey], defaultSeconds);\n const result = seconds > 0\n ? await executeWithWait(client, name, args, waitKey, seconds, kind)\n : await client.executeTool(name, args);\n return { content: [{ type: 'text' as const, text: JSON.stringify(result, null, 2) }] };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: toolErrorText(err) }],\n isError: true,\n };\n }\n };\n if (annotations) {\n server.tool(name, description, schema, annotations, handler);\n } else {\n server.tool(name, description, schema, handler);\n }\n}\n\nexport function registerAllTools(server: McpServer, client: ValidateQAClient, toolset: Toolset = 'full') {\n // The 'agent' toolset registers only AGENT_TOOL_NAMES. Every tool shares one session, so\n // collect_verdicts knows what save_regression_test saved and what edit_test_code changed.\n const session = new AgentSession();\n if (toolset === 'agent') server = limitTools(server, AGENT_TOOL_NAMES);\n client = session.track(client);\n registerAgentTools(server, client, session);\n\n // ── Test Management ──────────────────────────────────────────────────\n\n tool(server, client, 'list_tests',\n 'List tests in the current project. Returns test name, status, suite, tags, confidence, and the last run result. Paginated - use limit/offset to page through all tests.',\n {\n status: z.enum(['DRAFT', 'ACTIVE', 'DISABLED', 'QUARANTINED']).optional().describe('Filter by test lifecycle status'),\n suite: z.string().optional().describe('Filter by suite name'),\n search: z.string().optional().describe('Search test names (case-insensitive substring match)'),\n tag: z.string().optional().describe('Filter by tag (e.g. @regression, @smoke, @auth)'),\n limit: z.number().optional().describe('Max results to return (default: 50, max: 200)'),\n offset: z.number().optional().describe('Number of results to skip for pagination (default: 0)'),\n },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_test',\n 'Get details of a specific test: name, status, tags, steps, session, and the 5 most recent run results. Code is truncated to 200 lines - use get_test_code for the full Playwright code.',\n { testId: z.string().describe('The test case ID') },\n { readOnlyHint: true },\n );\n\n waitTool(server, client, 'run_test',\n 'Queue a test for execution. Creates a PENDING test run that will be picked up by an online runner. Returns the run ID; with waitSeconds, the finished run (as wait_for_run). For a mobile (Appium) test, pass platform to run it on the OTHER platform than it was authored on - if some steps lack a native locator for that platform, a one-time locator-discovery pass runs first (supply appId once for the target-platform build), then the test runs automatically.',\n {\n testId: z.string().describe('The test case ID to run'),\n platform: z.enum(['IOS', 'ANDROID']).optional().describe('Mobile only: run this test on the given platform (cross-platform run). Omit to run on the authoring platform.'),\n appId: z.string().optional().describe('Mobile cross-platform only: the target-platform app id (bundleId/appPackage). Only needed the first time this project runs on the other platform.'),\n deeplink: z.string().optional().describe('Mobile cross-platform only: target-platform deep link URL. Required when the authored test uses a custom app scheme that may differ between iOS and Android (the server answers need_deeplink); http/https App Links are reused automatically.'),\n waitSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`Optional. Wait up to this many seconds (max ${WAIT_MAX_SECONDS}) for the run to finish and return its final result, like wait_for_run (timedOut: true if still going). 0 or omitted: return at once with the run ID.`),\n },\n 'waitSeconds', 0, { tool: 'wait_for_run', idKey: 'runId' },\n );\n\n tool(server, client, 'delete_test',\n 'Permanently delete a test case and all its run history. Cannot be undone.',\n { testId: z.string().describe('The test case ID to delete') },\n { destructiveHint: true },\n );\n\n tool(server, client, 'heal_test',\n 'Queue an AI healing run for a failing test. The AI agent replays the test in a real browser, analyzes the failure, and either fixes the Playwright code OR detects an application bug (quarantines the test with @bug-found if the app itself is broken, not the test).',\n { testId: z.string().describe('The failing test case ID') },\n );\n\n tool(server, client, 'edit_test_code',\n 'Update an existing test case. WEB (Playwright) tests: provide the full replacement playwrightCode (read project://test-conventions for patterns; code is validated - fix any warnings returned). MOBILE (Appium) tests: provide the FULL replacement steps array (playwrightCode is rejected - mobile tests execute structured steps, not code). Read the current form first with get_test_code.',\n {\n testId: z.string().describe('The test case ID to edit'),\n playwrightCode: z.string().optional().describe('Web tests only: new Playwright test code (full file replacement)'),\n steps: z.array(z.record(z.string(), z.unknown())).optional().describe('Mobile (Appium) tests only: FULL replacement structured step array (same shape as create_test steps). Include every step, not just the changed ones.'),\n name: z.string().optional().describe('New test name'),\n description: z.string().optional().describe('New short description of what the test verifies'),\n tags: z.array(z.string()).optional().describe('New tags array (e.g. [\"@regression\", \"@smoke\"])'),\n status: z.enum(['DRAFT', 'ACTIVE', 'DISABLED', 'QUARANTINED']).optional().describe('New test status. Re-activating a bug-found test (QUARANTINED→ACTIVE) also queues a verification run and returns its testRunId.'),\n },\n );\n\n tool(server, client, 'update_test_status',\n 'Change the lifecycle status of one or more tests (e.g. promote DRAFT→ACTIVE, disable a flaky test). Re-activating exactly ONE bug-found test (QUARANTINED→ACTIVE) also queues a verification run and returns its testRunId; with several ids no run is queued: any bug-found tests among them come back as unverifiedTestIds, to check with batch_run_tests.',\n {\n testIds: z.array(z.string()).describe('Test IDs to update'),\n status: z.enum(['DRAFT', 'ACTIVE', 'DISABLED', 'QUARANTINED']).describe('New status to set'),\n },\n );\n\n // ── Test Runs ────────────────────────────────────────────────────────\n\n tool(server, client, 'get_test_run',\n 'Get the status and result of a specific test run once (to wait for it to finish, use wait_for_run, not a loop of this). Look up by runId, or the latest run for a testId. A FAILED or ERROR run also carries its failure evidence: error (up to 4 KB), consoleLog (the last 200 lines, at most 20 KB), failedSteps, and for a mobile run domSnapshot (the page source at the failure). Screenshots: get_test_screenshots. No Playwright trace is recorded.',\n {\n runId: z.string().optional().describe('The test run ID'),\n testId: z.string().optional().describe('Get the most recent run for this test case ID'),\n },\n { readOnlyHint: true },\n );\n\n waitTool(server, client, 'wait_for_run',\n `Wait until a test run (run_test, heal_test, a verification run) is PASSED, FAILED or ERROR, then return it in the get_test_run shape, failure evidence included. Returns after at most timeoutSeconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS}) with timedOut: true and the current status if it is still pending or running; call it again to keep waiting. Use this instead of polling get_test_run.`,\n {\n runId: z.string().describe('The test run ID to wait for'),\n timeoutSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`How long to wait, in seconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS})`),\n },\n 'timeoutSeconds', WAIT_DEFAULT_SECONDS, { tool: 'wait_for_run', idKey: 'runId' },\n { readOnlyHint: true },\n );\n\n waitTool(server, client, 'wait_for_batch',\n `Wait until a batch from batch_run_tests is COMPLETED, then return its counts (total, completed, passed, failed, errored) and, when tests failed, up to 20 failures with their runId and error. Returns after at most timeoutSeconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS}) with timedOut: true and the current counts if it is still running; call it again to keep waiting. Use get_test_run on a failure's runId for its console log.`,\n {\n batchId: z.string().describe('The batch ID returned by batch_run_tests'),\n timeoutSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`How long to wait, in seconds (default ${WAIT_DEFAULT_SECONDS}, max ${WAIT_MAX_SECONDS})`),\n },\n 'timeoutSeconds', WAIT_DEFAULT_SECONDS, { tool: 'wait_for_batch', idKey: 'batchId' },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'run_discovery',\n 'Start AI discovery for this project. For web, discovery uses the configured base URL. For mobile, pass platform and appId to explore a native app on a connected runner/device. Use survey to map features, deep with featureName to generate tests for one feature, or full for broad generation. On a project with no platform yet, a workspace owner must choose Mobile first; tokens can\\'t make that first choice.',\n {\n mode: z.enum(['survey', 'deep', 'full']).optional().describe('Discovery mode. survey maps features/screens and generates no tests; deep generates tests for one feature; full explores and generates broadly.'),\n featureName: z.string().optional().describe('Required for deep mode: the feature name to generate tests for.'),\n focusAreas: z.array(z.string()).optional().describe('Optional routes, screens, or areas to focus discovery on.'),\n platform: z.enum(['IOS', 'ANDROID']).optional().describe('Mobile only: target platform. Requires appId.'),\n appId: z.string().optional().describe('Mobile only: bundle id/package id to explore. Requires platform.'),\n resetPolicy: z.enum(['AUTO', 'CLEAR_APP_DATA', 'PRESERVE_APP_DATA']).optional().describe('Mobile only: app-state policy for discovery/replay. AUTO uses default safe reset behavior.'),\n },\n );\n\n tool(server, client, 'get_discovery_status',\n 'Check progress for a discovery run. Omit discoveryRunId to get the latest discovery run for this project.',\n {\n discoveryRunId: z.string().optional().describe('Specific discovery run ID. Omit for latest.'),\n },\n { readOnlyHint: true },\n );\n\n // Custom handler - returns MCP image content blocks so the AI can see screenshots\n server.tool(\n 'get_test_screenshots',\n 'Get screenshots from a specific test run as images the AI can actually see. Use this to visually inspect what happened during a failed or passing run.',\n {\n runId: z.string().describe('The test run ID to fetch screenshots from'),\n limit: z.number().optional().describe('Max screenshots to return (default: 5, max: 10)'),\n },\n { readOnlyHint: true },\n async (args) => {\n try {\n const result = await client.executeTool('get_test_screenshots', args) as {\n runId: string;\n status: string;\n total: number;\n returned: number;\n screenshots: Array<{ index: number; data: string; mimeType: string }>;\n };\n\n if (!result.screenshots || result.screenshots.length === 0) {\n return {\n content: [{\n type: 'text' as const,\n text: `No screenshots available for run ${result.runId} (status: ${result.status})`,\n }],\n };\n }\n\n const content: Array<{ type: 'text'; text: string } | { type: 'image'; data: string; mimeType: string }> = [\n {\n type: 'text' as const,\n text: `Run ${result.runId} - status: ${result.status} - showing ${result.returned} of ${result.total} screenshot(s)`,\n },\n ];\n\n for (const shot of result.screenshots) {\n content.push({ type: 'image' as const, data: shot.data, mimeType: shot.mimeType });\n }\n\n return { content };\n } catch (err) {\n return {\n content: [{ type: 'text' as const, text: toolErrorText(err) }],\n isError: true,\n };\n }\n },\n );\n\n tool(server, client, 'cancel_test_run',\n 'Cancel a pending or running test run. Can cancel by run ID or cancel the latest running batch.',\n {\n runId: z.string().optional().describe('The specific test run ID to cancel'),\n cancelBatch: z.boolean().optional().describe('If true, cancel the latest batch run instead'),\n },\n );\n\n waitTool(server, client, 'batch_run_tests',\n 'Run multiple tests from the database individually. Filter by test IDs or suite name; omit both to run all ACTIVE tests. Each test executes in its own browser. Returns the batchId; follow it with wait_for_batch, or pass waitSeconds.',\n {\n testIds: z.array(z.string()).optional().describe('Specific test IDs to run'),\n suite: z.string().optional().describe('Run all active tests in this suite'),\n waitSeconds: z.number().min(0).max(WAIT_MAX_SECONDS).optional().describe(`Optional. Wait up to this many seconds (max ${WAIT_MAX_SECONDS}) for the batch to complete and return the wait_for_batch result (timedOut: true if still running). 0 or omitted: return at once with the batchId.`),\n },\n 'waitSeconds', 0, { tool: 'wait_for_batch', idKey: 'batchId' },\n );\n\n // ── Features & API Endpoints ──────────────────────────────────────────\n\n tool(server, client, 'list_features',\n 'List all features with test counts and failure counts (last 24h). Use failingTests > 0 to identify which features need attention, then call get_test_failures({ suite: featureName }) to drill into the full error details for that feature.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'generate_tests_for_feature',\n 'Start test generation for an existing feature by id or name. For mobile, run_discovery with mode=deep, featureName, platform, and appId gives explicit device/platform control.',\n {\n featureId: z.string().optional().describe('Feature ID. Either featureId or featureName is required.'),\n featureName: z.string().optional().describe('Feature name. Either featureId or featureName is required.'),\n },\n );\n\n tool(server, client, 'list_api_endpoints',\n 'List captured API endpoints for the current project. Optionally scope to a specific feature name.',\n {\n featureName: z.string().optional().describe('Optional feature name to scope the endpoint list'),\n },\n { readOnlyHint: true },\n );\n\n // ── Infrastructure ───────────────────────────────────────────────────\n\n tool(server, client, 'get_runner_health',\n 'Check whether the project\\'s configured runners are online, including available device capabilities.',\n {},\n { readOnlyHint: true },\n );\n\n // ── Reports & Observability ──────────────────────────────────────────\n\n tool(server, client, 'get_project_overview',\n 'Get a workspace-level QA overview: test counts, pass rates, runner readiness, features, coverage gaps, variables, schedules, and notifications.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_reports_summary',\n 'Get a high-level report: total tests (active, never-run), 7-day pass rate, today\\'s run count, and the 10 most recent test run results.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_regression_stats',\n 'Get test counts by lifecycle status (active, quarantined, draft, disabled, total; API specs count their inner tests) and the 7-day pass rate, with rate-limit, config-issue and cancelled runs left out. The same numbers as the dashboard\\'s regression stats.',\n {},\n { readOnlyHint: true },\n );\n\n // ── Schedules (Admin) ────────────────────────────────────────────────\n\n tool(server, client, 'list_schedules',\n 'List all scheduled test runs in the project.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'create_schedule',\n 'Create a new recurring test schedule. Runs all matching tests from the database on the configured interval.',\n {\n name: z.string().describe('Name for the schedule (e.g. \"Nightly smoke tests\")'),\n mode: z.enum(['batch']).describe('Run mode: batch (runs project tests from the database)'),\n intervalMinutes: z.union([z.literal(60), z.literal(360), z.literal(720), z.literal(1440), z.literal(2880), z.literal(10080)]).describe('Interval in minutes: 60 (hourly), 360 (6h), 720 (12h), 1440 (daily), 2880 (2 days), 10080 (weekly)'),\n tags: z.array(z.string()).optional().describe('Tags to filter which tests run (e.g. [\"@smoke\"]). Omit to run all ACTIVE tests.'),\n autoHeal: z.boolean().optional().describe(\"Heal this schedule's failed runs (default false). Takes effect only while the project's auto-heal (toggle_auto_heal) is on: a run is healed only when both are on.\"),\n enabled: z.boolean().optional().describe('Whether the schedule is active (default: true)'),\n runImmediately: z.boolean().optional().describe('Whether to trigger the first run immediately (default: true)'),\n },\n );\n\n tool(server, client, 'update_schedule',\n 'Update an existing test schedule.',\n {\n scheduleId: z.string().describe('The schedule ID to update'),\n name: z.string().optional().describe('New name for the schedule'),\n intervalMinutes: z.union([z.literal(60), z.literal(360), z.literal(720), z.literal(1440), z.literal(2880), z.literal(10080)]).optional().describe('New interval in minutes'),\n suite: z.string().optional().describe('New suite name to filter tests'),\n tags: z.array(z.string()).optional().describe('New tag filter'),\n autoHeal: z.boolean().optional().describe(\"Heal this schedule's failed runs. Takes effect only while the project's auto-heal (toggle_auto_heal) is on.\"),\n enabled: z.boolean().optional().describe('Enable or disable the schedule'),\n },\n );\n\n tool(server, client, 'delete_schedule',\n 'Delete (remove) a test schedule permanently.',\n { scheduleId: z.string().describe('The schedule ID to delete') },\n { destructiveHint: true },\n );\n\n tool(server, client, 'run_schedule_now',\n 'Trigger a scheduled run immediately instead of waiting for the next scheduled time.',\n { scheduleId: z.string().describe('The schedule ID to trigger') },\n );\n\n // ── Configuration ────────────────────────────────────────────────────\n\n tool(server, client, 'get_variables',\n 'Get all test variables (environment variables) for the current project. Secrets are redacted.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'set_variable',\n 'Set or update a test variable (environment variable) for the project. Used to configure base URLs, credentials, etc.',\n {\n key: z.string().describe('Variable name (e.g. BASE_URL, TEST_USER_EMAIL, API_KEY)'),\n value: z.string().describe('Variable value. Set value to empty string to remove a variable.'),\n secret: z.boolean().optional().describe('Whether this variable is a secret (will be redacted in UI)'),\n },\n );\n\n tool(server, client, 'create_test',\n 'Create a new test case. WEB projects: provide playwrightCode (read project://test-conventions for templates BEFORE writing code; on a production BASE_URL use its read-only guard template). The response may carry warnings (weak selectors, hardcoded hosts in URL assertions, vacuous checks, a missing read-only guard) - they never block, fix them with edit_test_code. MOBILE (Appium) projects: provide steps - a structured step array is the ONLY executable form for mobile (code is never executed). The test starts as DRAFT - auto-runs and auto-promotes to ACTIVE on pass. IMPORTANT: (1) Set suite to the EXACT feature name. (2) After creating all tests for a web feature, you MUST call add_api_endpoints.',\n {\n name: z.string().describe('Test name (e.g. \"Login - Happy Path\")'),\n playwrightCode: z.string().optional().describe('Web projects: full Playwright test code (required). Mobile projects: optional WebdriverIO export for humans - never executed.'),\n steps: z.array(z.record(z.string(), z.unknown())).optional().describe('Mobile (Appium) projects ONLY - required. Ordered steps: { action: tap|type|clear|swipe|scroll|back|home|waitForElement|assertVisible|assertText, description, target?: { primary, fallbacks?, screenName? }, value? }. target.primary: \"css=#domId\" for WebView-rendered (hybrid) controls, resource-id/accessibility-id/text= only for genuinely native UI. type/assertText require value ({{TEST_VAR}} placeholders supported). Do not author OS permission-dialog taps. End with an assert step. Call get_feature first for mapped screens + known selectors.'),\n suite: z.string().optional().describe('MUST match an existing feature name exactly to link the test to that feature (e.g. \"Authentication\", not \"Auth Tests\")'),\n tags: z.array(z.string()).optional().describe('Tags (e.g. [\"@smoke\", \"@auth\"])'),\n },\n );\n\n // ── Features ─────────────────────────────────────────────────────────\n\n tool(server, client, 'get_feature',\n 'Get detailed info about a feature including its pages, test plans, existing tests with lastRunStatus, and AI insights.',\n { featureName: z.string().describe('The feature name (case-insensitive match)') },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'get_spec_coverage',\n 'Map spec items to the existing tests of a feature (heuristic: test names, tags and the copy in their code) and list the items no test covers yet. Pass the spec you were given as one item per line item, scoped by page (\"Sign up: malformed email shows an error\"); without specItems it uses the feature\\'s stored test plans. Call it before you stop writing tests for a feature, then cover every uncovered item.',\n {\n featureName: z.string().describe('The feature name (case-insensitive) - its suite holds the tests'),\n specItems: z.array(z.string()).optional().describe('Spec items, one per behaviour and page (max 200)'),\n },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'create_feature',\n 'Create a new feature with page routes. Read the developer\\'s route files to provide accurate page URLs. Pages are used for coverage tracking and site map. Idempotent, not an upsert: if an active feature with this name already exists it is returned unchanged with existing: true (use update_feature with its id to change it). Fails only when the name belongs to an archived feature (restore it with restore_feature, or pick another name).',\n {\n name: z.string().describe('Feature name (e.g. \"Checkout Flow\")'),\n description: z.string().optional().describe('What the feature does and what to test'),\n pages: z.array(z.string()).describe('Page routes this feature covers (e.g. [\"/checkout\", \"/checkout/payment\"]). Required for test generation.'),\n criticality: z.enum(['critical', 'high', 'medium', 'low']).optional().describe('Feature criticality level (default: medium)'),\n },\n );\n\n // ── Full Test Code ───────────────────────────────────────────────────\n\n tool(server, client, 'get_test_code',\n 'Get the full (untruncated) Playwright test code for a test case. Use this when you need to read, analyze, or prepare edits for the complete test code.',\n { testId: z.string().describe('The test case ID') },\n { readOnlyHint: true },\n );\n\n // ── Project Settings ─────────────────────────────────────────────────\n\n tool(server, client, 'set_runner_preference',\n 'Switch between cloud and local runner for test execution. Copilot (browser session) only; access tokens and MCP connectors get 403 — change the runner in Project Settings.',\n { preference: z.enum(['cloud', 'local']).describe('Runner type: cloud (shared infrastructure) or local (runs on your machine)') },\n );\n\n tool(server, client, 'toggle_auto_heal',\n 'Enable or disable automatic AI healing when tests fail: the verification run after create_test, batch and suite runs. When off, no heal is queued automatically; heal_test still works.',\n { enabled: z.boolean().describe('true to enable auto-heal, false to disable') },\n );\n\n // ── Test Data ────────────────────────────────────────────────────────\n\n tool(server, client, 'list_test_data',\n 'List all test data sets in the project. Each set contains key-value pairs injected into AI prompts and test runs.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'create_test_data',\n 'Create a new test data set with key-value pairs. Max 200 entries per set.',\n {\n name: z.string().describe('Name of the test data set (max 100 chars)'),\n description: z.string().optional().describe('Optional description (max 1000 chars)'),\n data: z.record(z.string(), z.string()).describe('Key-value pairs (e.g. {\"email\": \"test@example.com\", \"password\": \"secret\"})'),\n },\n );\n\n tool(server, client, 'delete_test_data',\n 'Delete a test data set by ID.',\n { dataSetId: z.string().describe('The test data set ID to delete') },\n { destructiveHint: true },\n );\n\n // ── Feature Management ───────────────────────────────────────────────\n\n tool(server, client, 'update_feature',\n 'Update a feature\\'s name, description, pages, or criticality.',\n {\n featureId: z.string().describe('The feature ID to update'),\n name: z.string().optional().describe('New feature name'),\n description: z.string().optional().describe('New description'),\n pages: z.array(z.string()).optional().describe('Updated page routes'),\n criticality: z.enum(['critical', 'high', 'medium', 'low']).optional().describe('Feature criticality level'),\n },\n );\n\n tool(server, client, 'archive_feature',\n 'Archive a feature (soft delete). Archived features are hidden from the dashboard but can be restored with restore_feature.',\n { featureId: z.string().describe('The feature ID to archive') },\n { destructiveHint: true },\n );\n\n tool(server, client, 'restore_feature',\n 'Restore an archived feature so it shows in the dashboard again. Does nothing for a feature that is already active. Fails if an active feature now holds the same name.',\n { featureId: z.string().describe('The ID of the archived feature to restore') },\n );\n\n // ── Site Map ─────────────────────────────────────────────────────────\n\n tool(server, client, 'get_site_map_coverage',\n 'Get site map coverage statistics: total pages, pages with tests, elements covered, coverage percentage, and per-page breakdown.',\n {},\n { readOnlyHint: true },\n );\n\n // ── App Map ──────────────────────────────────────────────────────────\n\n tool(server, client, 'get_app_map',\n 'Get the app map: the features, sub-features, user journeys and page states found for this project, each with its routes, sign-in need, what it writes (orders, bookings, sign-ups, payments, messages), where it came from (survey, code, bundle, network, the customer) and whether the customer ticked it. Returns { map: null } before the first build. Use format \"eval\" for the compact features/journeys/states export.',\n { format: z.enum(['eval']).optional().describe('\"eval\" returns the compact features/journeys/states export instead of the full map') },\n { readOnlyHint: true },\n );\n\n // ── Issue Triage ─────────────────────────────────────────────────────\n\n tool(server, client, 'list_bugs',\n 'Get all detected bugs: app bugs (confirmed, and waiting for a person), security findings, flaky tests, and visual issues flagged as bugs. appBugs holds at most `limit` bugs (default 50, max 200); appBugsTruncated says when the project has more, summary.totalBugs is the full count.',\n { limit: z.number().int().min(1).max(200).optional().describe('Max app bugs to return (default 50, max 200). When appBugsTruncated is true, ask again with a higher limit.') },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'list_visual_issues',\n 'List visual issues found on the app\\'s pages by the layout checks that run during surveys and discovery (clipped or overflowing text, covered or cut-off controls, broken images, sideways scrolling on phones, small tap targets, low contrast). Each has the page route, viewport, CSS selector, element box and a description, so you can find and fix the element in the code. Visual issues never fail tests.',\n {\n status: z.enum(['OPEN', 'ACCEPTED', 'DISMISSED', 'GONE']).optional().describe('Filter by status (default OPEN). ACCEPTED = flagged as a bug; GONE = no longer seen on the page'),\n severity: z.enum(['high', 'medium', 'low']).optional().describe('Filter by severity'),\n viewport: z.enum(['desktop', 'phone']).optional().describe('Filter by viewport'),\n route: z.string().optional().describe('Only this page route, e.g. /pricing'),\n },\n { readOnlyHint: true },\n );\n\n tool(server, client, 'flag_as_bug',\n 'Mark a failing test as a confirmed application bug - quarantines it with @bug-found and records the bug description. Use when the failure is caused by broken app behaviour, NOT a test script issue. Do NOT use for selector changes or timeout failures - use heal_test or edit_test_code for those.',\n {\n testId: z.string().describe('The failing test case ID'),\n description: z.string().describe('What is broken in the application (e.g. \"POST /api/checkout returns 500 when payment method is valid\")'),\n },\n );\n\n // ── Security Audit ───────────────────────────────────────────────────\n\n tool(server, client, 'run_security_audit',\n 'Run an AI security audit on captured API traffic. Requires prior browser-based test execution (discovery or capture) - analyzes real network traffic, not just registered endpoints. If no traffic exists, the developer can trigger \"Security Audit\" from the Dashboard which launches a live browser to capture it.',\n {},\n );\n\n tool(server, client, 'list_security_findings',\n 'List security audit findings with optional filters. Returns severity, category, status, and affected endpoints.',\n {\n severity: z.enum(['critical', 'high', 'medium', 'low', 'info']).optional().describe('Filter by severity'),\n status: z.enum(['open', 'acknowledged', 'resolved', 'false-positive', 'false-positive-auto']).optional().describe('Filter by status'),\n verificationStatus: z.enum(['confirmed', 'false-positive-auto', 'unverifiable', 'error']).optional().describe('Filter by verification probe result'),\n },\n { readOnlyHint: true },\n );\n\n // ── Project Settings ─────────────────────────────────────────────────\n\n tool(server, client, 'set_app_brief',\n 'Set or update the project\\'s application brief - a description of the app used by AI for better test generation.',\n { appBrief: z.string().describe('Application description. Set empty to clear.') },\n );\n\n // ── Coverage & Draft Verification ────────────────────────────────────\n\n tool(server, client, 'get_coverage_summary',\n 'Get feature-level test coverage: planned vs existing vs passing tests, plus API endpoint coverage. Shows uncoveredEndpoints per feature - use this to find which API endpoints still need tests.',\n {},\n { readOnlyHint: true },\n );\n\n tool(server, client, 'run_draft_tests',\n 'Run DRAFT tests to verify them, 50 at most per call (oldest first; hasMore says when more remain). Tests that pass auto-promote to ACTIVE. Optionally scope to a feature. Returns a batchId: wait with wait_for_batch.',\n {\n featureName: z.string().optional().describe('Only run drafts for this feature (optional)'),\n },\n );\n\n // ── Project Setup ────────────────────────────────────────────────────\n\n tool(server, client, 'setup_project',\n 'One-shot project setup from codebase knowledge. Sets app brief, base URL, test credentials, and creates features with pages - all in one call. Use this when a developer says \"set up test coverage for my app.\"',\n {\n appBrief: z.string().optional().describe('Description of the application (what it does, key features, tech stack)'),\n baseUrl: z.string().describe('Application base URL (e.g. \"https://myapp.com\" or \"http://localhost:3000\")'),\n testCredentials: z.object({\n email: z.string().optional().describe('Test user email'),\n password: z.string().optional().describe('Test user password'),\n }).optional().describe('Login credentials for test execution'),\n features: z.array(z.object({\n name: z.string().describe('Feature name'),\n description: z.string().optional().describe('What the feature does'),\n pages: z.array(z.string()).describe('Page routes (e.g. [\"/login\", \"/register\"])'),\n criticality: z.enum(['critical', 'high', 'medium', 'low']).optional().describe('Feature criticality'),\n })).describe('Features to create with their page routes'),\n },\n );\n\n // ── API Endpoints ───────────────────────────────────────────────────\n\n tool(server, client, 'add_api_endpoints',\n 'Register API endpoints for a feature from codebase knowledge. Read the developer\\'s route handlers to extract methods, paths, request/response shapes. Populates the API intelligence dashboard.',\n {\n featureName: z.string().describe('Feature name to link these endpoints to'),\n endpoints: z.array(z.object({\n method: z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']).describe('HTTP method'),\n path: z.string().describe('API path (e.g. \"/api/auth/login\")'),\n requestShape: z.record(z.string(), z.string()).optional().describe('Request body fields with types (e.g. { \"email\": \"string\" })'),\n responseShapes: z.record(z.string(), z.record(z.string(), z.string())).optional().describe('Response shapes by status code (e.g. { \"200\": { \"token\": \"string\" } })'),\n requiresAuth: z.boolean().optional().describe('Whether endpoint requires authentication'),\n })).describe('API endpoints to register'),\n },\n );\n\n // ── Failure Diagnostics ──────────────────────────────────────────────\n\n tool(server, client, 'get_test_failures',\n 'Get recent test failures with full error messages, Playwright code, step-level failures, and heal context. Use this to understand why tests are failing.',\n {\n limit: z.number().optional().describe('Max failures to return (default: 50, max: 100)'),\n suite: z.string().optional().describe('Filter by feature/suite name'),\n mode: z.enum(['run', 'heal', 'all']).optional().describe('run = regular failures, heal = heal attempt failures, all = both (default)'),\n sinceHours: z.number().optional().describe('Only failures from the last N hours (default: 24)'),\n },\n { readOnlyHint: true },\n );\n}\n","import { z } from 'zod';\nimport type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { toolErrorText, type ValidateQAClient } from '../api-client.js';\n\n// Tools for an agent that explores an app with its own browser and locks in the\n// bugs it finds as saved tests: start_here (one read with everything to start),\n// save_regression_test (save and move on, the run happens in the cloud) and\n// collect_verdicts (one call at the end: which tests reproduce their bug).\n\n/** The tools VALIDATEQA_TOOLSET=agent registers: the bug-hunting loop and the few it needs around it. */\nexport const AGENT_TOOL_NAMES: ReadonlySet<string> = new Set([\n 'start_here', 'get_feature', 'save_regression_test', 'collect_verdicts', 'edit_test_code', 'get_test_code',\n 'list_tests', 'get_test_run', 'get_test_screenshots', 'list_bugs', 'flag_as_bug', 'delete_test',\n]);\n\nexport type Toolset = 'full' | 'agent';\n\n/** `server` with only the tools in `names` registered; every other registration is skipped. */\nexport function limitTools(server: McpServer, names: ReadonlySet<string>): McpServer {\n return new Proxy(server, {\n get(target, prop, receiver) {\n if (prop !== 'tool') return Reflect.get(target, prop, receiver);\n return (name: string, ...rest: unknown[]) =>\n names.has(name) ? (target.tool as (...args: unknown[]) => unknown).call(target, name, ...rest) : undefined;\n },\n });\n}\n\n// ── Session memory ─────────────────────────────────────────────────────\n\ntype SavedTest = { testId: string; name: string; bug?: string; runId?: string; edited: boolean };\n\n/** What this MCP process saved, so collect_verdicts needs no arguments. One process serves one agent session. */\nexport class AgentSession {\n readonly saved = new Map<string, SavedTest>();\n\n remember(test: SavedTest): void {\n this.saved.set(test.testId, test);\n }\n\n /** An edited test's last run checked the old code: collect_verdicts runs it again. */\n markEdited(testId: string): void {\n const test = this.saved.get(testId);\n if (test) test.edited = true;\n }\n\n /**\n * `client`, noting each edit_test_code call so collect_verdicts knows which runs are out of date. An edit of a\n * test saved with save_regression_test is linted as a bug test, like its save (assertsBug).\n */\n track(client: ValidateQAClient): ValidateQAClient {\n return new Proxy(client, {\n get: (target, prop, receiver) => {\n if (prop !== 'executeTool') return Reflect.get(target, prop, receiver);\n return async (tool: string, args?: Record<string, unknown>) => {\n const edited = tool === 'edit_test_code' && typeof args?.testId === 'string' ? args.testId : undefined;\n const result = await target.executeTool(tool, edited && this.saved.has(edited) ? { ...args, assertsBug: true } : args);\n if (edited) this.markEdited(edited);\n return result;\n };\n },\n });\n }\n}\n\n// ── Pure helpers ───────────────────────────────────────────────────────\n\nexport type Verdict = 'reproduces' | 'passes' | 'test_problem' | 'pending' | 'infra_error';\n\n// Playwright colours its expect() output.\n// eslint-disable-next-line no-control-regex\nconst ANSI = /\\u001b\\[[0-9;]*m/g;\nconst NOT_FOUND = /element\\(s\\) not found|resolved to 0 elements/i;\nconst ACTION_TIMEOUT = /Test timeout of \\d+ms exceeded|\\b(locator|page)\\.\\w+: Timeout \\d+ms exceeded/;\nconst INFRA = /net::ERR_|ECONNREFUSED|ECONNRESET|ENOTFOUND|browserType\\.launch|Target (page, context or browser|closed)|runner (lost|disconnected)/i;\nconst CHECK_MAX = 300;\n\nfunction clip(text: string, max = CHECK_MAX): string {\n return text.length > max ? `${text.slice(0, max - 1)}…` : text;\n}\n\n/** The failing check in one line: the expect() call and what it expected and received. */\nexport function checkLine(error: string): string {\n const lines = error.replace(ANSI, '').split('\\n').map((line) => line.trim()).filter(Boolean);\n const check = lines.find((line) => /\\bexpect\\(/.test(line)) ?? lines[0] ?? '';\n const expected = lines.find((line) => /^Expected\\b/.test(line) && line !== check);\n const received = lines.find((line) => /^Received\\b/.test(line));\n return clip([check, expected, received].filter(Boolean).join(' | '));\n}\n\n/** The matcher an expect() failure names, with its `not.`: \"toHaveCount\", \"not.toHaveURL\". */\nexport function failedMatcher(error: string): string | null {\n const m = /\\bexpect\\([^)]*\\)\\.(not\\.)?(to\\w+)\\(/.exec(error.replace(ANSI, ''));\n return m ? `${m[1] ?? ''}${m[2]}` : null;\n}\n\n// Playwright and Jest matchers only, so toString( / toFixed( / toLowerCase( in the test body are not taken for one.\nconst MATCHER = /\\.\\s*(not\\s*\\.\\s*)?(to(?:Be|Have|Equal|StrictEqual|Contain|Match|Throw|Pass)\\w*)\\s*\\(/g;\n\n// The code without its comments, in one pass. Strings are skipped whole, so a route glob ending in slash-star or\n// getByText('a//b') is code, not a comment. A slash after a backslash (an escaped slash in a regex) opens no\n// comment. A misread string can only leave a comment in; it never drops code.\nfunction stripComments(code: string): string {\n let out = '';\n let from = 0;\n for (let at = 0; at < code.length;) {\n const c = code[at];\n if (c === '\"' || c === \"'\" || c === '`') {\n at++;\n while (at < code.length && code[at] !== c && (c === '`' || code[at] !== '\\n')) at += code[at] === '\\\\' ? 2 : 1;\n at++;\n } else if (c === '/' && (code[at + 1] === '/' || code[at + 1] === '*') && code[at - 1] !== '\\\\') {\n out += code.slice(from, at);\n const end = code[at + 1] === '/' ? code.indexOf('\\n', at) : code.indexOf('*/', at + 2);\n if (end < 0) return out;\n at = from = code[at + 1] === '/' ? end : end + 2;\n } else {\n at++;\n }\n }\n return out + code.slice(from);\n}\n\n/**\n * The matcher of the last expect() in the test code (expect, expect.soft or expect.poll), the check the test was\n * written for. One linear pass: it finds matcher calls, not expect( openings, so a huge spec stays cheap.\n */\nexport function lastMatcher(code: string): string | null {\n let last: string | null = null;\n for (const m of stripComments(code).matchAll(MATCHER)) last = `${m[1] ? 'not.' : ''}${m[2]}`;\n return last;\n}\n\n// Checks that something is there at all (rows to read, a value to compare). One of these failing before the\n// last check is a precondition: the test never reached the bug. Any other earlier check failing (the bug check,\n// with a cleanup check after it) still reproduces. toBeVisible is not one: it is often the bug check itself (an\n// error message that should show), and a missing element is already a test problem through NOT_FOUND.\nconst PRESENCE = new Set(['toBeGreaterThan', 'toBeGreaterThanOrEqual', 'toBeTruthy', 'toBeDefined', 'not.toBeNull', 'not.toBeUndefined', 'not.toBeFalsy']);\n\n/**\n * Whether a finished run shows the bug its test was written for. With the test's code, a failed presence check\n * (no rows to check, a button that never showed) before the last check is a precondition and says nothing\n * about the bug.\n */\nexport function classifyRun(run: { status?: unknown; error?: unknown }, code?: string): { verdict: Verdict; check: string } {\n const status = String(run.status ?? '');\n const error = typeof run.error === 'string' ? run.error.replace(ANSI, '') : '';\n if (status === 'PASSED') return { verdict: 'passes', check: '' };\n if (status !== 'FAILED' && status !== 'ERROR') return { verdict: 'pending', check: '' };\n const check = error ? checkLine(error) : '';\n if (INFRA.test(error)) return { verdict: 'infra_error', check };\n const assertion = /\\bexpect\\(/.test(error) && /Expected/.test(error) && /Received/.test(error);\n if (status === 'FAILED' && assertion && !NOT_FOUND.test(error) && !ACTION_TIMEOUT.test(error)) {\n const failed = failedMatcher(error);\n const last = code ? lastMatcher(code) : null;\n if (failed && last && failed !== last && PRESENCE.has(failed)) {\n return { verdict: 'test_problem', check: clip(`Failed on an earlier check (${failed}), not the last one (${last}): ${check}`) };\n }\n return { verdict: 'reproduces', check };\n }\n if (!error && status === 'ERROR') return { verdict: 'infra_error', check };\n return { verdict: 'test_problem', check };\n}\n\nconst VERDICT_NEXT: Record<Verdict, string> = {\n reproduces: 'Locked in: the test fails on the bug and will pass once it is fixed.',\n passes: 'The test passed, so it does not show the bug yet; that does not mean the bug is gone. Check it again in your browser: if you still see it, the test checks the wrong thing (assert the rule you saw broken over all the rows, not one record), fix it with edit_test_code and collect again. delete_test only when you can no longer see the bug.',\n test_problem: 'The test broke before its last check (timeout, locator not found, a failed precondition, script error), so it never compared the value. Fix how it reaches and reads the value: look at the page in your browser, use the exact label or text you see there, and keep the compared value inside expect(), not in an await that can throw first. Keep the expected value as it is: it states what a correct app must show. Fix with edit_test_code, then collect_verdicts again.',\n pending: 'Still queued or running: call collect_verdicts again in a minute.',\n infra_error: 'The run could not reach the app or the browser died: call collect_verdicts with rerun for this test.',\n};\n\n// ── Tools ──────────────────────────────────────────────────────────────\n\ntype Result = { content: Array<{ type: 'text'; text: string }>; isError?: boolean };\n\nfunction ok(value: unknown): Result {\n return { content: [{ type: 'text', text: JSON.stringify(value, null, 2) }] };\n}\n\nfunction fail(err: unknown): Result {\n return { content: [{ type: 'text', text: toolErrorText(err) }], isError: true };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction list(value: unknown): Array<Record<string, unknown>> {\n return Array.isArray(value) ? value.filter(isRecord) : [];\n}\n\nconst SERVER_WAIT_MAX_SECONDS = 55;\nconst COLLECT_DEFAULT_SECONDS = 90;\nconst COLLECT_MAX_SECONDS = 120;\nconst TERMINAL = new Set(['PASSED', 'FAILED', 'ERROR']);\n// The read-only guard lint has two variants (marked read-only / production host); both end in \"but has no request guard\".\nconst CONVENTION_WARNING = /console\\.log\\(\\) milestones|__QA_HTTP__|but has no request guard/;\n\nconst CODE_RULES = [\n 'Assert the CORRECT behaviour, so the test fails now and passes once the bug is fixed; one bug per test',\n 'Relative URLs only (page.goto(\\'/orders\\')): the runner sets the base URL',\n 'Sign in inside the test with process.env.TEST_USER_EMAIL! and process.env.TEST_USER_PASSWORD!, never literal values',\n 'Locators you saw in your browser: getByRole(role, { name, exact: true }), getByLabel, getByTestId; prefer knownSelectors',\n 'End with an expect() on the value the app gets wrong, with the exact expected value',\n 'The test runs later on its own, on an account that has none of what your browser did: from sign-in, redo every step that made the state you saw (a report you generated, a check you started, a record you created or booked first) and then assert on the value the bug gets wrong, not on something that only exists after your earlier steps',\n];\n\n/** Pages from get_feature: its routes, plus any route its test plans add. */\nfunction featurePages(feature: Record<string, unknown>): string[] {\n const plans = isRecord(feature.testPlans) ? list(feature.testPlans.pages) : [];\n const routes = Array.isArray(feature.pages) ? feature.pages.filter((p): p is string => typeof p === 'string') : [];\n for (const plan of plans) {\n const route = String(plan.route ?? '');\n if (route && !routes.includes(route)) routes.push(route);\n }\n return routes;\n}\n\nasync function startHere(client: ValidateQAClient, featureName: string | undefined): Promise<unknown> {\n const warnings: string[] = [];\n const [features, variables, bugs] = await Promise.all([\n client.executeTool('list_features'),\n client.executeTool('get_variables').catch((err) => {\n warnings.push(`get_variables failed (${toolErrorText(err)}): credentials and baseUrl below are unknown, not missing. Call start_here again.`);\n return null;\n }),\n client.executeTool('list_bugs', { limit: 200 }).catch((err) => {\n warnings.push(`list_bugs failed (${toolErrorText(err)}): openBugs below is unknown, not empty. Call start_here again before filing bugs.`);\n return null;\n }),\n ]);\n if (isRecord(bugs) && bugs.appBugsTruncated === true) {\n warnings.push('list_bugs returned only part of the open bugs: openBugs below may be missing some.');\n }\n const featureList = isRecord(features) ? list(features.features) : [];\n const chosen = featureName?.trim()\n || (featureList.length === 1 ? String(featureList[0].name) : undefined);\n\n const vars = isRecord(variables) && isRecord(variables.variables) ? variables.variables : {};\n const names = Object.keys(vars);\n const baseUrl = isRecord(vars.BASE_URL) && vars.BASE_URL.secret !== true && typeof vars.BASE_URL.value === 'string'\n ? vars.BASE_URL.value\n : null;\n const credentials = {\n baseUrl,\n loginReady: names.includes('TEST_USER_EMAIL') && names.includes('TEST_USER_PASSWORD'),\n variables: names.map((name) => ({ name, secret: isRecord(vars[name]) && vars[name].secret === true })),\n };\n\n let feature: Record<string, unknown> | null = null;\n if (chosen) {\n try {\n const detail = await client.executeTool('get_feature', { featureName: chosen });\n if (isRecord(detail)) feature = detail;\n } catch (err) {\n warnings.push(`get_feature(\"${chosen}\") failed (${toolErrorText(err)}): the feature's pages, tests and selectors are missing below. Check the name against project.features and call start_here again.`);\n }\n }\n\n const suite = feature ? String(feature.name) : undefined;\n const openBugs = (isRecord(bugs) ? list(bugs.appBugs) : [])\n .filter((bug) => !suite || String(bug.suite ?? '').toLowerCase() === suite.toLowerCase())\n .map((bug) => ({\n testId: bug.id,\n name: bug.name,\n reviewStatus: bug.reviewStatus,\n check: isRecord(bug.evidence) ? bug.evidence.failingCheck ?? null : null,\n }));\n\n return {\n project: {\n features: featureList.map((f) => ({\n name: f.name, pages: f.pages, criticality: f.criticality, tests: f.existingTests, failing: f.failingTests,\n })),\n },\n credentials,\n ...(warnings.length > 0 ? { warnings } : {}),\n ...(feature\n ? {\n feature: {\n name: feature.name,\n description: feature.description,\n pages: featurePages(feature),\n tests: list(feature.tests).map((t) => ({ id: t.id, name: t.name, status: t.status, lastRunStatus: t.lastRunStatus ?? null })),\n knownSelectors: Array.isArray(feature.knownSelectors) ? feature.knownSelectors : [],\n ...(typeof feature.surveyNotes === 'string' ? { surveyNotes: feature.surveyNotes } : {}),\n },\n }\n : {}),\n openBugs,\n codeRules: CODE_RULES,\n next: [\n credentials.loginReady || !variables ? null : 'TEST_USER_EMAIL / TEST_USER_PASSWORD are not set: saved tests cannot sign in. Ask the developer for a test account.',\n 'Explore each page with your browser. Skip bugs already in openBugs.',\n 'For each bug: save_regression_test right away, then keep exploring. Do not wait for its run.',\n 'When you are done exploring: collect_verdicts. Fix test_problem tests with edit_test_code and collect again.',\n ].filter(Boolean),\n };\n}\n\nasync function waitForRun(client: ValidateQAClient, runId: string, deadline: number): Promise<Record<string, unknown>> {\n let view: unknown = null;\n for (;;) {\n const left = Math.floor((deadline - Date.now()) / 1000);\n view = left >= 1\n ? await client.executeTool('wait_for_run', { runId, timeoutSeconds: Math.min(left, SERVER_WAIT_MAX_SECONDS) })\n : await client.executeTool('get_test_run', { runId });\n if (!isRecord(view)) return {};\n if (TERMINAL.has(String(view.status)) || view.timedOut !== true || left < 1) return view;\n }\n}\n\nasync function collectVerdicts(\n client: ValidateQAClient,\n session: AgentSession,\n args: { testIds?: string[]; rerun?: string[]; timeoutSeconds?: number },\n): Promise<unknown> {\n const seconds = Math.min(Math.max(args.timeoutSeconds ?? COLLECT_DEFAULT_SECONDS, 0), COLLECT_MAX_SECONDS);\n const deadline = Date.now() + seconds * 1000;\n const ids = args.testIds?.length ? args.testIds : [...session.saved.keys()];\n if (ids.length === 0) {\n return { summary: {}, verdicts: [], next: 'Nothing saved in this session yet: save_regression_test first, or pass testIds.' };\n }\n\n // Runs to check: a fresh one for each edited or requested test, else the test's verification run, else its latest run.\n const rerun = new Set(args.rerun ?? []);\n const targets: Array<{ testId: string; runId?: string; error?: string }> = [];\n for (const testId of ids) {\n const saved = session.saved.get(testId);\n try {\n if (rerun.has(testId) || saved?.edited) {\n const queued = await client.executeTool('run_test', { testId });\n const runId = isRecord(queued) && typeof queued.runId === 'string' ? queued.runId : undefined;\n if (saved) { saved.edited = false; saved.runId = runId; }\n targets.push({ testId, runId });\n } else if (saved?.runId) {\n targets.push({ testId, runId: saved.runId });\n } else {\n const latest = await client.executeTool('get_test_run', { testId });\n targets.push({ testId, runId: isRecord(latest) && typeof latest.id === 'string' ? latest.id : undefined });\n }\n } catch (err) {\n targets.push({ testId, error: toolErrorText(err) });\n }\n }\n\n const verdicts: Array<Record<string, unknown>> = [];\n const summary: Record<Verdict, number> = { reproduces: 0, passes: 0, test_problem: 0, pending: 0, infra_error: 0 };\n for (const { testId, runId, error } of targets) {\n const saved = session.saved.get(testId);\n if (!runId) {\n summary.pending++;\n verdicts.push({ testId, name: saved?.name, verdict: 'pending', check: error ?? 'no run yet', next: VERDICT_NEXT.pending });\n continue;\n }\n let view: Record<string, unknown>;\n try {\n view = await waitForRun(client, runId, deadline);\n } catch (err) {\n summary.infra_error++;\n verdicts.push({ testId, name: saved?.name, runId, verdict: 'infra_error', check: toolErrorText(err), next: VERDICT_NEXT.infra_error });\n continue;\n }\n // The code tells a failed precondition from the bug check. validate.qa's own triage rides along: after a\n // failure the healer may quarantine the test as an app bug, or rewrite it.\n let test: Record<string, unknown> = {};\n if (TERMINAL.has(String(view.status))) {\n test = await client.executeTool('get_test_code', { testId }).then((t) => (isRecord(t) ? t : {}), () => ({}));\n }\n const { verdict, check } = classifyRun(view, typeof test.playwrightCode === 'string' ? test.playwrightCode : undefined);\n summary[verdict]++;\n const tags = Array.isArray(test.tags) ? test.tags : [];\n verdicts.push({\n testId,\n name: saved?.name ?? view.testName ?? test.name,\n ...(saved?.bug ? { bug: saved.bug } : {}),\n runId,\n verdict,\n ...(check ? { check } : {}),\n ...(typeof test.status === 'string' ? { testStatus: test.status } : {}),\n ...(tags.includes('@bug-found') ? { triagedAsAppBug: true } : {}),\n next: VERDICT_NEXT[verdict],\n });\n }\n\n const next = summary.pending > 0\n ? 'Some runs are still going: call collect_verdicts again.'\n : summary.test_problem + summary.passes + summary.infra_error > 0\n ? 'Keep the reproduces tests as they are. Fix the others (see each next), then collect_verdicts again.'\n : 'Every saved test reproduces its bug.';\n return { summary, verdicts, next };\n}\n\nexport function registerAgentTools(server: McpServer, client: ValidateQAClient, session: AgentSession) {\n server.tool('start_here',\n 'START HERE when asked to test, find bugs in or lock in a feature. One read with everything to begin: the project\\'s features, the feature\\'s pages, its existing tests and knownSelectors (verified locators), whether test credentials are set, open bugs, and the rules for saved test code.',\n { feature: z.string().optional().describe('The feature to work on (case-insensitive). Leave out to list the features.') },\n { readOnlyHint: true },\n async (args: { feature?: string }) => {\n try {\n return ok(await startHere(client, args.feature));\n } catch (err) {\n return fail(err);\n }\n },\n );\n\n server.tool('save_regression_test',\n 'Save a bug you found as a Playwright test that asserts the CORRECT behaviour (it fails now and passes once fixed). Returns at once: the test runs in the cloud while you keep exploring. Call collect_verdicts at the end to learn which tests reproduce their bug. It runs later on an account with none of your browser\\'s changes, so it redoes every step from sign-in. Same code rules as create_test (see start_here codeRules).',\n {\n name: z.string().describe('What should happen, e.g. \"Orders - status filter shows only Paid orders\"'),\n feature: z.string().describe('The feature name exactly as start_here lists it (becomes the suite)'),\n playwrightCode: z.string().describe('Full Playwright test file'),\n bug: z.string().optional().describe('One line: what the app does wrong'),\n tags: z.array(z.string()).optional().describe('Extra tags; @regression is added'),\n },\n async (args: { name: string; feature: string; playwrightCode: string; bug?: string; tags?: string[] }) => {\n try {\n const extra = args.tags ?? [];\n // @auth tests may not carry @regression (create_test rule).\n const tags = extra.includes('@auth') ? extra : [...new Set([...extra, '@regression'])];\n // assertsBug: the code is meant to fail until the bug is fixed, so the server lints it as a bug test\n // (no re-read or stale-after-submit advisory) without storing @bug-found.\n const created = await client.executeTool('create_test', {\n name: args.name, playwrightCode: args.playwrightCode, suite: args.feature, tags, assertsBug: true,\n });\n if (!isRecord(created) || typeof created.id !== 'string') return ok(created);\n const runId = isRecord(created.verification) && typeof created.verification.runId === 'string'\n ? created.verification.runId\n : undefined;\n session.remember({ testId: created.id, name: args.name, bug: args.bug, runId, edited: false });\n // Copilot conventions (progress logs, network log lines, the production read-only guard) are not about the bug.\n const warnings = (Array.isArray(created.warnings) ? created.warnings : []).filter((w) => typeof w === 'string' && !CONVENTION_WARNING.test(w));\n return ok({\n testId: created.id,\n name: created.name ?? args.name,\n status: created.status,\n ...(runId ? { runId } : {}),\n ...(warnings.length ? { warnings } : {}),\n next: warnings.length\n ? 'Saved; its run is queued. Fix the warnings with edit_test_code, then keep exploring. collect_verdicts at the end.'\n : 'Saved; its run is queued. Keep exploring. collect_verdicts at the end.',\n });\n } catch (err) {\n return fail(err);\n }\n },\n );\n\n server.tool('collect_verdicts',\n 'After exploring: wait for the runs of the tests you saved this session (or testIds) and say for each whether it reproduces its bug (fails on its check), passes (does not show the bug), has a test problem (broke before its check), is still pending, or hit an infrastructure error. Tests changed with edit_test_code since their run are run again first. Waits up to timeoutSeconds in total. A test that fails on its last check is the goal: it reproduces the bug, so keep it as it is. A test that passes is the one to question.',\n {\n testIds: z.array(z.string()).optional().describe('Tests to check (default: every test saved this session)'),\n rerun: z.array(z.string()).optional().describe('Tests to run again before checking'),\n timeoutSeconds: z.number().optional().describe(`Total wait (default ${COLLECT_DEFAULT_SECONDS}, max ${COLLECT_MAX_SECONDS})`),\n },\n async (args: { testIds?: string[]; rerun?: string[]; timeoutSeconds?: number }) => {\n try {\n return ok(await collectVerdicts(client, session, args));\n } catch (err) {\n return fail(err);\n }\n },\n );\n}\n\nexport const AGENT_INSTRUCTIONS = `Validate.QA saves the bugs you find as Playwright tests that run in its cloud and keep running.\n\nFinding and locking in bugs in a feature:\n1. start_here({ feature }): its pages, existing tests, knownSelectors, credentials, and open bugs.\n2. Explore the pages with your browser tools.\n3. Each bug: save_regression_test with code that asserts the CORRECT behaviour. It runs later on an account with none of your browser's changes, so from sign-in it redoes every click that made the state you saw. It returns at once; keep exploring.\n4. At the end: collect_verdicts. reproduces (the test fails on its check) = the bug is locked in: keep that test as it is. passes = the test misses the bug: check again in your browser and fix its assertion. test_problem = it broke before its check: fix the steps. Fix with edit_test_code, then collect_verdicts again.\nRate limit: 50 calls a minute.`;\n"],"mappings":"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","names":["mkdirSync","readFileSync","writeFileSync","resolve","join","config","existsSync","mkdirSync","readFileSync","writeFileSync","join","resolve","readFileSync","tool","z","server","client","tool","server","client","SERVER_WAIT_MAX_SECONDS","isRecord","z","runInit","runInitAgents"]}