swipium 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +7 -8
  2. package/dist/appMap/featureIndex.js +183 -0
  3. package/dist/appMap/featureIndex.js.map +1 -0
  4. package/dist/featureTesting/executionBootstrap.js +14 -10
  5. package/dist/featureTesting/executionBootstrap.js.map +1 -1
  6. package/dist/featureTesting/featureMap.js +53 -0
  7. package/dist/featureTesting/featureMap.js.map +1 -0
  8. package/dist/featureTesting/featureScope.js +354 -0
  9. package/dist/featureTesting/featureScope.js.map +1 -0
  10. package/dist/featureTesting/mapFeatureScope.js +143 -0
  11. package/dist/featureTesting/mapFeatureScope.js.map +1 -0
  12. package/dist/featureTesting/objectiveModel.js +129 -0
  13. package/dist/featureTesting/objectiveModel.js.map +1 -0
  14. package/dist/featureTesting/resultMerge.js +112 -0
  15. package/dist/featureTesting/resultMerge.js.map +1 -0
  16. package/dist/featureTesting/sources.js +96 -0
  17. package/dist/featureTesting/sources.js.map +1 -0
  18. package/dist/featureTesting/suiteBridge.js +51 -0
  19. package/dist/featureTesting/suiteBridge.js.map +1 -0
  20. package/dist/featureTesting/synonyms.js +73 -0
  21. package/dist/featureTesting/synonyms.js.map +1 -0
  22. package/dist/featureTesting/testCaseFactory.js +107 -0
  23. package/dist/featureTesting/testCaseFactory.js.map +1 -0
  24. package/dist/featureTesting/testPlan.js +50 -0
  25. package/dist/featureTesting/testPlan.js.map +1 -0
  26. package/dist/server.js +11 -0
  27. package/dist/server.js.map +1 -1
  28. package/dist/tools/build.js +172 -0
  29. package/dist/tools/build.js.map +1 -0
  30. package/dist/tools/bundletool.js +145 -0
  31. package/dist/tools/bundletool.js.map +1 -0
  32. package/dist/tools/capabilities.js +20 -0
  33. package/dist/tools/capabilities.js.map +1 -1
  34. package/dist/tools/featureTesting.js +344 -0
  35. package/dist/tools/featureTesting.js.map +1 -0
  36. package/dist/tools/resolveArtifact.js +80 -0
  37. package/dist/tools/resolveArtifact.js.map +1 -0
  38. package/dist/tools/resolveTarget.js +67 -0
  39. package/dist/tools/resolveTarget.js.map +1 -0
  40. package/dist/version.js +12 -2
  41. package/dist/version.js.map +1 -1
  42. package/docs/mcp-server.md +1 -1
  43. package/docs/tools.md +41 -3
  44. package/package.json +1 -1
