@skyramp/mcp 0.2.9 → 0.2.10-rc.1
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/build/index.js +1 -1
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +28 -1
- package/build/prompts/initialize-workspace/initializeWorkspacePrompt.js +1 -0
- package/build/prompts/personas.d.ts +2 -2
- package/build/prompts/personas.js +2 -2
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +3 -0
- package/build/prompts/sut-setup/shared.js +1 -3
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +1 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +10 -5
- package/build/prompts/test-maintenance/driftAnalysisSections.js +1 -4
- package/build/prompts/test-maintenance/driftAnalysisShared.d.ts +7 -1
- package/build/prompts/test-maintenance/driftAnalysisShared.js +19 -1
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.d.ts +0 -6
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +0 -16
- package/build/prompts/test-recommendation/recommendationShared.js +1 -1
- package/build/prompts/testbot/testbot-prompts.js +8 -8
- package/build/resources/testbotResource.js +1 -1
- package/build/services/TestDiscoveryService.d.ts +0 -1
- package/build/services/TestDiscoveryService.js +13 -33
- package/build/services/TestDiscoveryService.test.js +136 -1
- package/build/services/TestExecutionService.d.ts +1 -1
- package/build/tools/executeSkyrampTestTool.js +40 -71
- package/build/tools/submitReportTool.d.ts +20 -20
- package/build/tools/submitReportTool.js +113 -31
- package/build/tools/submitReportTool.test.js +411 -94
- package/build/tools/test-management/actionsTool.js +135 -53
- package/build/tools/test-management/actionsTool.test.d.ts +1 -0
- package/build/tools/test-management/actionsTool.test.js +297 -0
- package/build/tools/test-management/analyzeChangesTool.d.ts +9 -0
- package/build/tools/test-management/analyzeChangesTool.js +63 -152
- package/build/tools/test-management/analyzeChangesTool.test.js +34 -3
- package/build/tools/test-management/analyzeTestHealthTool.js +24 -157
- package/build/tools/test-management/analyzeTestHealthTool.test.js +15 -215
- package/build/tools/workspace/initializeWorkspaceTool.js +35 -15
- package/build/types/FrontendIntegration.d.ts +25 -0
- package/build/types/FrontendIntegration.js +19 -0
- package/build/types/RepositoryAnalysis.d.ts +8 -8
- package/build/types/TestAnalysis.d.ts +16 -50
- package/build/types/TestAnalysis.js +0 -28
- package/build/types/TestTypes.d.ts +3 -1
- package/build/types/TestTypes.js +3 -1
- package/build/utils/AnalysisStateManager.d.ts +13 -1
- package/build/utils/docker.test.js +1 -1
- package/build/utils/featureFlags.d.ts +1 -1
- package/build/utils/featureFlags.js +1 -1
- package/build/utils/frontendIntegration.d.ts +9 -0
- package/build/utils/frontendIntegration.js +237 -0
- package/build/utils/frontendIntegration.test.d.ts +1 -0
- package/build/utils/frontendIntegration.test.js +229 -0
- package/build/utils/pr-comment-parser.d.ts +3 -3
- package/build/utils/pr-comment-parser.js +6 -6
- package/build/utils/pr-comment-parser.test.js +3 -3
- package/build/utils/repoScanner.d.ts +7 -0
- package/build/utils/repoScanner.js +14 -6
- package/build/utils/repoScanner.test.js +9 -9
- package/build/utils/routeParsers.d.ts +32 -0
- package/build/utils/routeParsers.js +205 -2
- package/build/utils/routeParsers.test.js +279 -29
- package/build/utils/sourceRouteExtractor.js +174 -5
- package/build/utils/sourceRouteExtractor.test.js +173 -0
- package/build/utils/utils.d.ts +11 -0
- package/build/utils/utils.js +19 -0
- package/build/utils/utils.test.js +23 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/node_modules/playwright/lib/mcp/browser/tools/files.js +8 -3
- package/node_modules/playwright/lib/mcp/browser/tools/wait.js +1 -1
- package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +10 -1
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +33 -9
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +7 -1
- package/node_modules/playwright/lib/mcp/test/skyRampExport.test.js +44 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.B_7ywgmr.js → index.d4gkVSou.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/package.json +1 -1
- package/package.json +3 -3
|
@@ -8,7 +8,7 @@ import { TestType, HttpMethod } from "../types/TestTypes.js";
|
|
|
8
8
|
import { DriftAction } from "../types/TestAnalysis.js";
|
|
9
9
|
import { TestExecutionStatus } from "../types/TestExecution.js";
|
|
10
10
|
import { StateManager } from "../utils/AnalysisStateManager.js";
|
|
11
|
-
import { toolError } from "../utils/utils.js";
|
|
11
|
+
import { toolError, testFileMatches } from "../utils/utils.js";
|
|
12
12
|
// Drift actions that actually modify a test file. VERIFY and IGNORE are
|
|
13
13
|
// no-ops (the test was assessed but left unchanged), so they must not count
|
|
14
14
|
// toward "tests maintained" telemetry.
|
|
@@ -244,10 +244,14 @@ export const additionalRecommendationSchema = z.object({
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
246
|
});
|
|
247
|
+
// TODO(multi-repo maintenance): no `repository` field yet — see readData() TODO below.
|
|
247
248
|
const testMaintenanceSchema = z.object({
|
|
248
249
|
testType: z.nativeEnum(TestType).describe("Type of test."),
|
|
249
250
|
endpoint: z.string().describe("HTTP verb and path, e.g. 'GET /api/v1/products'"),
|
|
250
|
-
|
|
251
|
+
testFilePath: z
|
|
252
|
+
.string()
|
|
253
|
+
.refine((p) => path.isAbsolute(p), { message: "testFilePath must be an absolute path" })
|
|
254
|
+
.describe("Absolute path of the test file that was maintained, e.g. '/repo/tests/products_smoke_test.py' — the same path you passed to skyramp_execute_test's testFile param. Consumers should basename this for display."),
|
|
251
255
|
action: z.nativeEnum(DriftAction).describe("The drift action assigned to this test during maintenance triage."),
|
|
252
256
|
description: z.string().describe("What was changed and why"),
|
|
253
257
|
beforeDetails: z.string().describe("One line only — no embedded newlines, no raw HTTP headers or JSON blobs. " +
|
|
@@ -269,6 +273,14 @@ const testMaintenanceLLMSchema = testMaintenanceSchema.omit({
|
|
|
269
273
|
beforeStatus: true,
|
|
270
274
|
afterStatus: true,
|
|
271
275
|
});
|
|
276
|
+
// LLM only drafts beforeDetails/afterDetails per file — action/testType/endpoint/description
|
|
277
|
+
// come from stateData.maintenanceVerdicts. Picked from testMaintenanceLLMSchema to avoid
|
|
278
|
+
// redeclaring those fields.
|
|
279
|
+
const testMaintenanceDetailSchema = testMaintenanceLLMSchema.pick({
|
|
280
|
+
testFilePath: true,
|
|
281
|
+
beforeDetails: true,
|
|
282
|
+
afterDetails: true,
|
|
283
|
+
});
|
|
272
284
|
/**
|
|
273
285
|
* Derive per-run analytics counts from a submitted report. These power the
|
|
274
286
|
* alpha-launch dashboards (tests generated/maintained, suite growth, bugs vs
|
|
@@ -341,7 +353,8 @@ export function registerSubmitReportTool(server) {
|
|
|
341
353
|
.describe("The file path where the report should be written (provided in the task instructions)"),
|
|
342
354
|
businessCaseAnalysis: z
|
|
343
355
|
.string()
|
|
344
|
-
.
|
|
356
|
+
.min(1, "businessCaseAnalysis is required — describe what the PR does, even for no-test runs (config-only, non-application, Testbot onboarding). It is never optional and must not be empty.")
|
|
357
|
+
.describe("1-2 sentences describing what user-facing interactions this PR enables or changes (e.g. 'customers can now leave and view product reviews'). Focus on the user journey, not technical implementation. Flag backend-only or frontend-only gaps. Always required — even when no tests apply (config-only diffs, Testbot onboarding PRs); describe the PR's purpose and why no tests apply."),
|
|
345
358
|
newTestsCreated: z
|
|
346
359
|
.array(newTestSchema)
|
|
347
360
|
.describe("List of new tests created. Use empty array [] if none."),
|
|
@@ -350,10 +363,10 @@ export function registerSubmitReportTool(server) {
|
|
|
350
363
|
.optional()
|
|
351
364
|
.default([])
|
|
352
365
|
.describe("Recommended tests that were not generated (lower priority). Only include recommendations that add distinct coverage beyond generated tests — do not pad with variants testing the same endpoint and flow."),
|
|
353
|
-
|
|
354
|
-
.array(
|
|
366
|
+
testMaintenanceDetails: z
|
|
367
|
+
.array(testMaintenanceDetailSchema)
|
|
355
368
|
.optional()
|
|
356
|
-
.describe("One entry per test that
|
|
369
|
+
.describe("One entry per maintained test that had a before/after execution recorded, with beforeDetails/afterDetails drafted from what you actually saw when you ran it. Omit an entry entirely when no execution was recorded for that test."),
|
|
357
370
|
testResults: z
|
|
358
371
|
.array(testResultSchema)
|
|
359
372
|
.describe("List of ALL test execution results. One entry per test executed."),
|
|
@@ -392,31 +405,93 @@ export function registerSubmitReportTool(server) {
|
|
|
392
405
|
let errorResult;
|
|
393
406
|
const dedupedNewTests = deduplicateById([...params.newTestsCreated]);
|
|
394
407
|
const dedupedRecommendations = deduplicateById([...(params.additionalRecommendations ?? [])]);
|
|
408
|
+
const stateManager = StateManager.fromStatePath(params.stateFile);
|
|
409
|
+
let stateData;
|
|
410
|
+
try {
|
|
411
|
+
// Only reads the primary repo — see the related-repo check below for why related
|
|
412
|
+
// repos' maintenanceVerdicts are never folded in here.
|
|
413
|
+
stateData = await stateManager.readData();
|
|
414
|
+
}
|
|
415
|
+
catch (err) {
|
|
416
|
+
errorResult = toolError(`Failed to load stateFile: ${err.message}`);
|
|
417
|
+
return errorResult;
|
|
418
|
+
}
|
|
419
|
+
if (!stateData) {
|
|
420
|
+
errorResult = toolError(`stateFile not found or empty: ${params.stateFile}`);
|
|
421
|
+
return errorResult;
|
|
422
|
+
}
|
|
423
|
+
// undefined + existing tests present means skyramp_actions was never called this
|
|
424
|
+
// run — not "no maintenance needed" (that case writes an empty array). Reject rather
|
|
425
|
+
// than silently reporting an empty testMaintenance section indistinguishable from a
|
|
426
|
+
// real "nothing to do". With zero existing tests there was nothing to lose, so
|
|
427
|
+
// undefined is safe there — no round-trip through skyramp_actions required.
|
|
428
|
+
if (stateData.maintenanceVerdicts === undefined && (stateData.existingTests?.length ?? 0) > 0) {
|
|
429
|
+
errorResult = toolError("stateFile has existingTests but no maintenanceVerdicts — skyramp_actions was not called for this run. " +
|
|
430
|
+
"Call skyramp_actions (with recommendations: [] if no existing tests needed action) before skyramp_submit_report.");
|
|
431
|
+
return errorResult;
|
|
432
|
+
}
|
|
433
|
+
// TestbotReport.testMaintenance has no repository field, so a related repo's
|
|
434
|
+
// reconciled verdicts have nowhere to go in this report. Fail loud instead of
|
|
435
|
+
// silently omitting real maintenance work skyramp_actions already did and persisted.
|
|
436
|
+
// Multi-repo maintenance reporting is a separate, unscoped feature (would also need
|
|
437
|
+
// testbot prompt / drift-analysis / test-bot.git renderer changes) — not done here.
|
|
438
|
+
const relatedReposWithVerdicts = [];
|
|
439
|
+
for (const repo of await stateManager.listRelatedRepos()) {
|
|
440
|
+
const relatedData = await stateManager.readRepoData(repo);
|
|
441
|
+
if ((relatedData?.maintenanceVerdicts?.length ?? 0) > 0)
|
|
442
|
+
relatedReposWithVerdicts.push(repo);
|
|
443
|
+
}
|
|
444
|
+
if (relatedReposWithVerdicts.length > 0) {
|
|
445
|
+
errorResult = toolError(`Related repo(s) have reconciled maintenance verdicts that cannot be included in this report yet ` +
|
|
446
|
+
`(multi-repo maintenance reporting is not supported — testMaintenance has no repository field): ${relatedReposWithVerdicts.join(", ")}.`);
|
|
447
|
+
return errorResult;
|
|
448
|
+
}
|
|
449
|
+
// action/testType/endpoint/description come entirely from stateData.maintenanceVerdicts
|
|
450
|
+
// (written by skyramp_actions right after drift analysis) — the LLM never supplies
|
|
451
|
+
// testMaintenance directly, so there's nothing to re-transcribe here.
|
|
452
|
+
const rawMaintenance = (stateData.maintenanceVerdicts ?? []).map((v) => ({
|
|
453
|
+
testFilePath: v.testFilePath,
|
|
454
|
+
testType: v.testType,
|
|
455
|
+
endpoint: v.endpoint,
|
|
456
|
+
action: v.action,
|
|
457
|
+
description: v.rationale,
|
|
458
|
+
beforeDetails: "",
|
|
459
|
+
afterDetails: "",
|
|
460
|
+
}));
|
|
395
461
|
let testMaintenance;
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
462
|
+
// Rows missing a required detail (a real execution happened but the LLM didn't
|
|
463
|
+
// draft a summary for it) — collected here and rejected below, rather than
|
|
464
|
+
// silently shipping a blank field.
|
|
465
|
+
const missingDetails = [];
|
|
466
|
+
if (rawMaintenance.length > 0) {
|
|
467
|
+
// beforeStatus/afterStatus are always stateFile-authoritative. beforeDetails/
|
|
468
|
+
// afterDetails come from the LLM's drafted summary (testMaintenanceDetails) —
|
|
469
|
+
// required whenever the stateFile shows a matching execution actually ran.
|
|
470
|
+
testMaintenance = rawMaintenance.map((m) => {
|
|
471
|
+
const recorded = stateData.existingTests?.find((t) => testFileMatches(t.testFile, m.testFilePath));
|
|
472
|
+
const detail = params.testMaintenanceDetails?.find((d) => d.testFilePath === m.testFilePath);
|
|
473
|
+
const displayName = path.basename(m.testFilePath);
|
|
474
|
+
const defaultBeforeStatus = MAINTENANCE_CHANGE_ACTIONS.has(m.action) ? TestExecutionStatus.Unknown : TestExecutionStatus.Skipped;
|
|
475
|
+
const defaultAfterStatus = m.action === DriftAction.Delete ? TestExecutionStatus.Skipped
|
|
476
|
+
: MAINTENANCE_CHANGE_ACTIONS.has(m.action) ? TestExecutionStatus.Unknown : TestExecutionStatus.Skipped;
|
|
477
|
+
const beforeStatus = recorded?.executionBefore?.status ?? defaultBeforeStatus;
|
|
478
|
+
const afterStatus = recorded?.executionAfter?.status ?? defaultAfterStatus;
|
|
479
|
+
// Trim before checking — a whitespace-only string is semantically blank and
|
|
480
|
+
// must not satisfy the "drafted a summary" requirement.
|
|
481
|
+
const beforeDetails = detail?.beforeDetails?.trim() ?? "";
|
|
482
|
+
const afterDetails = detail?.afterDetails?.trim() ?? "";
|
|
483
|
+
if (recorded?.executionBefore && !beforeDetails)
|
|
484
|
+
missingDetails.push(`${displayName} (beforeDetails)`);
|
|
485
|
+
if (recorded?.executionAfter && !afterDetails)
|
|
486
|
+
missingDetails.push(`${displayName} (afterDetails)`);
|
|
487
|
+
logger.info(`${displayName}: before=${beforeStatus} after=${afterStatus}`);
|
|
488
|
+
return { ...m, beforeDetails, afterDetails, beforeStatus, afterStatus };
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
if (missingDetails.length > 0) {
|
|
492
|
+
errorResult = toolError(`${missingDetails.length} maintenance row(s) have a recorded execution but no drafted summary: ${missingDetails.join(", ")}. ` +
|
|
493
|
+
"Add a testMaintenanceDetails entry with the missing field(s), drafted from the execution output you already saw.");
|
|
494
|
+
return errorResult;
|
|
420
495
|
}
|
|
421
496
|
// Strip generation-artifact fields from newTestsCreated before writing.
|
|
422
497
|
// scenarioFile, traceFile, frontendTrace are internal paths used during
|
|
@@ -428,7 +503,14 @@ export function registerSubmitReportTool(server) {
|
|
|
428
503
|
businessCaseAnalysis: params.businessCaseAnalysis,
|
|
429
504
|
newTestsCreated: sanitizedNewTests,
|
|
430
505
|
additionalRecommendations: dedupedRecommendations.map(normalizeRepository),
|
|
431
|
-
|
|
506
|
+
// Report wire format keeps main's original `fileName` (basename) — testFilePath is
|
|
507
|
+
// an internal-only field, needed for matching but never meant to reach the report.
|
|
508
|
+
// TODO(multi-repo maintenance): map(normalizeRepository) once testMaintenanceSchema
|
|
509
|
+
// has a repository field (see TODO above).
|
|
510
|
+
testMaintenance: testMaintenance?.map(({ testFilePath, ...row }) => ({
|
|
511
|
+
...row,
|
|
512
|
+
fileName: path.basename(testFilePath),
|
|
513
|
+
})),
|
|
432
514
|
testResults: params.testResults.map(normalizeRepository),
|
|
433
515
|
issuesFound: params.issuesFound.map(normalizeRepository),
|
|
434
516
|
nextSteps: params.nextSteps ?? [],
|