@@ -0,0 +1,344 @@
1
+ // qa_feature_scope / qa_feature_test_plan / qa_test_feature (SWIPIUM-REQ-03 "MCP Tool Requirements").
2
+ // Feature-focused testing from the durable app map: map a natural-language feature request to code +
3
+ // runtime + existing tests, model its objective, generate scoped cases, optionally execute a focused
4
+ // run, and update the feature map. Thin wrappers — all logic lives in src/featureTesting/* + src/appMap.
5
+ import { z } from 'zod';
6
+ import { qaOk, qaError } from '../lib/result.js';
7
+ import { qaNeedsInput } from '../lib/needsInput.js';
8
+ import { getDriver } from '../session/attach.js';
9
+ import { startProgress } from '../session/progress.js';
10
+ import { log } from '../lib/logger.js';
11
+ import { runExplore } from '../explore/runner.js';
12
+ import { generateSessionReport } from '../services/report.js';
13
+ import { buildFeatureIndex } from '../appMap/featureIndex.js';
14
+ import { buildFeatureScope } from '../featureTesting/featureScope.js';
15
+ import { buildMapFeatureScope } from '../featureTesting/mapFeatureScope.js';
16
+ import { buildObjective } from '../featureTesting/objectiveModel.js';
17
+ import { buildFeatureTestPlan } from '../featureTesting/testPlan.js';
18
+ import { generateFeatureTestCases } from '../featureTesting/testCaseFactory.js';
19
+ import { mergeFeatureRun } from '../featureTesting/resultMerge.js';
20
+ import { upsertFeatureCoverage, findFeatureCoverage } from '../featureTesting/featureMap.js';
21
+ import { featureCasesToCanonical } from '../featureTesting/suiteBridge.js';
22
+ import { bootstrapFeatureExecution } from '../featureTesting/executionBootstrap.js';
23
+ import { runtimeScreensFromGraph, loadGraphFromFile, gatherExistingTests } from '../featureTesting/sources.js';
24
+ import { buildAppMap } from '../appMap/build.js';
25
+ import { applyMerge } from '../testSuite/store.js';
26
+ const EMPTY_INDEX = { root: '', symbols: [], routes: [], files: [], scannedFiles: 0, truncated: false };
27
+ /** Resolve a feature scope + objective from a session or a project root (read-only, no device). */
28
+ async function resolveContext(server, sessions, args) {
29
+ let session = args.sessionId ? sessions.get(args.sessionId) : undefined;
30
+ if (args.sessionId && !session) {
31
+ return { ok: false, result: qaError({ what: `Unknown sessionId ${args.sessionId}`, changedState: false, retrySafe: true, nextSteps: ['Call qa_start_session / qa_test_this first, or pass projectRoot.'] }) };
32
+ }
33
+ let root = session?.root;
34
+ if (!root) {
35
+ const { resolveProjectRoot } = await import('../context/projectRoot.js');
36
+ const resolved = await resolveProjectRoot(server, args.projectRoot);
37
+ if (!resolved.root) {
38
+ return { ok: false, result: qaError({ what: 'Could not resolve a project root', changedState: false, retrySafe: true, nextSteps: ['Pass projectRoot="/abs/path" or a sessionId.'], clientHint: resolved.hint }) };
39
+ }
40
+ root = resolved.root;
41
+ }
42
+ const appId = session?.appId;
43
+ const includeCode = args.includeCode !== false;
44
+ const index = includeCode ? buildFeatureIndex(root) : { ...EMPTY_INDEX, root };
45
+ // Runtime screens from the session's latest exploration graph (if any). When there is no live graph
46
+ // the app-map-first scope service falls back to the durable map's historical runtime screens.
47
+ let runtimeScreens = [];
48
+ if (session?.exploration?.graphUri) {
49
+ const found = sessions.findArtifact(session.exploration.graphUri);
50
+ runtimeScreens = runtimeScreensFromGraph(loadGraphFromFile(found?.rec.path));
51
+ }
52
+ const existingTests = gatherExistingTests(root);
53
+ // App-map FIRST (Vision Gap Fix 3): scope from .swipium/app-map.json (features, topology, tickets,
54
+ // persistent suite), falling back to the fresh code index only to fill gaps.
55
+ const scopeResult = buildMapFeatureScope({ root, query: args.feature, index, runtimeScreens, existingTests, platform: args.platform, limit: args.limit });
56
+ const objective = buildObjective(scopeResult.primary);
57
+ return { ok: true, ctx: { session, root, appId, scopeResult, objective, index, appMapUri: scopeResult.appMapUri } };
58
+ }
59
+ export function registerFeatureTesting(server, sessions) {
60
+ // ---- qa_feature_scope (read-only) ----------------------------------------------------------
61
+ server.registerTool('qa_feature_scope', {
62
+ title: 'Feature scope (read-only)',
63
+ description: 'Map a natural-language feature request (e.g. "weather analysis feature") to the app: ranked code symbols, screens/routes, runtime screens, existing tests, an inferred objective model, risks, coverage gaps, and a recommended test strategy. READ-ONLY — no device, no mutation. Queries the static code index + the latest exploration screen graph + authored flows/test cases. Returns ALL plausible feature candidates with confidence, and asks ONE disambiguation question only when genuinely-different features tie. Pass a sessionId (preferred — adds runtime evidence) or a projectRoot.',
64
+ inputSchema: {
65
+ sessionId: z.string().optional().describe('Session to scope against (adds runtime screen-graph evidence).'),
66
+ projectRoot: z.string().optional().describe('Project root when no session exists (static code scope only).'),
67
+ feature: z.string().describe('The feature to scope, in natural language (e.g. "weather analysis").'),
68
+ platform: z.enum(['android', 'ios']).optional(),
69
+ includeCode: z.boolean().optional().describe('Scan source for code-aware matches (default true).'),
70
+ limit: z.number().optional().describe('Max items per list in the scope (default 8).'),
71
+ },
72
+ }, async ({ sessionId, projectRoot, feature, platform, includeCode, limit }) => {
73
+ const r = await resolveContext(server, sessions, { sessionId, projectRoot, feature, platform, includeCode, limit });
74
+ if (!r.ok)
75
+ return r.result;
76
+ const { scopeResult, objective, index } = r.ctx;
77
+ const scope = scopeResult.primary;
78
+ if (!scopeResult.found) {
79
+ return qaOk({
80
+ sessionId: sessionId ?? null,
81
+ feature,
82
+ found: false,
83
+ scope,
84
+ searched: scopeResult.searched,
85
+ nextRecommendedAction: { tool: 'qa_test_this', args: { ...(sessionId ? { sessionId } : { projectRoot: r.ctx.root }), goal: 'explore' }, why: 'Grow the map with an initial run, then re-scope the feature' },
86
+ }, `🔎 No feature matched "${feature}". Searched ${scopeResult.searched.symbols} symbols, ${scopeResult.searched.routes} routes, ${scopeResult.searched.files} files, ${scopeResult.searched.runtimeScreens} runtime screens with terms: ${scopeResult.searched.terms.slice(0, 12).join(', ')}. Run qa_test_this/qa_explore to grow coverage, or refine the feature name.`);
87
+ }
88
+ if (scopeResult.needsInput) {
89
+ return qaNeedsInput({
90
+ needsInput: true,
91
+ kind: 'monorepo_target',
92
+ question: scopeResult.needsInput.question,
93
+ fields: [{ name: 'feature', description: 'The exact feature to test', example: scopeResult.candidates[0]?.title }],
94
+ fallbackOptions: scopeResult.needsInput.options,
95
+ resume: { tool: 'qa_feature_scope', args: {} },
96
+ attempted: [`scoped "${feature}" — matched ${scopeResult.candidates.length} distinct candidates that tie`],
97
+ ifDeclined: 'Swipium scopes the highest-confidence candidate and records the others as alternatives.',
98
+ }, { sessionId: sessionId ?? undefined, candidates: scopeResult.candidates });
99
+ }
100
+ const nextRecommendedAction = scope.recommendedStrategy === 'manual_blocked'
101
+ ? { tool: 'qa_feature_test_plan', args: { ...(sessionId ? { sessionId } : { projectRoot: r.ctx.root }), feature }, why: 'Review the plan + setup needed before any automated execution' }
102
+ : { tool: 'qa_test_feature', args: { sessionId: sessionId ?? '${sessionId}', feature, mode: 'execute' }, why: 'Run a focused test of this feature' };
103
+ return qaOk({
104
+ sessionId: sessionId ?? null,
105
+ feature,
106
+ found: true,
107
+ scope,
108
+ objective,
109
+ appMapUri: r.ctx.appMapUri,
110
+ mapFeatureId: scopeResult.mapFeatureId ?? null,
111
+ ticketRefs: scopeResult.ticketRefs,
112
+ runtimeSource: scopeResult.runtimeSource,
113
+ candidates: scopeResult.candidates,
114
+ searched: scopeResult.searched,
115
+ codeIndex: { scannedFiles: index.scannedFiles, truncated: index.truncated },
116
+ nextRecommendedAction,
117
+ }, `🔎 ${scope.title} (confidence ${Math.round(scope.confidence * 100)}%, strategy ${scope.recommendedStrategy}) — ` +
118
+ `${scope.staticScreens.length} static screen(s), ${scope.runtimeScreens.length} runtime screen(s), ${scope.functions.length} symbol(s), ${scope.existingTests.length} existing test(s).` +
119
+ (scopeResult.candidates.length > 1 ? ` ${scopeResult.candidates.length} candidate(s).` : ''));
120
+ });
121
+ // ---- qa_feature_test_plan (read-only) ------------------------------------------------------
122
+ server.registerTool('qa_feature_test_plan', {
123
+ title: 'Feature test plan (read-only)',
124
+ description: 'Generate a read-only test plan for a feature: the resolved scope + objective, generated test cases (linked to featureId + map screens), required fixtures, risks, automation readiness, and the EXACT ordered execution plan. Useful before execution and for PR review. No device, no mutation.',
125
+ inputSchema: {
126
+ sessionId: z.string().optional(),
127
+ projectRoot: z.string().optional(),
128
+ feature: z.string().describe('The feature to plan, in natural language.'),
129
+ platform: z.enum(['android', 'ios']).optional(),
130
+ creativity: z.enum(['conservative', 'standard', 'creative', 'adversarial']).optional().describe('Case ambition: conservative=happy path; standard(default)=+validation/empty/error; creative=+boundary/offline/interruption; adversarial=+destructive (consent-gated).'),
131
+ allowAdversarial: z.boolean().optional().describe('Include destructive/high-impact cases (requires consent + disposable state at run time). Default false.'),
132
+ includeCode: z.boolean().optional(),
133
+ limit: z.number().optional(),
134
+ },
135
+ }, async ({ sessionId, projectRoot, feature, platform, creativity, allowAdversarial, includeCode, limit }) => {
136
+ const r = await resolveContext(server, sessions, { sessionId, projectRoot, feature, platform, includeCode, limit });
137
+ if (!r.ok)
138
+ return r.result;
139
+ const { scopeResult, objective } = r.ctx;
140
+ if (!scopeResult.found) {
141
+ return qaOk({ sessionId: sessionId ?? null, feature, found: false, searched: scopeResult.searched }, `No feature matched "${feature}" — nothing to plan. Searched terms: ${scopeResult.searched.terms.slice(0, 12).join(', ')}.`);
142
+ }
143
+ const plan = buildFeatureTestPlan(scopeResult.primary, objective, { creativity: creativity, platform, allowAdversarial, sessionId });
144
+ return qaOk({ sessionId: sessionId ?? null, feature, found: true, plan, candidates: scopeResult.candidates }, `📋 ${plan.title} plan: ${plan.cases.length} case(s), readiness ${plan.automationReadiness}, ${plan.requiredFixtures.length} fixture(s)${plan.blockers.length ? `, ${plan.blockers.length} blocker(s)` : ''}.`);
145
+ });
146
+ // ---- qa_test_feature (plan | execute | interactive) ----------------------------------------
147
+ server.registerTool('qa_test_feature', {
148
+ title: 'Test a feature (focused)',
149
+ description: 'Run a focused test of a named feature. mode:"plan" (default) returns the scope + objective + generated cases + execution plan (read-only). mode:"execute" runs a focused exploration toward the feature\'s best entry point as a background JOB — it generates cases, records pass/fail/blocked per case, updates the durable feature map, and generates a report (poll qa_job_status for the terminal result: reportUri, map delta, cases, blockers). Execute requires a prepared device session (run qa_test_this { mode:"execute" } first). Honest: a feature gated by auth/paywall/permission/missing-fixture returns blocked with setup guidance, not a false failure.',
150
+ inputSchema: {
151
+ sessionId: z.string().optional().describe('Prepared session. If omitted in execute/interactive, Swipium bootstraps a device from projectRoot (same path as qa_test_this).'),
152
+ projectRoot: z.string().optional().describe('Project root — used for plan mode and to bootstrap a device in execute/interactive when no sessionId is given.'),
153
+ feature: z.string().describe('The feature to test, in natural language.'),
154
+ mode: z.enum(['plan', 'execute', 'interactive']).optional().describe('plan (default) | execute (focused run as a job) | interactive (run until the first question).'),
155
+ platform: z.enum(['android', 'ios']).optional(),
156
+ device: z.string().optional().describe('Specific device/simulator serial or udid to prepare when bootstrapping from projectRoot.'),
157
+ consentId: z.string().optional().describe('Consent id for the privileged boot/install/launch steps when bootstrapping a device.'),
158
+ approve: z.boolean().optional().describe('Approve the bootstrap consent request (paired with consentId).'),
159
+ creativity: z.enum(['conservative', 'standard', 'creative', 'adversarial']).optional(),
160
+ allowAdversarial: z.boolean().optional(),
161
+ maxScreens: z.number().optional().describe('Max distinct screens for the focused exploration (default 8).'),
162
+ maxActions: z.number().optional().describe('Max actions for the focused exploration (default 20).'),
163
+ timeoutMs: z.number().optional(),
164
+ generateCases: z.boolean().optional().describe('Generate test cases (default true).'),
165
+ includeCode: z.boolean().optional(),
166
+ limit: z.number().optional(),
167
+ },
168
+ }, async (args) => {
169
+ const { sessionId, projectRoot, feature, mode, platform, creativity, allowAdversarial, maxScreens, maxActions, generateCases, includeCode, limit } = args;
170
+ const effectiveMode = mode ?? 'plan';
171
+ // PLAN mode: pure, read-only — same as qa_feature_test_plan but under the action tool name.
172
+ if (effectiveMode === 'plan') {
173
+ const r = await resolveContext(server, sessions, { sessionId, projectRoot, feature, platform, includeCode, limit });
174
+ if (!r.ok)
175
+ return r.result;
176
+ const { scopeResult, objective } = r.ctx;
177
+ if (!scopeResult.found)
178
+ return qaOk({ sessionId: sessionId ?? null, feature, found: false, searched: scopeResult.searched }, `No feature matched "${feature}".`);
179
+ if (scopeResult.needsInput) {
180
+ return qaNeedsInput({ needsInput: true, kind: 'monorepo_target', question: scopeResult.needsInput.question, fields: [{ name: 'feature', example: scopeResult.candidates[0]?.title }], fallbackOptions: scopeResult.needsInput.options, resume: { tool: 'qa_test_feature', args: { mode: 'plan' } }, attempted: [`scoped "${feature}" — multiple candidates tie`], ifDeclined: 'Swipium plans the highest-confidence candidate.' }, { sessionId: sessionId ?? undefined, candidates: scopeResult.candidates });
181
+ }
182
+ const plan = buildFeatureTestPlan(scopeResult.primary, objective, { creativity: creativity, platform, allowAdversarial, sessionId, maxScreens, maxActions });
183
+ return qaOk({ sessionId: sessionId ?? null, feature, mode: 'plan', found: true, plan }, `📋 ${plan.title}: ${plan.cases.length} case(s), readiness ${plan.automationReadiness}. To run it: qa_test_feature { mode:"execute" }.`);
184
+ }
185
+ // EXECUTE / INTERACTIVE: reuse a prepared session, else BOOTSTRAP one from projectRoot using
186
+ // the same resolver/planner/prepare path as qa_test_this (Fix Group 4) — so "test the weather
187
+ // feature" works even when the first instruction is feature-focused.
188
+ let session = sessionId ? sessions.get(sessionId) : undefined;
189
+ if (sessionId && !session) {
190
+ return qaError({ what: `Unknown sessionId ${sessionId}`, changedState: false, retrySafe: true, nextSteps: ['Omit sessionId to bootstrap from projectRoot, or pass a valid session.'] });
191
+ }
192
+ let driver = session ? (await getDriver(session)).driver : undefined;
193
+ if (!session || !driver) {
194
+ const boot = await bootstrapFeatureExecution({ server, sessions, projectRoot, feature, platform, device: args.device, consentId: args.consentId, approve: args.approve });
195
+ if (!boot.ok)
196
+ return boot.result;
197
+ session = boot.session;
198
+ driver = boot.driver;
199
+ }
200
+ const r = await resolveContext(server, sessions, { sessionId: session.id, feature, platform, includeCode, limit });
201
+ if (!r.ok)
202
+ return r.result;
203
+ const { scopeResult } = r.ctx;
204
+ if (!scopeResult.found) {
205
+ return qaOk({ sessionId: session.id, feature, mode: effectiveMode, found: false, searched: scopeResult.searched }, `No feature matched "${feature}" — nothing to execute. Searched: ${scopeResult.searched.terms.slice(0, 10).join(', ')}.`);
206
+ }
207
+ if (scopeResult.needsInput) {
208
+ return qaNeedsInput({ needsInput: true, kind: 'monorepo_target', question: scopeResult.needsInput.question, fields: [{ name: 'feature', example: scopeResult.candidates[0]?.title }], fallbackOptions: scopeResult.needsInput.options, resume: { tool: 'qa_test_feature', args: { sessionId: session.id, mode: effectiveMode } }, attempted: [`scoped "${feature}" — multiple candidates tie`], ifDeclined: 'Swipium tests the highest-confidence candidate.' }, { sessionId: session.id, candidates: scopeResult.candidates });
209
+ }
210
+ const job = sessions.createJob(session, `test_feature:${effectiveMode}`);
211
+ void runFeatureTestJob(sessions, session, job, {
212
+ feature,
213
+ scopeResult,
214
+ objective: r.ctx.objective,
215
+ mode: effectiveMode,
216
+ platform,
217
+ creativity: creativity,
218
+ allowAdversarial: !!allowAdversarial,
219
+ maxScreens,
220
+ maxActions,
221
+ generateCases: generateCases !== false,
222
+ stopOnAuth: effectiveMode === 'interactive',
223
+ });
224
+ return qaOk({ sessionId: session.id, state: 'running', mode: effectiveMode, jobId: job.jobId, kind: job.kind, feature, featureId: scopeResult.primary.featureId, appMapUri: r.ctx.appMapUri, mapFeatureId: scopeResult.mapFeatureId ?? null }, `🎯 focused test of "${scopeResult.primary.title}" started as job ${job.jobId}. Poll qa_job_status { sessionId:"${session.id}", jobId:"${job.jobId}" } for the report, map delta, cases, and blockers.`);
225
+ });
226
+ }
227
+ /** Focused feature run: targeted exploration → record cases → merge into the feature map → report. */
228
+ async function runFeatureTestJob(sessions, session, job, a) {
229
+ const signal = sessions.abortSignal(session, job.jobId);
230
+ const upd = (patch) => sessions.updateJobIfRunning(session, job, patch);
231
+ const prog = startProgress(sessions, session, job, 'exploring', { statusText: `Focusing tests on "${a.scopeResult.primary.title}".`, nextExpected: 'Navigate to the feature and exercise it.' });
232
+ const scope = a.scopeResult.primary;
233
+ const notesBefore = session.notes.length;
234
+ try {
235
+ const { driver } = await getDriver(session);
236
+ if (!driver) {
237
+ upd({ status: 'failed', error: 'no driver', resultText: '❌ no driver bound', endedAt: Date.now() });
238
+ return;
239
+ }
240
+ // 1. Targeted exploration toward the feature objective (prioritizes feature-named screens).
241
+ const ex = await runExplore(sessions, session, driver, { goal: scope.title, strategy: 'hybrid', stopOnAuth: a.stopOnAuth, maxScreens: a.maxScreens ?? 8, maxActions: a.maxActions ?? 20 }, { signal, onProgress: (p) => prog.event(p) });
242
+ if (signal?.aborted)
243
+ return;
244
+ // 2. Persist the screen graph artifact + harvest runtime screens visited.
245
+ const at = Date.now();
246
+ const generatedAt = new Date(at).toISOString();
247
+ const serialized = ex.graph.serialize(generatedAt);
248
+ const graphUri = sessions.saveArtifact(session, 'explore', `feature-graph-${at}.json`, JSON.stringify(serialized, null, 2), 'application/json', `feature graph: ${scope.title}`);
249
+ sessions.setExploration(session, { at, graphUri, state: ex.state, stoppedReason: ex.stoppedReason, summary: ex.summary });
250
+ prog.done(`explored ${ex.summary.screensVisited} screens for ${scope.title}`);
251
+ // Re-scope against the fresh runtime graph so cases link to the newly-observed screens.
252
+ const runtimeScreens = runtimeScreensFromGraph(serialized);
253
+ const rescoped = buildFeatureScope({ query: a.feature, index: { root: session.root, symbols: [], routes: [], files: [], scannedFiles: 0, truncated: false }, runtimeScreens, existingTests: scope.existingTests, platform: a.platform });
254
+ const liveScope = rescoped.found ? { ...scope, runtimeScreens: rescoped.primary.runtimeScreens } : scope;
255
+ // 3. Generate cases + merge the run's notes/blockers into them and the feature map.
256
+ const cases = a.generateCases ? generateFeatureTestCases(liveScope, a.objective, { creativity: a.creativity, platform: a.platform, allowAdversarial: a.allowAdversarial }) : [];
257
+ const newNotes = session.notes.slice(notesBefore);
258
+ const mergeNotes = newNotes.map((n) => ({ workflow: n.workflow, outcome: n.outcome, reason: n.reason, recommendedSetup: n.recommendedSetup, artifactUris: n.artifactUris }));
259
+ const visited = serialized.nodes.map((n) => n.id);
260
+ const blocked = ex.state === 'blocked' || !!ex.needsInput;
261
+ const blockReason = ex.needsInput?.question ?? (blocked ? ex.stoppedReason : undefined);
262
+ const prior = findFeatureCoverage(session.root, session.appId, liveScope.featureId);
263
+ const merge = mergeFeatureRun(liveScope, cases, { notes: mergeNotes, visitedRuntimeScreens: visited, evidence: [graphUri], exploration: { state: ex.state, stoppedReason: ex.stoppedReason, screensVisited: ex.summary.screensVisited }, blocked, blockReason, blockGuidance: ex.needsInput?.ifDeclined }, prior);
264
+ // 4. Refresh the DERIVED feature-coverage cache (Fix 11 — disposable; the durable truth is the app
265
+ // map + persistent suite written below) + a readable cases artifact.
266
+ const mapPath = upsertFeatureCoverage(session.root, session.appId, merge.coverage);
267
+ const casesUri = sessions.saveArtifact(session, 'feature', `cases-${liveScope.featureId}-${at}.json`, JSON.stringify({ featureId: liveScope.featureId, title: liveScope.title, objective: a.objective, cases: merge.cases }, null, 2), 'application/json', `feature cases: ${liveScope.title}`);
268
+ // 4a. SOURCE-OF-TRUTH UPDATES (Fix Group 3): fold the focused run into the durable App Knowledge
269
+ // Map (runtime↔static links + feature coverage) and the persistent test suite, so canonical
270
+ // facts never live only under .swipium/feature-map.
271
+ let appMapUri;
272
+ try {
273
+ const built = buildAppMap(session.root, { mode: 'runtime_merge', at: generatedAt, sessionId: session.id, exploreGraph: serialized, persist: true });
274
+ appMapUri = built.save?.resourceUri;
275
+ }
276
+ catch (e) {
277
+ log('warn', 'feature test app-map update failed', { jobId: job.jobId, err: String(e) });
278
+ }
279
+ let suiteWritten = [];
280
+ try {
281
+ const canonical = featureCasesToCanonical(merge.cases, liveScope, generatedAt, a.platform);
282
+ if (canonical.length) {
283
+ const applied = applyMerge(session.root, canonical, { source: 'feature', mode: 'update', now: generatedAt, runId: `feature-${at}`, sourceUri: casesUri }, session.appId);
284
+ suiteWritten = applied.written;
285
+ }
286
+ }
287
+ catch (e) {
288
+ log('warn', 'feature test suite merge failed', { jobId: job.jobId, err: String(e) });
289
+ }
290
+ // 5. Report.
291
+ let reportUri;
292
+ try {
293
+ const rep = await generateSessionReport(sessions, session, { format: 'summary', includeCurrentDump: true });
294
+ reportUri = rep.reportUri;
295
+ }
296
+ catch (e) {
297
+ log('warn', 'feature test report generation failed', { err: String(e) });
298
+ }
299
+ const state = blocked ? 'blocked' : merge.delta.casesFailed > 0 ? 'completed_with_findings' : 'completed';
300
+ const nextRecommendedAction = blocked
301
+ ? { tool: 'qa_continue_from_blocker', args: { sessionId: session.id, kind: 'credentials' }, why: 'Provide setup to unblock the feature, then re-run qa_test_feature' }
302
+ : reportUri
303
+ ? { tool: 'qa_get_artifact', args: { uri: reportUri }, why: 'Open the focused feature report' }
304
+ : { tool: 'qa_report', args: { sessionId: session.id }, why: 'Summarize the focused run' };
305
+ upd({
306
+ status: blocked ? 'failed' : 'done',
307
+ progress: state,
308
+ result: {
309
+ state,
310
+ feature: a.feature,
311
+ featureId: liveScope.featureId,
312
+ title: liveScope.title,
313
+ scope: liveScope,
314
+ objective: a.objective,
315
+ cases: merge.cases,
316
+ mapDelta: merge.delta,
317
+ coverage: merge.coverage,
318
+ blockers: merge.coverage.blockers,
319
+ graphUri,
320
+ casesUri,
321
+ reportUri: reportUri ?? null,
322
+ appMapUri: appMapUri ?? null,
323
+ suiteWritten,
324
+ featureCoverageCachePath: mapPath,
325
+ needsInput: ex.needsInput ?? null,
326
+ nextRecommendedAction,
327
+ },
328
+ resultText: `🎯 feature test ${state} — ${liveScope.title}: ${merge.delta.summary}\n` +
329
+ `cases: ${merge.cases.length} (${merge.delta.casesPassed} pass / ${merge.delta.casesFailed} fail / ${merge.delta.casesBlocked} blocked); map: ${merge.delta.statusBefore ?? 'new'} → ${merge.delta.statusAfter}\n` +
330
+ (reportUri ? `report: ${reportUri}\n` : '') +
331
+ (appMapUri ? `appMap: ${appMapUri}\n` : '') +
332
+ `featureCoverageCache: ${mapPath}` +
333
+ (ex.needsInput ? `\n❓ ${ex.needsInput.question}` : ''),
334
+ endedAt: Date.now(),
335
+ });
336
+ }
337
+ catch (e) {
338
+ if (signal?.aborted)
339
+ return;
340
+ log('error', 'feature test job failed', { jobId: job.jobId, err: String(e) });
341
+ upd({ status: 'failed', error: String(e), resultText: `❌ feature test error: ${String(e)}`, endedAt: Date.now() });
342
+ }
343
+ }
344
+ //# sourceMappingURL=featureTesting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"featureTesting.js","sourceRoot":"","sources":["../../src/tools/featureTesting.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ // qa_resolve_artifact (roadmap §4.2) — find the best installable build for the project, wherever
2
+ // it lives, and explain exactly where Swipium looked. Wraps src/artifacts/resolve.ts and returns
3
+ // a typed NO_BUILD_ARTIFACT / AAB_NEEDS_BUNDLETOOL / outside-root blocker instead of guessing.
4
+ import { z } from 'zod';
5
+ import { qaOk, qaError } from '../lib/result.js';
6
+ import { qaFail } from '../oracle/failures.js';
7
+ import { resolveProjectRoot } from '../context/projectRoot.js';
8
+ import { resolveArtifact } from '../artifacts/resolve.js';
9
+ import { qaNeedsInput, NeedsInput } from '../lib/needsInput.js';
10
+ export function registerResolveArtifact(server, sessions) {
11
+ server.registerTool('qa_resolve_artifact', {
12
+ title: 'Resolve a build artifact',
13
+ description: 'Find the best installable app build (.apk/.aab/.ipa/.app) for the project — searching Gradle/Flutter/Xcode output trees and (opt-in) Xcode DerivedData, not just the project root. Returns ranked candidates with build type, installability, app/bundle id, native ABIs, warnings, and the EXACT locations searched. Typed blockers: NO_BUILD_ARTIFACT (with where it looked), AAB_NEEDS_BUNDLETOOL, ARTIFACT_OUTSIDE_ROOT_REQUIRES_APPROVAL. Side-effect free (never installs/builds).',
14
+ inputSchema: {
15
+ sessionId: z.string().optional().describe("Use this session's projectRoot if given."),
16
+ projectRoot: z.string().optional().describe('Absolute path; else resolved via MCP roots.'),
17
+ platform: z.enum(['android', 'ios', 'any']).optional(),
18
+ buildType: z.enum(['debug', 'release', 'any']).optional(),
19
+ path: z.string().optional().describe('Explicit artifact path — short-circuits the search.'),
20
+ allowOutsideRoot: z.boolean().optional().describe('Allow a best candidate outside the project root (downloads/DerivedData).'),
21
+ requireInstallableOn: z.enum(['android-emulator', 'android-real', 'ios-simulator', 'ios-real']).optional(),
22
+ },
23
+ }, async ({ sessionId, projectRoot, platform, buildType, path, allowOutsideRoot, requireInstallableOn }) => {
24
+ let root;
25
+ if (sessionId)
26
+ root = sessions.get(sessionId)?.root;
27
+ if (!root) {
28
+ const resolved = await resolveProjectRoot(server, projectRoot);
29
+ if (!resolved.root) {
30
+ return qaError({ what: 'Could not resolve a project root', changedState: false, retrySafe: true, nextSteps: ['Pass projectRoot="/abs/path", or call qa_start_session first.'], clientHint: resolved.hint });
31
+ }
32
+ root = resolved.root;
33
+ }
34
+ const result = await resolveArtifact({
35
+ projectRoot: root,
36
+ platform,
37
+ buildType,
38
+ explicitPath: path,
39
+ allowOutsideRoot,
40
+ requireInstallableOn: requireInstallableOn,
41
+ });
42
+ if (result.failureCode === 'ARTIFACT_OUTSIDE_ROOT_REQUIRES_APPROVAL') {
43
+ const outside = result.candidates.find((c) => c.outsideRoot);
44
+ return qaNeedsInput(NeedsInput.artifactOutsideRoot(outside?.path ?? '(outside root)'), {
45
+ sessionId: sessionId ?? null,
46
+ candidates: result.candidates.slice(0, 5),
47
+ searchedLocations: result.searchedLocations,
48
+ });
49
+ }
50
+ if (result.failureCode) {
51
+ return qaFail(result.failureCode, {
52
+ what: result.failureCode === 'NO_BUILD_ARTIFACT'
53
+ ? `No installable artifact found under ${root}`
54
+ : result.warnings[0],
55
+ nextSteps: [
56
+ ...(result.warnings.length ? result.warnings : []),
57
+ 'Build from source: qa_build_plan → qa_build.',
58
+ `Searched: ${result.searchedLocations.slice(0, 8).join('; ')}${result.searchedLocations.length > 8 ? ' …' : ''}`,
59
+ ],
60
+ extra: { searchedLocations: result.searchedLocations, candidates: result.candidates.slice(0, 5) },
61
+ });
62
+ }
63
+ const best = result.best;
64
+ const summary = `Best: ${best.type.toUpperCase()} ${best.path}\n` +
65
+ `platform=${best.platform} buildType=${best.buildType} installableOn=[${best.installableOn.join(', ')}]` +
66
+ (best.appId ? ` appId=${best.appId}` : '') +
67
+ (best.abis?.length ? ` abis=[${best.abis.join(',')}]` : '') +
68
+ `\ncandidates: ${result.candidates.length} (top ${Math.min(5, result.candidates.length)} returned)` +
69
+ (best.warnings.length ? `\n⚠ ${best.warnings.join('; ')}` : '') +
70
+ (result.warnings.length ? `\nnote: ${result.warnings.join('; ')}` : '');
71
+ return qaOk({
72
+ best,
73
+ newest: result.newest,
74
+ candidates: result.candidates.slice(0, 5),
75
+ searchedLocations: result.searchedLocations,
76
+ warnings: result.warnings,
77
+ }, summary);
78
+ });
79
+ }
80
+ //# sourceMappingURL=resolveArtifact.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveArtifact.js","sourceRoot":"","sources":["../../src/tools/resolveArtifact.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ // qa_resolve_target (roadmap §5) — gather live device/simulator/artifact inputs and pick the
2
+ // best target with an explained reason, alternatives, preconditions, and whether a boot is
3
+ // needed. Pure decision logic lives in src/core/targetPlan.ts.
4
+ import { z } from 'zod';
5
+ import { qaOk, qaError } from '../lib/result.js';
6
+ import { qaFail } from '../oracle/failures.js';
7
+ import { resolveProjectRoot } from '../context/projectRoot.js';
8
+ import { adbDevices, listAvds, which } from '../lib/android.js';
9
+ import { simctlAvailable, listSimulators } from '../lib/simctl.js';
10
+ import { resolveArtifact } from '../artifacts/resolve.js';
11
+ import { planTarget } from '../core/targetPlan.js';
12
+ export function registerResolveTarget(server, sessions) {
13
+ server.registerTool('qa_resolve_target', {
14
+ title: 'Resolve the best test target',
15
+ description: 'Choose the best device/simulator to test on, deterministically: honors an explicit platform/device, respects a platform-specific artifact, prefers an already-online device (fastest), falls back to booting an emulator/simulator, and uses a real device only when requested. Returns the selection, a human reason, alternatives, preconditions (e.g. WDA/signing), and willBoot. Side-effect free (does not boot anything — qa_prepare_target / qa_ios do that).',
16
+ inputSchema: {
17
+ sessionId: z.string().optional(),
18
+ projectRoot: z.string().optional(),
19
+ platform: z.enum(['android', 'ios']).optional(),
20
+ device: z.string().optional().describe('Explicit adb serial / simulator udid or name / AVD name.'),
21
+ preferRealDevice: z.boolean().optional(),
22
+ },
23
+ }, async ({ sessionId, projectRoot, platform, device, preferRealDevice }) => {
24
+ let root;
25
+ if (sessionId)
26
+ root = sessions.get(sessionId)?.root;
27
+ if (!root) {
28
+ const resolved = await resolveProjectRoot(server, projectRoot);
29
+ if (!resolved.root)
30
+ return qaError({ what: 'Could not resolve a project root', changedState: false, retrySafe: true, nextSteps: ['Pass projectRoot or call qa_start_session.'], clientHint: resolved.hint });
31
+ root = resolved.root;
32
+ }
33
+ // Gather live inputs in parallel.
34
+ const [adbPresent, simPresent] = await Promise.all([which('adb'), simctlAvailable()]);
35
+ const [online, avds, sims] = await Promise.all([
36
+ adbPresent ? adbDevices() : Promise.resolve([]),
37
+ adbPresent ? listAvds() : Promise.resolve([]),
38
+ simPresent ? listSimulators() : Promise.resolve([]),
39
+ ]);
40
+ // The artifact (best-effort) tells us the platform constraint — never fail on this.
41
+ const art = await resolveArtifact({ projectRoot: root, platform: platform ?? 'any' }, false).catch(() => null);
42
+ const inputs = {
43
+ requestedPlatform: platform,
44
+ requestedDevice: device,
45
+ preferRealDevice,
46
+ artifactPlatform: art?.best?.platform,
47
+ artifactInstallTargets: art?.best?.installableOn,
48
+ android: { online, avds },
49
+ ios: {
50
+ bootedSimulators: sims.filter((s) => s.state === 'Booted').map((s) => ({ udid: s.udid, name: s.name })),
51
+ availableSimulators: sims.filter((s) => s.state !== 'Booted').map((s) => ({ udid: s.udid, name: s.name })),
52
+ },
53
+ wdaAvailable: undefined, // unknown without probing — surfaced as a precondition
54
+ };
55
+ const plan = planTarget(inputs);
56
+ if (plan.blocked) {
57
+ return qaFail(plan.blocked.failureCode, { what: plan.blocked.detail, extra: { targetPlan: plan, artifactPlatform: inputs.artifactPlatform ?? null } });
58
+ }
59
+ const summary = `selected: ${plan.selected}${plan.device ? ` (${plan.device})` : ''}\n` +
60
+ `reason: ${plan.reason}\n` +
61
+ (plan.willBoot ? `willBoot: yes${plan.bootTarget ? ` → ${plan.bootTarget}` : ''}\n` : 'willBoot: no\n') +
62
+ (plan.alternatives.length ? `alternatives: ${plan.alternatives.join(', ')}\n` : '') +
63
+ (plan.preconditions.length ? `preconditions: ${plan.preconditions.join('; ')}` : 'preconditions: none');
64
+ return qaOk({ ...plan, artifactPlatform: inputs.artifactPlatform ?? null }, summary);
65
+ });
66
+ }
67
+ //# sourceMappingURL=resolveTarget.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTarget.js","sourceRoot":"","sources":["../../src/tools/resolveTarget.ts"],"names":[],"mappings":"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"}
package/dist/version.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // Single source of truth for the Swipium version and the public tool surface. Used by the
2
2
  // server identity, qa_doctor / qa_start_session, qa_capabilities, and `swipium verify`.
3
- export const SWIPIUM_VERSION = '1.2.0';
3
+ export const SWIPIUM_VERSION = '1.3.0';
4
4
  export const TOOL_NAMES = [
5
5
  'qa_agent_brief',
6
6
  'qa_capabilities',
@@ -19,6 +19,12 @@ export const TOOL_NAMES = [
19
19
  'qa_prepare_ios_target',
20
20
  'qa_ios',
21
21
  'qa_wda',
22
+ // Local build + artifact resolution (REQ "test this app")
23
+ 'qa_resolve_target',
24
+ 'qa_resolve_artifact',
25
+ 'qa_build_plan',
26
+ 'qa_build',
27
+ 'qa_bundletool',
22
28
  // Device / app environment parity (Phase 5)
23
29
  'qa_device_info',
24
30
  'qa_permissions',
@@ -60,6 +66,10 @@ export const TOOL_NAMES = [
60
66
  'qa_app_map_query',
61
67
  'qa_app_map_feature_scope',
62
68
  'qa_app_map_validate',
69
+ // Feature-focused testing (REQ-03: "test the X feature")
70
+ 'qa_feature_scope',
71
+ 'qa_feature_test_plan',
72
+ 'qa_test_feature',
63
73
  // Repeatable flow system (Phase 4)
64
74
  'qa_flow_check',
65
75
  'qa_flow_plan',
@@ -108,6 +118,6 @@ export const PROMPT_NAMES = [
108
118
  export const PROMPT_COUNT = PROMPT_NAMES.length;
109
119
  /** Shown when a client may be running an older build than what's installed on disk. */
110
120
  export const STALE_CLIENT_HINT = `Swipium v${SWIPIUM_VERSION} exposes ${TOOL_COUNT} tools + ${PROMPT_COUNT} prompts. If your MCP client lists fewer ` +
111
- `(e.g. qa_issue_log / qa_test_suite_read / qa_mobile_audit / qa_maestro_export / qa_flow_repair missing), it is running a server ` +
121
+ `(e.g. qa_test_feature / qa_feature_scope / qa_build / qa_resolve_target / qa_bundletool missing), it is running a server ` +
112
122
  `spawned before the upgrade. Restart the client to reload Swipium.`;
113
123
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,wFAAwF;AAExF,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAEvC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,gBAAgB;IAChB,iBAAiB;IACjB,cAAc;IACd,eAAe;IACf,eAAe;IACf,WAAW;IACX,oBAAoB;IACpB,0BAA0B;IAC1B,iBAAiB;IACjB,WAAW;IACX,kBAAkB;IAClB,mBAAmB;IACnB,SAAS;IACT,mBAAmB;IACnB,uBAAuB;IACvB,QAAQ;IACR,QAAQ;IACR,4CAA4C;IAC5C,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,YAAY;IACZ,UAAU;IACV,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,aAAa;IACb,QAAQ;IACR,kBAAkB;IAClB,iBAAiB;IACjB,eAAe;IACf,SAAS;IACT,kBAAkB;IAClB,oEAAoE;IACpE,WAAW;IACX,qBAAqB;IACrB,qCAAqC;IACrC,oBAAoB;IACpB,SAAS;IACT,kBAAkB;IAClB,uBAAuB;IACvB,UAAU;IACV,YAAY;IACZ,WAAW;IACX,4DAA4D;IAC5D,mBAAmB;IACnB,gBAAgB;IAChB,yBAAyB;IACzB,SAAS;IACT,kBAAkB;IAClB,iBAAiB;IACjB,mBAAmB;IACnB,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;IAClB,0BAA0B;IAC1B,qBAAqB;IACrB,mCAAmC;IACnC,eAAe;IACf,cAAc;IACd,aAAa;IACb,kBAAkB;IAClB,gBAAgB;IAChB,uCAAuC;IACvC,mBAAmB;IACnB,kBAAkB;IAClB,eAAe;IACf,iBAAiB;IACjB,sBAAsB;IACtB,oBAAoB;IACpB,sBAAsB;IACtB,wBAAwB;IACxB,sBAAsB;IACtB,oBAAoB;IACpB,mBAAmB;IACnB,uBAAuB;IACvB,oBAAoB;IACpB,wBAAwB;IACxB,wBAAwB;IACxB,kBAAkB;IAClB,mBAAmB;IACnB,mBAAmB;IACnB,6DAA6D;IAC7D,cAAc;IACd,kBAAkB;IAClB,qBAAqB;IACrB,iBAAiB;IACjB,mBAAmB;IACnB,uBAAuB;IACvB,kBAAkB;IAClB,iBAAiB;CACT,CAAC;AAEX,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;AAEtE,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,qBAAqB;IACrB,8BAA8B;IAC9B,oBAAoB;IACpB,mBAAmB;IACnB,6BAA6B;CACrB,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;AAEhD,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAC5B,YAAY,eAAe,YAAY,UAAU,YAAY,YAAY,2CAA2C;IACpH,kIAAkI;IAClI,mEAAmE,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,wFAAwF;AAExF,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AAEvC,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,gBAAgB;IAChB,iBAAiB;IACjB,cAAc;IACd,eAAe;IACf,eAAe;IACf,WAAW;IACX,oBAAoB;IACpB,0BAA0B;IAC1B,iBAAiB;IACjB,WAAW;IACX,kBAAkB;IAClB,mBAAmB;IACnB,SAAS;IACT,mBAAmB;IACnB,uBAAuB;IACvB,QAAQ;IACR,QAAQ;IACR,0DAA0D;IAC1D,mBAAmB;IACnB,qBAAqB;IACrB,eAAe;IACf,UAAU;IACV,eAAe;IACf,4CAA4C;IAC5C,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,gBAAgB;IAChB,YAAY;IACZ,UAAU;IACV,gBAAgB;IAChB,gBAAgB;IAChB,kBAAkB;IAClB,aAAa;IACb,QAAQ;IACR,kBAAkB;IAClB,iBAAiB;IACjB,eAAe;IACf,SAAS;IACT,kBAAkB;IAClB,oEAAoE;IACpE,WAAW;IACX,qBAAqB;IACrB,qCAAqC;IACrC,oBAAoB;IACpB,SAAS;IACT,kBAAkB;IAClB,uBAAuB;IACvB,UAAU;IACV,YAAY;IACZ,WAAW;IACX,4DAA4D;IAC5D,mBAAmB;IACnB,gBAAgB;IAChB,yBAAyB;IACzB,SAAS;IACT,kBAAkB;IAClB,iBAAiB;IACjB,mBAAmB;IACnB,kBAAkB;IAClB,iBAAiB;IACjB,kBAAkB;IAClB,0BAA0B;IAC1B,qBAAqB;IACrB,yDAAyD;IACzD,kBAAkB;IAClB,sBAAsB;IACtB,iBAAiB;IACjB,mCAAmC;IACnC,eAAe;IACf,cAAc;IACd,aAAa;IACb,kBAAkB;IAClB,gBAAgB;IAChB,uCAAuC;IACvC,mBAAmB;IACnB,kBAAkB;IAClB,eAAe;IACf,iBAAiB;IACjB,sBAAsB;IACtB,oBAAoB;IACpB,sBAAsB;IACtB,wBAAwB;IACxB,sBAAsB;IACtB,oBAAoB;IACpB,mBAAmB;IACnB,uBAAuB;IACvB,oBAAoB;IACpB,wBAAwB;IACxB,wBAAwB;IACxB,kBAAkB;IAClB,mBAAmB;IACnB,mBAAmB;IACnB,6DAA6D;IAC7D,cAAc;IACd,kBAAkB;IAClB,qBAAqB;IACrB,iBAAiB;IACjB,mBAAmB;IACnB,uBAAuB;IACvB,kBAAkB;IAClB,iBAAiB;CACT,CAAC;AAEX,MAAM,CAAC,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC;AAC5C,MAAM,CAAC,MAAM,aAAa,GAAwB,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;AAEtE,oEAAoE;AACpE,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,qBAAqB;IACrB,8BAA8B;IAC9B,oBAAoB;IACpB,mBAAmB;IACnB,6BAA6B;CACrB,CAAC;AAEX,MAAM,CAAC,MAAM,YAAY,GAAG,YAAY,CAAC,MAAM,CAAC;AAEhD,uFAAuF;AACvF,MAAM,CAAC,MAAM,iBAAiB,GAC5B,YAAY,eAAe,YAAY,UAAU,YAAY,YAAY,2CAA2C;IACpH,2HAA2H;IAC3H,mEAAmE,CAAC"}