@muggleai/works 4.2.2 → 4.4.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 (33) hide show
  1. package/README.md +45 -37
  2. package/dist/{chunk-BZJXQZ5Q.js → chunk-PMI2DI3V.js} +524 -173
  3. package/dist/cli.js +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/plugin/.claude-plugin/plugin.json +4 -4
  6. package/dist/plugin/.cursor-plugin/plugin.json +3 -3
  7. package/dist/plugin/README.md +7 -5
  8. package/dist/plugin/scripts/ensure-electron-app.sh +3 -3
  9. package/dist/plugin/skills/do/e2e-acceptance.md +161 -0
  10. package/dist/plugin/skills/do/open-prs.md +86 -16
  11. package/dist/plugin/skills/muggle/SKILL.md +15 -13
  12. package/dist/plugin/skills/muggle-do/SKILL.md +6 -6
  13. package/dist/plugin/skills/muggle-test/SKILL.md +380 -0
  14. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +44 -27
  15. package/dist/plugin/skills/muggle-test-import/SKILL.md +272 -0
  16. package/dist/plugin/skills/muggle-upgrade/SKILL.md +1 -1
  17. package/dist/plugin/skills/optimize-descriptions/SKILL.md +8 -8
  18. package/package.json +15 -12
  19. package/plugin/.claude-plugin/plugin.json +4 -4
  20. package/plugin/.cursor-plugin/plugin.json +3 -3
  21. package/plugin/README.md +7 -5
  22. package/plugin/scripts/ensure-electron-app.sh +3 -3
  23. package/plugin/skills/do/e2e-acceptance.md +161 -0
  24. package/plugin/skills/do/open-prs.md +86 -16
  25. package/plugin/skills/muggle/SKILL.md +15 -13
  26. package/plugin/skills/muggle-do/SKILL.md +6 -6
  27. package/plugin/skills/muggle-test/SKILL.md +380 -0
  28. package/plugin/skills/muggle-test-feature-local/SKILL.md +44 -27
  29. package/plugin/skills/muggle-test-import/SKILL.md +272 -0
  30. package/plugin/skills/muggle-upgrade/SKILL.md +1 -1
  31. package/plugin/skills/optimize-descriptions/SKILL.md +8 -8
  32. package/dist/plugin/skills/do/qa.md +0 -89
  33. package/plugin/skills/do/qa.md +0 -89
@@ -8,9 +8,10 @@ import { fileURLToPath } from 'url';
8
8
  import winston from 'winston';
9
9
  import axios, { AxiosError } from 'axios';
10
10
  import { spawn, exec, execFile } from 'child_process';
11
+ import * as crypto from 'crypto';
12
+ import { randomUUID } from 'crypto';
11
13
  import * as fs5 from 'fs/promises';
12
14
  import { z, ZodError } from 'zod';
13
- import * as crypto from 'crypto';
14
15
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
15
16
  import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
16
17
  import { v4 } from 'uuid';
@@ -39,6 +40,7 @@ var DEFAULT_PROMPT_SERVICE_PRODUCTION_URL = "https://promptservice.muggle-ai.com
39
40
  var DEFAULT_PROMPT_SERVICE_DEV_URL = "http://localhost:5050";
40
41
  var DEFAULT_WEB_SERVICE_URL = "http://localhost:3001";
41
42
  var ELECTRON_APP_DIR = "electron-app";
43
+ var ELECTRON_APP_RELEASE_TAG_PREFIX = "electron-app-v";
42
44
  var API_KEY_FILE = "api-key.json";
43
45
  var DEFAULT_AUTH0_PRODUCTION_DOMAIN = "login.muggle-ai.com";
44
46
  var DEFAULT_AUTH0_PRODUCTION_CLIENT_ID = "UgG5UjoyLksxMciWWKqVpwfWrJ4rFvtT";
@@ -262,7 +264,7 @@ function buildAuth0Config() {
262
264
  scope: process.env.AUTH0_SCOPE ?? DEFAULT_AUTH0_SCOPE
263
265
  };
264
266
  }
265
- function buildQaConfig() {
267
+ function buildE2eConfig() {
266
268
  const defaultPromptServiceUrl = getDefaultPromptServiceUrl();
267
269
  return {
268
270
  promptServiceBaseUrl: process.env.PROMPT_SERVICE_BASE_URL ?? defaultPromptServiceUrl,
@@ -306,7 +308,7 @@ function getConfig() {
306
308
  serverVersion: "1.0.0",
307
309
  logLevel: process.env.LOG_LEVEL ?? "info",
308
310
  auth0: buildAuth0Config(),
309
- qa: buildQaConfig(),
311
+ e2e: buildE2eConfig(),
310
312
  localQa: buildLocalQaConfig()
311
313
  };
312
314
  return configInstance;
@@ -357,6 +359,15 @@ function getBundledElectronAppVersion() {
357
359
  function getDownloadBaseUrl() {
358
360
  return getMuggleConfig().downloadBaseUrl;
359
361
  }
362
+ function buildElectronAppReleaseTag(version) {
363
+ return `${ELECTRON_APP_RELEASE_TAG_PREFIX}${version}`;
364
+ }
365
+ function buildElectronAppReleaseAssetUrl(params) {
366
+ return `${getDownloadBaseUrl()}/${buildElectronAppReleaseTag(params.version)}/${params.assetFileName}`;
367
+ }
368
+ function buildElectronAppChecksumsUrl(version) {
369
+ return `${getDownloadBaseUrl()}/${buildElectronAppReleaseTag(version)}/checksums.txt`;
370
+ }
360
371
  function getElectronAppChecksums() {
361
372
  return getMuggleConfig().checksums;
362
373
  }
@@ -403,13 +414,14 @@ function resetLogger() {
403
414
  loggerInstance = null;
404
415
  }
405
416
 
406
- // packages/mcps/src/mcp/qa/index.ts
407
- var qa_exports2 = {};
408
- __export(qa_exports2, {
417
+ // packages/mcps/src/mcp/e2e/index.ts
418
+ var e2e_exports2 = {};
419
+ __export(e2e_exports2, {
409
420
  ActionScriptGetInputSchema: () => ActionScriptGetInputSchema,
410
421
  ApiKeyCreateInputSchema: () => ApiKeyCreateInputSchema,
411
422
  ApiKeyGetInputSchema: () => ApiKeyGetInputSchema,
412
423
  ApiKeyListInputSchema: () => ApiKeyListInputSchema,
424
+ ApiKeyRecordIdSchema: () => ApiKeyRecordIdSchema,
413
425
  ApiKeyRevokeInputSchema: () => ApiKeyRevokeInputSchema,
414
426
  AuthLoginInputSchema: () => AuthLoginInputSchema,
415
427
  AuthPollInputSchema: () => AuthPollInputSchema,
@@ -419,6 +431,7 @@ __export(qa_exports2, {
419
431
  LocalExecutionContextInputSchema: () => LocalExecutionContextInputSchema,
420
432
  LocalRunUploadInputSchema: () => LocalRunUploadInputSchema,
421
433
  McpErrorCode: () => McpErrorCode,
434
+ MuggleEntityIdSchema: () => MuggleEntityIdSchema,
422
435
  PaginationInputSchema: () => PaginationInputSchema,
423
436
  PrdFileDeleteInputSchema: () => PrdFileDeleteInputSchema,
424
437
  PrdFileListInputSchema: () => PrdFileListInputSchema,
@@ -440,11 +453,13 @@ __export(qa_exports2, {
440
453
  ReportFinalGenerateInputSchema: () => ReportFinalGenerateInputSchema,
441
454
  ReportPreferencesUpsertInputSchema: () => ReportPreferencesUpsertInputSchema,
442
455
  ReportStatsSummaryInputSchema: () => ReportStatsSummaryInputSchema,
456
+ RunBatchIdSchema: () => RunBatchIdSchema,
443
457
  SecretCreateInputSchema: () => SecretCreateInputSchema,
444
458
  SecretDeleteInputSchema: () => SecretDeleteInputSchema,
445
459
  SecretGetInputSchema: () => SecretGetInputSchema,
446
460
  SecretListInputSchema: () => SecretListInputSchema,
447
461
  SecretUpdateInputSchema: () => SecretUpdateInputSchema,
462
+ StripePaymentMethodIdSchema: () => StripePaymentMethodIdSchema,
448
463
  TestCaseCreateInputSchema: () => TestCaseCreateInputSchema,
449
464
  TestCaseGenerateFromPromptInputSchema: () => TestCaseGenerateFromPromptInputSchema,
450
465
  TestCaseGetInputSchema: () => TestCaseGetInputSchema,
@@ -453,6 +468,8 @@ __export(qa_exports2, {
453
468
  TestScriptGetInputSchema: () => TestScriptGetInputSchema,
454
469
  TestScriptListInputSchema: () => TestScriptListInputSchema,
455
470
  TestScriptListPaginatedInputSchema: () => TestScriptListPaginatedInputSchema,
471
+ TokenPackageIdSchema: () => TokenPackageIdSchema,
472
+ TokenUsageFilterTypeSchema: () => TokenUsageFilterTypeSchema,
456
473
  UseCaseCandidatesApproveInputSchema: () => UseCaseCandidatesApproveInputSchema,
457
474
  UseCaseCreateFromPromptsInputSchema: () => UseCaseCreateFromPromptsInputSchema,
458
475
  UseCaseDiscoveryMemoryGetInputSchema: () => UseCaseDiscoveryMemoryGetInputSchema,
@@ -1621,7 +1638,7 @@ var RunResultStorageService = class {
1621
1638
  */
1622
1639
  createRunResult(params) {
1623
1640
  const now = (/* @__PURE__ */ new Date()).toISOString();
1624
- const id = `run_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
1641
+ const id = randomUUID();
1625
1642
  const result = {
1626
1643
  id,
1627
1644
  runType: params.runType,
@@ -1704,7 +1721,7 @@ var RunResultStorageService = class {
1704
1721
  */
1705
1722
  createTestScript(params) {
1706
1723
  const now = (/* @__PURE__ */ new Date()).toISOString();
1707
- const id = `ts_${Date.now()}_${Math.random().toString(36).substring(2, 8)}`;
1724
+ const id = randomUUID();
1708
1725
  const script = {
1709
1726
  id,
1710
1727
  name: params.name,
@@ -1751,7 +1768,7 @@ function getAuthenticatedUserId() {
1751
1768
  const authService = getAuthService();
1752
1769
  const authStatus = authService.getAuthStatus();
1753
1770
  if (!authStatus.authenticated) {
1754
- throw new Error("Not authenticated. Please run qa_auth_login first.");
1771
+ throw new Error("Not authenticated. Please run muggle-remote-auth-login first.");
1755
1772
  }
1756
1773
  if (!authStatus.userId) {
1757
1774
  throw new Error("User ID not found in auth. Please re-authenticate.");
@@ -1801,7 +1818,7 @@ function buildStudioAuthContent() {
1801
1818
  const authStatus = authService.getAuthStatus();
1802
1819
  const storedAuth = authService.loadStoredAuth();
1803
1820
  if (!authStatus.authenticated || !storedAuth) {
1804
- throw new Error("Not authenticated. Please run qa_auth_login first.");
1821
+ throw new Error("Not authenticated. Please run muggle-remote-auth-login first.");
1805
1822
  }
1806
1823
  if (!storedAuth.email || !storedAuth.userId) {
1807
1824
  throw new Error("Auth data incomplete. Please re-authenticate.");
@@ -1928,6 +1945,7 @@ function buildGenerationActionScript(params) {
1928
1945
  url: params.localUrl,
1929
1946
  description: params.testCase.title,
1930
1947
  precondition: params.testCase.precondition ?? "",
1948
+ instructions: params.testCase.instructions ?? "",
1931
1949
  expectedResult: params.testCase.expectedResult,
1932
1950
  steps: [],
1933
1951
  ownerId: params.ownerUserId,
@@ -2616,7 +2634,7 @@ async function pollDeviceCode(config, deviceCode) {
2616
2634
  }
2617
2635
  async function createApiKeyWithToken(accessToken, keyName, expiry = "90d") {
2618
2636
  const config = getConfig();
2619
- const apiKeyUrl = `${config.qa.promptServiceBaseUrl}/v1/protected/api-keys`;
2637
+ const apiKeyUrl = `${config.e2e.promptServiceBaseUrl}/v1/protected/api-keys`;
2620
2638
  try {
2621
2639
  logger4.info("[Auth] Creating API key", {
2622
2640
  keyName,
@@ -2809,6 +2827,7 @@ function toolRequiresAuth(toolName) {
2809
2827
  ];
2810
2828
  return !noAuthTools.includes(toolName);
2811
2829
  }
2830
+ var MuggleEntityIdSchema = z.string().uuid();
2812
2831
  var LocalExecutionContextInputSchema = z.object({
2813
2832
  originalUrl: z.string().url().describe("Original local URL used during local execution (typically localhost)"),
2814
2833
  productionUrl: z.string().url().describe("Cloud production URL for the test case"),
@@ -2818,12 +2837,12 @@ var LocalExecutionContextInputSchema = z.object({
2818
2837
  electronAppVersion: z.string().optional().describe("Electron app version used for local run"),
2819
2838
  mcpServerVersion: z.string().optional().describe("MCP server version used for local run"),
2820
2839
  localExecutionCompletedAt: z.number().int().positive().describe("Epoch milliseconds when local run completed"),
2821
- uploadedAt: z.number().int().positive().optional().describe("Epoch milliseconds when uploaded to cloud")
2840
+ uploadedAt: z.number().int().positive().describe("Epoch milliseconds when uploaded to cloud")
2822
2841
  });
2823
2842
  var LocalRunUploadInputSchema = z.object({
2824
- projectId: z.string().min(1).describe("Project ID for the local run"),
2825
- useCaseId: z.string().min(1).describe("Use case ID for the local run"),
2826
- testCaseId: z.string().min(1).describe("Test case ID for the local run"),
2843
+ projectId: MuggleEntityIdSchema.describe("Project ID (UUID) for the local run"),
2844
+ useCaseId: MuggleEntityIdSchema.describe("Use case ID (UUID) for the local run"),
2845
+ testCaseId: MuggleEntityIdSchema.describe("Test case ID (UUID) for the local run"),
2827
2846
  runType: z.enum(["generation", "replay"]).describe("Type of local run to upload"),
2828
2847
  productionUrl: z.string().url().describe("Cloud production URL associated with the run"),
2829
2848
  localExecutionContext: LocalExecutionContextInputSchema.describe("Local execution metadata"),
@@ -2833,12 +2852,16 @@ var LocalRunUploadInputSchema = z.object({
2833
2852
  errorMessage: z.string().optional().describe("Error message when status is failed")
2834
2853
  });
2835
2854
 
2836
- // packages/mcps/src/mcp/qa/contracts/index.ts
2855
+ // packages/mcps/src/mcp/e2e/contracts/index.ts
2837
2856
  var PaginationInputSchema = z.object({
2838
2857
  page: z.number().int().positive().optional().describe("Page number (1-based)"),
2839
2858
  pageSize: z.number().int().positive().max(100).optional().describe("Number of items per page")
2840
2859
  });
2841
- var IdSchema = z.string().min(1).describe("Unique identifier");
2860
+ var IdSchema = MuggleEntityIdSchema;
2861
+ var RunBatchIdSchema = MuggleEntityIdSchema.describe("Bulk replay run batch ID (UUID)");
2862
+ var TokenPackageIdSchema = z.string().min(1).describe("Token package ID from wallet catalog");
2863
+ var StripePaymentMethodIdSchema = z.string().regex(/^pm_[a-zA-Z0-9]+$/).describe("Stripe payment method ID (pm_\u2026)");
2864
+ var ApiKeyRecordIdSchema = z.string().length(24).regex(/^[0-9a-f]+$/i).describe("API key record ID (24-character hex)");
2842
2865
  var WorkflowMemoryParamsSchema = z.object({
2843
2866
  enableSharedTestMemory: z.boolean().optional().describe("Override to enable/disable SharedTestMemory for this workflow run"),
2844
2867
  enableEverMemOS: z.boolean().optional().describe("Override to enable/disable EverMemOS for this workflow run")
@@ -2846,39 +2869,46 @@ var WorkflowMemoryParamsSchema = z.object({
2846
2869
  var WorkflowParamsSchema = z.object({
2847
2870
  memory: WorkflowMemoryParamsSchema.optional().describe("Per-run memory override settings")
2848
2871
  }).passthrough().optional().describe("Optional workflow parameters for workflow-level overrides");
2872
+ var TokenUsageFilterTypeSchema = z.enum([
2873
+ "project",
2874
+ "useCase",
2875
+ "testCase",
2876
+ "testScript",
2877
+ "actionScript"
2878
+ ]).describe("Token cost aggregation dimension");
2849
2879
  var ProjectCreateInputSchema = z.object({
2850
2880
  projectName: z.string().min(1).max(255).describe("Name of the project"),
2851
2881
  description: z.string().min(1).describe("Project description"),
2852
2882
  url: z.string().url().describe("Target website URL to test")
2853
2883
  });
2854
2884
  var ProjectGetInputSchema = z.object({
2855
- projectId: IdSchema.describe("Project ID to retrieve")
2885
+ projectId: IdSchema.describe("Project ID (UUID) to retrieve")
2856
2886
  });
2857
2887
  var ProjectDeleteInputSchema = z.object({
2858
- projectId: IdSchema.describe("Project ID to delete")
2888
+ projectId: IdSchema.describe("Project ID (UUID) to delete")
2859
2889
  });
2860
2890
  var ProjectUpdateInputSchema = z.object({
2861
- projectId: IdSchema.describe("Project ID to update"),
2891
+ projectId: IdSchema.describe("Project ID (UUID) to update"),
2862
2892
  projectName: z.string().min(1).max(255).optional().describe("New project name"),
2863
2893
  description: z.string().optional().describe("Updated description"),
2864
2894
  url: z.string().url().optional().describe("Updated target URL")
2865
2895
  });
2866
2896
  var ProjectListInputSchema = PaginationInputSchema.extend({});
2867
2897
  var PrdFileUploadInputSchema = z.object({
2868
- projectId: IdSchema.describe("Project ID to associate the PRD file with"),
2898
+ projectId: IdSchema.describe("Project ID (UUID) to associate the PRD file with"),
2869
2899
  fileName: z.string().min(1).describe("Name of the file"),
2870
2900
  contentBase64: z.string().min(1).describe("Base64-encoded file content"),
2871
2901
  contentType: z.string().optional().describe("MIME type of the file")
2872
2902
  });
2873
2903
  var PrdFileListInputSchema = z.object({
2874
- projectId: IdSchema.describe("Project ID to list PRD files for")
2904
+ projectId: IdSchema.describe("Project ID (UUID) to list PRD files for")
2875
2905
  });
2876
2906
  var PrdFileDeleteInputSchema = z.object({
2877
- projectId: IdSchema.describe("Project ID"),
2878
- prdFileId: IdSchema.describe("PRD file ID to delete")
2907
+ projectId: IdSchema.describe("Project ID (UUID)"),
2908
+ prdFileId: IdSchema.describe("PRD file ID (UUID) to delete")
2879
2909
  });
2880
2910
  var PrdFileProcessStartInputSchema = z.object({
2881
- projectId: IdSchema.describe("Project ID to process PRD files for"),
2911
+ projectId: IdSchema.describe("Project ID (UUID) to process PRD files for"),
2882
2912
  name: z.string().min(1).describe("Workflow name"),
2883
2913
  description: z.string().min(1).describe("Description of the PRD processing workflow"),
2884
2914
  prdFilePath: z.string().min(1).describe("Storage path of the uploaded PRD file (from upload response)"),
@@ -2888,75 +2918,75 @@ var PrdFileProcessStartInputSchema = z.object({
2888
2918
  fileSize: z.number().int().min(0).describe("Size of the PRD file in bytes (from upload response)")
2889
2919
  });
2890
2920
  var PrdFileProcessLatestRunInputSchema = z.object({
2891
- workflowRuntimeId: IdSchema.describe("PRD processing workflow runtime ID")
2921
+ workflowRuntimeId: IdSchema.describe("PRD processing workflow runtime ID (UUID)")
2892
2922
  });
2893
2923
  var SecretListInputSchema = z.object({
2894
- projectId: IdSchema.describe("Project ID to list secrets for")
2924
+ projectId: IdSchema.describe("Project ID (UUID) to list secrets for")
2895
2925
  });
2896
2926
  var SecretCreateInputSchema = z.object({
2897
- projectId: IdSchema.describe("Project ID to create the secret for"),
2927
+ projectId: IdSchema.describe("Project ID (UUID) to create the secret for"),
2898
2928
  name: z.string().min(1).describe("Secret name/key"),
2899
2929
  value: z.string().min(1).describe("Secret value"),
2900
2930
  description: z.string().min(1).describe("Human-readable description for selection guidance"),
2901
2931
  source: z.enum(["user", "agent"]).optional().describe("Source of the secret: 'user' for user-provided credentials, 'agent' for agent-generated credentials")
2902
2932
  });
2903
2933
  var SecretGetInputSchema = z.object({
2904
- secretId: IdSchema.describe("Secret ID to retrieve")
2934
+ secretId: IdSchema.describe("Secret ID (UUID) to retrieve")
2905
2935
  });
2906
2936
  var SecretUpdateInputSchema = z.object({
2907
- secretId: IdSchema.describe("Secret ID to update"),
2937
+ secretId: IdSchema.describe("Secret ID (UUID) to update"),
2908
2938
  name: z.string().min(1).optional().describe("Updated secret name"),
2909
2939
  value: z.string().min(1).optional().describe("Updated secret value"),
2910
2940
  description: z.string().optional().describe("Updated description")
2911
2941
  });
2912
2942
  var SecretDeleteInputSchema = z.object({
2913
- secretId: IdSchema.describe("Secret ID to delete")
2943
+ secretId: IdSchema.describe("Secret ID (UUID) to delete")
2914
2944
  });
2915
2945
  var UseCaseDiscoveryMemoryGetInputSchema = z.object({
2916
- projectId: IdSchema.describe("Project ID to get use case discovery memory for")
2946
+ projectId: IdSchema.describe("Project ID (UUID) to get use case discovery memory for")
2917
2947
  });
2918
2948
  var UseCaseCandidatesApproveInputSchema = z.object({
2919
- projectId: IdSchema.describe("Project ID"),
2949
+ projectId: IdSchema.describe("Project ID (UUID)"),
2920
2950
  approvedCandidateIds: z.array(IdSchema).min(1).describe("IDs of candidates to approve/graduate")
2921
2951
  });
2922
2952
  var UseCaseListInputSchema = z.object({
2923
- projectId: IdSchema.describe("Project ID to list use cases for")
2953
+ projectId: IdSchema.describe("Project ID (UUID) to list use cases for")
2924
2954
  }).merge(PaginationInputSchema);
2925
2955
  var UseCaseGetInputSchema = z.object({
2926
- useCaseId: IdSchema.describe("Use case ID to retrieve")
2956
+ useCaseId: IdSchema.describe("Use case ID (UUID) to retrieve")
2927
2957
  });
2928
2958
  var UseCasePromptPreviewInputSchema = z.object({
2929
- projectId: IdSchema.describe("Project ID to generate use case for"),
2959
+ projectId: IdSchema.describe("Project ID (UUID) to generate use case for"),
2930
2960
  instruction: z.string().min(1).describe("Natural language instruction describing the use case (e.g., 'As a logged-in user, I can add items to cart')")
2931
2961
  });
2932
2962
  var UseCaseCreateFromPromptsInputSchema = z.object({
2933
- projectId: IdSchema.describe("Project ID to create use cases for"),
2963
+ projectId: IdSchema.describe("Project ID (UUID) to create use cases for"),
2934
2964
  prompts: z.array(z.object({
2935
2965
  instruction: z.string().min(1).describe("Natural language instruction describing the use case")
2936
2966
  })).min(1).describe("Array of prompts to generate use cases from")
2937
2967
  });
2938
2968
  var UseCaseUpdateFromPromptInputSchema = z.object({
2939
- projectId: IdSchema.describe("Project ID"),
2940
- useCaseId: IdSchema.describe("Use case ID to update"),
2969
+ projectId: IdSchema.describe("Project ID (UUID)"),
2970
+ useCaseId: IdSchema.describe("Use case ID (UUID) to update"),
2941
2971
  instruction: z.string().min(1).describe("Natural language instruction to regenerate the use case from")
2942
2972
  });
2943
2973
  var TestCaseListInputSchema = z.object({
2944
- projectId: IdSchema.describe("Project ID to list test cases for")
2974
+ projectId: IdSchema.describe("Project ID (UUID) to list test cases for")
2945
2975
  }).merge(PaginationInputSchema);
2946
2976
  var TestCaseGetInputSchema = z.object({
2947
- testCaseId: IdSchema.describe("Test case ID to retrieve")
2977
+ testCaseId: IdSchema.describe("Test case ID (UUID) to retrieve")
2948
2978
  });
2949
2979
  var TestCaseListByUseCaseInputSchema = z.object({
2950
- useCaseId: IdSchema.describe("Use case ID to list test cases for")
2980
+ useCaseId: IdSchema.describe("Use case ID (UUID) to list test cases for")
2951
2981
  });
2952
2982
  var TestCaseGenerateFromPromptInputSchema = z.object({
2953
- projectId: IdSchema.describe("Project ID"),
2954
- useCaseId: IdSchema.describe("Use case ID to generate test cases for"),
2983
+ projectId: IdSchema.describe("Project ID (UUID)"),
2984
+ useCaseId: IdSchema.describe("Use case ID (UUID) to generate test cases for"),
2955
2985
  instruction: z.string().min(1).describe("Natural language instruction describing the test cases to generate")
2956
2986
  });
2957
2987
  var TestCaseCreateInputSchema = z.object({
2958
- projectId: IdSchema.describe("Project ID"),
2959
- useCaseId: IdSchema.describe("Use case ID to associate the test case with"),
2988
+ projectId: IdSchema.describe("Project ID (UUID)"),
2989
+ useCaseId: IdSchema.describe("Use case ID (UUID) to associate the test case with"),
2960
2990
  title: z.string().min(1).describe("Test case title"),
2961
2991
  description: z.string().min(1).describe("Detailed description of what the test case validates"),
2962
2992
  goal: z.string().min(1).describe("Concise, measurable goal of the test"),
@@ -2970,43 +3000,43 @@ var TestCaseCreateInputSchema = z.object({
2970
3000
  automated: z.boolean().optional().describe("Whether this test case is automated (default: true)")
2971
3001
  });
2972
3002
  var TestScriptListInputSchema = z.object({
2973
- projectId: IdSchema.describe("Project ID to list test scripts for"),
2974
- testCaseId: IdSchema.optional().describe("Optional test case ID to filter scripts by")
3003
+ projectId: IdSchema.describe("Project ID (UUID) to list test scripts for"),
3004
+ testCaseId: IdSchema.optional().describe("Optional test case ID (UUID) to filter scripts by")
2975
3005
  }).merge(PaginationInputSchema);
2976
3006
  var TestScriptGetInputSchema = z.object({
2977
- testScriptId: IdSchema.describe("Test script ID to retrieve")
3007
+ testScriptId: IdSchema.describe("Test script ID (UUID) to retrieve")
2978
3008
  });
2979
3009
  var TestScriptListPaginatedInputSchema = z.object({
2980
- projectId: IdSchema.describe("Project ID to list test scripts for")
3010
+ projectId: IdSchema.describe("Project ID (UUID) to list test scripts for")
2981
3011
  }).merge(PaginationInputSchema);
2982
3012
  var ActionScriptGetInputSchema = z.object({
2983
- actionScriptId: IdSchema.describe("Action script ID to retrieve")
3013
+ actionScriptId: IdSchema.describe("Action script ID (UUID) to retrieve")
2984
3014
  });
2985
3015
  var WorkflowStartWebsiteScanInputSchema = z.object({
2986
- projectId: IdSchema.describe("Project ID to scan"),
3016
+ projectId: IdSchema.describe("Project ID (UUID) to scan"),
2987
3017
  url: z.string().url().describe("Website URL to scan"),
2988
3018
  description: z.string().min(1).describe("Description of what to scan/discover"),
2989
3019
  archiveUnapproved: z.boolean().optional().describe("Whether to archive unapproved candidates before scanning"),
2990
3020
  workflowParams: WorkflowParamsSchema
2991
3021
  });
2992
3022
  var WorkflowListRuntimesInputSchema = z.object({
2993
- projectId: IdSchema.optional().describe("Filter by project ID")
3023
+ projectId: IdSchema.optional().describe("Filter by project ID (UUID)")
2994
3024
  });
2995
3025
  var WorkflowGetLatestRunInputSchema = z.object({
2996
- workflowRuntimeId: IdSchema.describe("Workflow runtime ID")
3026
+ workflowRuntimeId: IdSchema.describe("Workflow runtime ID (UUID)")
2997
3027
  });
2998
3028
  var WorkflowStartTestCaseDetectionInputSchema = z.object({
2999
- projectId: IdSchema.describe("Project ID"),
3000
- useCaseId: IdSchema.describe("Use case ID to detect test cases for"),
3029
+ projectId: IdSchema.describe("Project ID (UUID)"),
3030
+ useCaseId: IdSchema.describe("Use case ID (UUID) to detect test cases for"),
3001
3031
  name: z.string().min(1).describe("Workflow name"),
3002
3032
  description: z.string().min(1).describe("Workflow description"),
3003
3033
  url: z.string().url().describe("Target website URL"),
3004
3034
  workflowParams: WorkflowParamsSchema
3005
3035
  });
3006
3036
  var WorkflowStartTestScriptGenerationInputSchema = z.object({
3007
- projectId: IdSchema.describe("Project ID"),
3008
- useCaseId: IdSchema.describe("Use case ID"),
3009
- testCaseId: IdSchema.describe("Test case ID"),
3037
+ projectId: IdSchema.describe("Project ID (UUID)"),
3038
+ useCaseId: IdSchema.describe("Use case ID (UUID)"),
3039
+ testCaseId: IdSchema.describe("Test case ID (UUID)"),
3010
3040
  name: z.string().min(1).describe("Workflow name"),
3011
3041
  url: z.string().url().describe("Target website URL"),
3012
3042
  goal: z.string().min(1).describe("Test goal"),
@@ -3016,57 +3046,57 @@ var WorkflowStartTestScriptGenerationInputSchema = z.object({
3016
3046
  workflowParams: WorkflowParamsSchema
3017
3047
  });
3018
3048
  var WorkflowGetLatestScriptGenByTestCaseInputSchema = z.object({
3019
- testCaseId: IdSchema.describe("Test case ID")
3049
+ testCaseId: IdSchema.describe("Test case ID (UUID)")
3020
3050
  });
3021
3051
  var WorkflowStartTestScriptReplayInputSchema = z.object({
3022
- projectId: IdSchema.describe("Project ID"),
3023
- useCaseId: IdSchema.describe("Use case ID"),
3024
- testCaseId: IdSchema.describe("Test case ID"),
3025
- testScriptId: IdSchema.describe("Test script ID to replay"),
3052
+ projectId: IdSchema.describe("Project ID (UUID)"),
3053
+ useCaseId: IdSchema.describe("Use case ID (UUID)"),
3054
+ testCaseId: IdSchema.describe("Test case ID (UUID)"),
3055
+ testScriptId: IdSchema.describe("Test script ID (UUID) to replay"),
3026
3056
  name: z.string().min(1).describe("Workflow name"),
3027
3057
  workflowParams: WorkflowParamsSchema
3028
3058
  });
3029
3059
  var WorkflowStartTestScriptReplayBulkInputSchema = z.object({
3030
- projectId: IdSchema.describe("Project ID"),
3060
+ projectId: IdSchema.describe("Project ID (UUID)"),
3031
3061
  name: z.string().min(1).describe("Workflow name"),
3032
3062
  intervalSec: z.number().int().describe("Interval in seconds (-1 for one-time / on-demand)"),
3033
- useCaseId: IdSchema.optional().describe("Optional: only replay test cases under this use case"),
3063
+ useCaseId: IdSchema.optional().describe("Optional: only replay test cases under this use case (UUID)"),
3034
3064
  namePrefix: z.string().optional().describe("Optional: prefix for generated workflow names"),
3035
3065
  limit: z.number().int().optional().describe("Optional: limit number of test cases to replay"),
3036
- testCaseIds: z.array(IdSchema).optional().describe("Optional: targeted test cases to replay"),
3066
+ testCaseIds: z.array(IdSchema).optional().describe("Optional: targeted test case UUIDs to replay"),
3037
3067
  repeatPerTestCase: z.number().int().optional().describe("Optional: repeat count per test case"),
3038
3068
  workflowParams: WorkflowParamsSchema
3039
3069
  });
3040
3070
  var WorkflowGetReplayBulkBatchSummaryInputSchema = z.object({
3041
- runBatchId: IdSchema.describe("Run batch ID")
3071
+ runBatchId: RunBatchIdSchema.describe("Run batch ID (UUID) from bulk replay workflow")
3042
3072
  });
3043
3073
  var WorkflowCancelRunInputSchema = z.object({
3044
- workflowRunId: IdSchema.describe("Workflow run ID to cancel")
3074
+ workflowRunId: IdSchema.describe("Workflow run ID (UUID) to cancel")
3045
3075
  });
3046
3076
  var WorkflowCancelRuntimeInputSchema = z.object({
3047
- workflowRuntimeId: IdSchema.describe("Workflow runtime ID to cancel")
3077
+ workflowRuntimeId: IdSchema.describe("Workflow runtime ID (UUID) to cancel")
3048
3078
  });
3049
3079
  var ProjectTestResultsSummaryInputSchema = z.object({
3050
- projectId: IdSchema.describe("Project ID to get test results summary for")
3080
+ projectId: IdSchema.describe("Project ID (UUID) to get test results summary for")
3051
3081
  });
3052
3082
  var ProjectTestScriptsSummaryInputSchema = z.object({
3053
- projectId: IdSchema.describe("Project ID to get test scripts summary for")
3083
+ projectId: IdSchema.describe("Project ID (UUID) to get test scripts summary for")
3054
3084
  });
3055
3085
  var ProjectTestRunsSummaryInputSchema = z.object({
3056
- projectId: IdSchema.describe("Project ID to get test runs summary for")
3086
+ projectId: IdSchema.describe("Project ID (UUID) to get test runs summary for")
3057
3087
  });
3058
3088
  var ReportStatsSummaryInputSchema = z.object({
3059
- projectId: IdSchema.describe("Project ID to get report stats for")
3089
+ projectId: IdSchema.describe("Project ID (UUID) to get report stats for")
3060
3090
  });
3061
3091
  var ReportCostQueryInputSchema = z.object({
3062
- projectId: IdSchema.describe("Project ID"),
3092
+ projectId: IdSchema.describe("Project ID (UUID)"),
3063
3093
  startDateKey: z.string().optional().describe("Start date key (YYYYMMDD)"),
3064
3094
  endDateKey: z.string().optional().describe("End date key (YYYYMMDD)"),
3065
- filterType: z.string().optional().describe("Filter type for cost breakdown"),
3066
- filterIds: z.array(z.unknown()).optional().describe("Filter IDs")
3095
+ filterType: TokenUsageFilterTypeSchema.optional().describe("Aggregation dimension for cost breakdown"),
3096
+ filterIds: z.array(IdSchema).optional().describe("Entity UUIDs matching filterType (project / use case / test case / test script / action script)")
3067
3097
  });
3068
3098
  var ReportPreferencesUpsertInputSchema = z.object({
3069
- projectId: IdSchema.describe("Project ID"),
3099
+ projectId: IdSchema.describe("Project ID (UUID)"),
3070
3100
  channels: z.array(z.unknown()).describe("Delivery channels to enable"),
3071
3101
  emails: z.array(z.unknown()).optional().describe("Email addresses for delivery"),
3072
3102
  phones: z.array(z.unknown()).optional().describe("Phone numbers for SMS delivery"),
@@ -3074,11 +3104,11 @@ var ReportPreferencesUpsertInputSchema = z.object({
3074
3104
  defaultExportFormat: z.string().optional().describe("Default export format (pdf, html, etc.)")
3075
3105
  });
3076
3106
  var ReportFinalGenerateInputSchema = z.object({
3077
- projectId: IdSchema.describe("Project ID to generate report for"),
3107
+ projectId: IdSchema.describe("Project ID (UUID) to generate report for"),
3078
3108
  exportFormat: z.enum(["pdf", "html", "markdown"]).describe("Export format for the report")
3079
3109
  });
3080
3110
  var WalletTopUpInputSchema = z.object({
3081
- packageId: IdSchema.describe("Token package ID to purchase"),
3111
+ packageId: TokenPackageIdSchema.describe("Token package ID to purchase"),
3082
3112
  checkoutSuccessCallback: z.string().url().describe("URL to redirect to when checkout succeeds"),
3083
3113
  checkoutCancelCallback: z.string().url().describe("URL to redirect to when checkout is canceled")
3084
3114
  });
@@ -3087,21 +3117,21 @@ var WalletPaymentMethodCreateSetupSessionInputSchema = z.object({
3087
3117
  checkoutCancelCallback: z.string().url().describe("URL to redirect to when payment method setup is canceled")
3088
3118
  });
3089
3119
  var WalletAutoTopUpSetPaymentMethodInputSchema = z.object({
3090
- paymentMethodId: IdSchema.describe("Saved Stripe payment method ID (e.g., pm_xxx)")
3120
+ paymentMethodId: StripePaymentMethodIdSchema.describe("Saved Stripe payment method ID")
3091
3121
  });
3092
3122
  var WalletPaymentMethodListInputSchema = z.object({});
3093
3123
  var WalletAutoTopUpUpdateInputSchema = z.object({
3094
3124
  enabled: z.boolean().describe("Whether auto top-up is enabled"),
3095
3125
  topUpTriggerTokenThreshold: z.number().int().min(0).describe("Token balance threshold to trigger auto top-up"),
3096
- packageId: IdSchema.describe("Token package ID to purchase when auto top-up triggers")
3126
+ packageId: TokenPackageIdSchema.describe("Token package ID to purchase when auto top-up triggers")
3097
3127
  });
3098
3128
  var RecommendScheduleInputSchema = z.object({
3099
- projectId: IdSchema.optional().describe("Project ID for context"),
3129
+ projectId: IdSchema.optional().describe("Project ID (UUID) for context"),
3100
3130
  testFrequency: z.enum(["daily", "weekly", "onDemand"]).optional().describe("Desired test frequency"),
3101
3131
  timezone: z.string().optional().describe("Timezone for scheduling")
3102
3132
  });
3103
3133
  var RecommendCicdSetupInputSchema = z.object({
3104
- projectId: IdSchema.optional().describe("Project ID for context"),
3134
+ projectId: IdSchema.optional().describe("Project ID (UUID) for context"),
3105
3135
  repositoryProvider: z.enum(["github", "azureDevOps", "gitlab", "other"]).optional().describe("Git repository provider"),
3106
3136
  cadence: z.enum(["onPullRequest", "nightly", "onDemand"]).optional().describe("CI/CD trigger cadence")
3107
3137
  });
@@ -3111,10 +3141,10 @@ var ApiKeyCreateInputSchema = z.object({
3111
3141
  });
3112
3142
  var ApiKeyListInputSchema = z.object({});
3113
3143
  var ApiKeyGetInputSchema = z.object({
3114
- apiKeyId: IdSchema.describe("ID of the API key to retrieve")
3144
+ apiKeyId: ApiKeyRecordIdSchema.describe("ID of the API key record to retrieve")
3115
3145
  });
3116
3146
  var ApiKeyRevokeInputSchema = z.object({
3117
- apiKeyId: IdSchema.describe("ID of the API key to revoke")
3147
+ apiKeyId: ApiKeyRecordIdSchema.describe("ID of the API key record to revoke")
3118
3148
  });
3119
3149
  var AuthLoginInputSchema = z.object({
3120
3150
  waitForCompletion: z.boolean().optional().describe("Whether to wait for browser login completion before returning. Default: true"),
@@ -3125,7 +3155,7 @@ var AuthPollInputSchema = z.object({
3125
3155
  });
3126
3156
  var EmptyInputSchema = z.object({});
3127
3157
 
3128
- // packages/mcps/src/mcp/qa/types.ts
3158
+ // packages/mcps/src/mcp/e2e/types.ts
3129
3159
  var McpErrorCode = /* @__PURE__ */ ((McpErrorCode2) => {
3130
3160
  McpErrorCode2["UNAUTHORIZED"] = "UNAUTHORIZED";
3131
3161
  McpErrorCode2["FORBIDDEN"] = "FORBIDDEN";
@@ -3162,8 +3192,8 @@ var PromptServiceClient = class {
3162
3192
  requestTimeoutMs;
3163
3193
  constructor() {
3164
3194
  const config = getConfig();
3165
- this.baseUrl = config.qa.promptServiceBaseUrl;
3166
- this.requestTimeoutMs = config.qa.requestTimeoutMs;
3195
+ this.baseUrl = config.e2e.promptServiceBaseUrl;
3196
+ this.requestTimeoutMs = config.e2e.requestTimeoutMs;
3167
3197
  this.httpClient = axios.create({
3168
3198
  baseURL: this.baseUrl,
3169
3199
  timeout: this.requestTimeoutMs,
@@ -3394,13 +3424,13 @@ function getPromptServiceClient() {
3394
3424
  return clientInstance;
3395
3425
  }
3396
3426
 
3397
- // packages/mcps/src/mcp/tools/qa/tool-registry.ts
3427
+ // packages/mcps/src/mcp/tools/e2e/tool-registry.ts
3398
3428
  var MUGGLE_TEST_PREFIX = "/v1/protected/muggle-test";
3399
- var getWorkflowTimeoutMs = () => getConfig().qa.workflowTimeoutMs;
3429
+ var getWorkflowTimeoutMs = () => getConfig().e2e.workflowTimeoutMs;
3400
3430
  var projectTools = [
3401
3431
  {
3402
3432
  name: "muggle-remote-project-create",
3403
- description: "Create a QA testing project to organize browser tests for a web app. A project groups test scenarios (use cases), specific test steps (test cases), and replayable browser scripts (test scripts) for one application. Create a project first before generating or running any QA tests.",
3433
+ description: "Create an E2E acceptance testing project to organize browser tests for a web app. A project groups test scenarios (use cases), specific test steps (test cases), and replayable browser scripts (test scripts) for one application. Create a project first before generating or running any E2E tests.",
3404
3434
  inputSchema: ProjectCreateInputSchema,
3405
3435
  mapToUpstream: (input) => {
3406
3436
  const data = input;
@@ -3601,7 +3631,7 @@ var testCaseTools = [
3601
3631
  },
3602
3632
  {
3603
3633
  name: "muggle-remote-test-case-generate-from-prompt",
3604
- description: "Generate QA test cases from a plain-English description of what to test \u2014 e.g., 'test the signup flow with invalid email' or 'verify the checkout handles empty cart'. Returns preview test cases that can be used to generate executable browser test scripts.",
3634
+ description: "Generate E2E acceptance test cases from a plain-English description of what to test \u2014 e.g., 'test the signup flow with invalid email' or 'verify the checkout handles empty cart'. Returns preview test cases that can be used to generate executable browser test scripts.",
3605
3635
  inputSchema: TestCaseGenerateFromPromptInputSchema,
3606
3636
  mapToUpstream: (input) => {
3607
3637
  const data = input;
@@ -3697,7 +3727,7 @@ var actionScriptTools = [
3697
3727
  var workflowTools = [
3698
3728
  {
3699
3729
  name: "muggle-remote-workflow-start-website-scan",
3700
- description: "Scan a website to automatically discover testable user flows and UI interactions. Crawls the site and identifies use cases like signup, login, search, checkout, form submissions, and navigation patterns. Use this when setting up QA testing for a site without predefined test cases.",
3730
+ description: "Scan a website to automatically discover testable user flows and UI interactions. Crawls the site and identifies use cases like signup, login, search, checkout, form submissions, and navigation patterns. Use this when setting up E2E acceptance testing for a site without predefined test cases.",
3701
3731
  inputSchema: WorkflowStartWebsiteScanInputSchema,
3702
3732
  mapToUpstream: (input) => {
3703
3733
  const data = input;
@@ -4520,7 +4550,7 @@ var apiKeyTools = [
4520
4550
  },
4521
4551
  {
4522
4552
  name: "muggle-remote-auth-api-key-revoke",
4523
- description: "Revoke an API key. The key will immediately stop working. Use qa_auth_api_key_list to find the key ID first.",
4553
+ description: "Revoke an API key. The key will immediately stop working. Use muggle-remote-auth-api-key-list to find the key ID first.",
4524
4554
  inputSchema: ApiKeyRevokeInputSchema,
4525
4555
  mapToUpstream: (input) => {
4526
4556
  const data = input;
@@ -4739,15 +4769,15 @@ async function executeQaTool(toolName, input, correlationId) {
4739
4769
  }
4740
4770
  }
4741
4771
 
4742
- // packages/mcps/src/mcp/tools/qa/index.ts
4743
- var qa_exports = {};
4744
- __export(qa_exports, {
4772
+ // packages/mcps/src/mcp/tools/e2e/index.ts
4773
+ var e2e_exports = {};
4774
+ __export(e2e_exports, {
4745
4775
  allQaToolDefinitions: () => allQaToolDefinitions,
4746
4776
  executeQaTool: () => executeQaTool,
4747
4777
  getQaToolByName: () => getQaToolByName
4748
4778
  });
4749
4779
 
4750
- // packages/mcps/src/mcp/qa/index.ts
4780
+ // packages/mcps/src/mcp/e2e/index.ts
4751
4781
  function getQaTools() {
4752
4782
  return allQaToolDefinitions.map((tool) => ({
4753
4783
  name: tool.name,
@@ -4781,6 +4811,7 @@ __export(local_exports2, {
4781
4811
  LocalTestScriptStatus: () => LocalTestScriptStatus,
4782
4812
  LocalWorkflowFileEntityType: () => LocalWorkflowFileEntityType,
4783
4813
  LocalWorkflowRunStatus: () => LocalWorkflowRunStatus,
4814
+ MuggleEntityIdSchema: () => MuggleEntityIdSchema,
4784
4815
  PublishTestScriptInputSchema: () => PublishTestScriptInputSchema,
4785
4816
  RunResultGetInputSchema: () => RunResultGetInputSchema,
4786
4817
  RunResultListInputSchema: () => RunResultListInputSchema,
@@ -4825,7 +4856,7 @@ var AuthPollInputSchema2 = z.object({
4825
4856
  var EmptyInputSchema2 = z.object({});
4826
4857
  var TestCaseDetailsSchema = z.object({
4827
4858
  /** Cloud test case ID. */
4828
- id: z.string().min(1).describe("Cloud test case ID"),
4859
+ id: MuggleEntityIdSchema.describe("Cloud test case ID (UUID)"),
4829
4860
  /** Test case title. */
4830
4861
  title: z.string().min(1).describe("Test case title"),
4831
4862
  /** Test goal. */
@@ -4839,37 +4870,39 @@ var TestCaseDetailsSchema = z.object({
4839
4870
  /** Original cloud URL (for reference, replaced by localUrl). */
4840
4871
  url: z.string().url().optional().describe("Original cloud URL (replaced by localUrl during execution)"),
4841
4872
  /** Cloud project ID (required for electron workflow context). */
4842
- projectId: z.string().min(1).describe("Cloud project ID"),
4873
+ projectId: MuggleEntityIdSchema.describe("Cloud project ID (UUID)"),
4843
4874
  /** Cloud use case ID (required for electron workflow context). */
4844
- useCaseId: z.string().min(1).describe("Cloud use case ID")
4875
+ useCaseId: MuggleEntityIdSchema.describe("Cloud use case ID (UUID)")
4845
4876
  });
4846
4877
  var TestScriptDetailsSchema = z.object({
4847
4878
  /** Cloud test script ID. */
4848
- id: z.string().min(1).describe("Cloud test script ID"),
4879
+ id: MuggleEntityIdSchema.describe("Cloud test script ID (UUID)"),
4849
4880
  /** Script name. */
4850
4881
  name: z.string().min(1).describe("Test script name"),
4851
4882
  /** Cloud test case ID this script belongs to. */
4852
- testCaseId: z.string().min(1).describe("Cloud test case ID this script was generated from"),
4883
+ testCaseId: MuggleEntityIdSchema.describe("Cloud test case ID (UUID) this script was generated from"),
4853
4884
  /** Action script ID reference (use muggle-remote-action-script-get to fetch content). */
4854
- actionScriptId: z.string().min(1).describe("Action script ID - use muggle-remote-action-script-get to fetch the full script"),
4885
+ actionScriptId: MuggleEntityIdSchema.describe(
4886
+ "Action script ID (UUID) \u2014 use muggle-remote-action-script-get to fetch the full script"
4887
+ ),
4855
4888
  /** Original cloud URL (for reference, replaced by localUrl). */
4856
4889
  url: z.string().url().optional().describe("Original cloud URL (replaced by localUrl during execution)"),
4857
4890
  /** Cloud project ID (required for electron workflow context). */
4858
- projectId: z.string().min(1).describe("Cloud project ID"),
4891
+ projectId: MuggleEntityIdSchema.describe("Cloud project ID (UUID)"),
4859
4892
  /** Cloud use case ID (required for electron workflow context). */
4860
- useCaseId: z.string().min(1).describe("Cloud use case ID")
4893
+ useCaseId: MuggleEntityIdSchema.describe("Cloud use case ID (UUID)")
4861
4894
  });
4862
4895
  var ExecuteTestGenerationInputSchema = z.object({
4863
- /** Test case details from qa_test_case_get. */
4864
- testCase: TestCaseDetailsSchema.describe("Test case details obtained from qa_test_case_get"),
4896
+ /** Test case details from muggle-remote-test-case-get. */
4897
+ testCase: TestCaseDetailsSchema.describe("Test case details obtained from muggle-remote-test-case-get"),
4865
4898
  /** Local URL to test against. */
4866
4899
  localUrl: z.string().url().describe("Local URL to test against (e.g., http://localhost:3000)"),
4867
4900
  /** Explicit approval to launch electron-app. */
4868
4901
  approveElectronAppLaunch: z.boolean().describe("Set to true after the user explicitly approves launching electron-app"),
4869
4902
  /** Optional timeout. */
4870
4903
  timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds (default: 300000 = 5 min)"),
4871
- /** Show the electron-app UI during execution. */
4872
- showUi: z.boolean().optional().describe("Show the electron-app UI during execution (default: false, runs headless)")
4904
+ /** Show the electron-app UI during execution. Ask the user before approving; true = visible window, false or omit = headless. */
4905
+ showUi: z.boolean().optional().describe("Show the electron-app UI during generation. Ask the user: true to watch the window, false or omit for headless.")
4873
4906
  });
4874
4907
  var ExecuteReplayInputSchema = z.object({
4875
4908
  /** Test script metadata from muggle-remote-test-script-get. */
@@ -4882,28 +4915,28 @@ var ExecuteReplayInputSchema = z.object({
4882
4915
  approveElectronAppLaunch: z.boolean().describe("Set to true after the user explicitly approves launching electron-app"),
4883
4916
  /** Optional timeout. */
4884
4917
  timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds (default: 180000 = 3 min)"),
4885
- /** Show the electron-app UI during execution. */
4886
- showUi: z.boolean().optional().describe("Show the electron-app UI during execution (default: false, runs headless)")
4918
+ /** Show the electron-app UI during execution. Ask the user before approving; true = visible window, false or omit = headless. */
4919
+ showUi: z.boolean().optional().describe("Show the electron-app UI during replay. Ask the user: true to watch the window, false or omit for headless.")
4887
4920
  });
4888
4921
  var CancelExecutionInputSchema = z.object({
4889
- runId: z.string().min(1).describe("Run ID to cancel")
4922
+ runId: MuggleEntityIdSchema.describe("Run ID (UUID) to cancel")
4890
4923
  });
4891
4924
  var RunResultListInputSchema = z.object({
4892
- cloudTestCaseId: z.string().optional().describe("Optional cloud test case ID to filter by"),
4925
+ cloudTestCaseId: MuggleEntityIdSchema.optional().describe("Optional cloud test case ID (UUID) to filter by"),
4893
4926
  limit: z.number().int().positive().optional().describe("Maximum results to return (default: 20)")
4894
4927
  });
4895
4928
  var RunResultGetInputSchema = z.object({
4896
- runId: z.string().min(1).describe("Run result ID to retrieve")
4929
+ runId: MuggleEntityIdSchema.describe("Run result ID (UUID) to retrieve")
4897
4930
  });
4898
4931
  var TestScriptListInputSchema2 = z.object({
4899
- cloudTestCaseId: z.string().optional().describe("Optional cloud test case ID to filter by")
4932
+ cloudTestCaseId: MuggleEntityIdSchema.optional().describe("Optional cloud test case ID (UUID) to filter by")
4900
4933
  });
4901
4934
  var TestScriptGetInputSchema2 = z.object({
4902
- testScriptId: z.string().min(1).describe("Test script ID to retrieve")
4935
+ testScriptId: MuggleEntityIdSchema.describe("Local stored test script ID (UUID) to retrieve")
4903
4936
  });
4904
4937
  var PublishTestScriptInputSchema = z.object({
4905
- runId: z.string().min(1).describe("Local run result ID from muggle_execute_test_generation"),
4906
- cloudTestCaseId: z.string().min(1).describe("Cloud test case ID to publish the script under")
4938
+ runId: MuggleEntityIdSchema.describe("Local run result ID (UUID) from muggle_execute_test_generation"),
4939
+ cloudTestCaseId: MuggleEntityIdSchema.describe("Cloud test case ID (UUID) to publish the script under")
4907
4940
  });
4908
4941
  var ListSessionsInputSchema = z.object({
4909
4942
  limit: z.number().int().positive().optional().describe("Maximum number of sessions to return. Defaults to 10.")
@@ -5123,14 +5156,15 @@ var testScriptGetTool = {
5123
5156
  };
5124
5157
  var executeTestGenerationTool = {
5125
5158
  name: "muggle-local-execute-test-generation",
5126
- description: "Generate a QA test script by launching a real browser against your web app. The browser navigates your app, executes the test case steps (like signing up, filling forms, clicking through flows), and produces a replayable test script with screenshots. Use this to create new browser tests for any user flow. Requires a test case (from muggle-remote-test-case-get) and a localhost URL. Launches an Electron browser \u2014 requires explicit approval via approveElectronAppLaunch. Runs headless by default; set showUi: true to watch.",
5159
+ description: "Generate an end-to-end (E2E) acceptance test script by launching a real browser against your web app. The browser navigates your app, executes the test case steps (like signing up, filling forms, clicking through flows), and produces a replayable test script with screenshots. Use this to create new browser tests for any user flow. Requires a test case (from muggle-remote-test-case-get) and a localhost URL. Launches an Electron browser \u2014 requires explicit approval via approveElectronAppLaunch. Before approving, ask the user whether they want a visible GUI; pass showUi: true to watch the window or showUi: false for headless (default when omitted).",
5127
5160
  inputSchema: ExecuteTestGenerationInputSchema,
5128
5161
  execute: async (ctx) => {
5129
5162
  const logger14 = createChildLogger2(ctx.correlationId);
5130
5163
  logger14.info("Executing muggle-local-execute-test-generation");
5131
5164
  const input = ExecuteTestGenerationInputSchema.parse(ctx.input);
5132
5165
  if (!input.approveElectronAppLaunch) {
5133
- const uiMode = input.showUi ? "with visible UI" : "headless (no UI)";
5166
+ const showUiExplicit = input.showUi !== void 0;
5167
+ const uiMode = input.showUi === true ? "visible GUI (showUi: true)" : "headless (showUi: false or omitted)";
5134
5168
  return {
5135
5169
  content: [
5136
5170
  "## Electron App Launch Required",
@@ -5138,22 +5172,28 @@ var executeTestGenerationTool = {
5138
5172
  "This tool will launch the electron-app to generate a test script.",
5139
5173
  "Please set `approveElectronAppLaunch: true` to proceed.",
5140
5174
  "",
5175
+ "**Visible GUI:** Ask the user whether they want to watch the Electron window during generation.",
5176
+ "- If **yes** \u2192 when approving, pass `showUi: true`.",
5177
+ "- If **no** \u2192 when approving, pass `showUi: false` (or omit `showUi`; generation runs headless).",
5178
+ "",
5179
+ showUiExplicit ? `**Current choice:** ${uiMode}` : "**Current choice:** not set \u2014 default on approval is headless unless you pass `showUi: true`.",
5180
+ "",
5141
5181
  `**Test Case:** ${input.testCase.title}`,
5142
5182
  `**Local URL:** ${input.localUrl}`,
5143
- `**UI Mode:** ${uiMode}`,
5144
5183
  "",
5145
- "**Note:** The electron-app will open a browser window and navigate to your test URL."
5184
+ "**Note:** The electron-app will navigate your test URL and record steps."
5146
5185
  ].join("\n"),
5147
5186
  isError: false,
5148
5187
  data: { requiresApproval: true }
5149
5188
  };
5150
5189
  }
5190
+ const showUi = input.showUi === true;
5151
5191
  try {
5152
5192
  const result = await executeTestGeneration({
5153
5193
  testCase: input.testCase,
5154
5194
  localUrl: input.localUrl,
5155
5195
  timeoutMs: input.timeoutMs,
5156
- showUi: input.showUi
5196
+ showUi
5157
5197
  });
5158
5198
  const content = [
5159
5199
  "## Test Generation " + (result.status === "passed" ? "Successful" : "Failed"),
@@ -5162,6 +5202,7 @@ var executeTestGenerationTool = {
5162
5202
  `**Test Script ID:** ${result.testScriptId}`,
5163
5203
  `**Status:** ${result.status}`,
5164
5204
  `**Duration:** ${result.executionTimeMs}ms`,
5205
+ `**UI:** ${showUi ? "visible GUI" : "headless"}`,
5165
5206
  result.errorMessage ? `**Error:** ${result.errorMessage}` : ""
5166
5207
  ].filter(Boolean).join("\n");
5167
5208
  return {
@@ -5178,14 +5219,15 @@ var executeTestGenerationTool = {
5178
5219
  };
5179
5220
  var executeReplayTool = {
5180
5221
  name: "muggle-local-execute-replay",
5181
- description: "Replay an existing QA test script in a real browser to verify your app still works correctly \u2014 use this for regression testing after code changes. The browser executes each saved step and captures screenshots so you can see what happened. Requires: (1) test script metadata from muggle-remote-test-script-get, (2) actionScript content from muggle-remote-action-script-get using the testScript.actionScriptId, and (3) a localhost URL. Launches an Electron browser \u2014 requires explicit approval via approveElectronAppLaunch. Runs headless by default; set showUi: true to watch.",
5222
+ description: "Replay an existing E2E acceptance test script in a real browser to verify your app still works correctly \u2014 use this for regression testing after code changes. The browser executes each saved step and captures screenshots so you can see what happened. Requires: (1) test script metadata from muggle-remote-test-script-get, (2) actionScript content from muggle-remote-action-script-get using the testScript.actionScriptId, and (3) a localhost URL. Launches an Electron browser \u2014 requires explicit approval via approveElectronAppLaunch. Before approving, ask the user whether they want a visible GUI; pass showUi: true to watch the window or showUi: false for headless (default when omitted).",
5182
5223
  inputSchema: ExecuteReplayInputSchema,
5183
5224
  execute: async (ctx) => {
5184
5225
  const logger14 = createChildLogger2(ctx.correlationId);
5185
5226
  logger14.info("Executing muggle-local-execute-replay");
5186
5227
  const input = ExecuteReplayInputSchema.parse(ctx.input);
5187
5228
  if (!input.approveElectronAppLaunch) {
5188
- const uiMode = input.showUi ? "with visible UI" : "headless (no UI)";
5229
+ const showUiExplicit = input.showUi !== void 0;
5230
+ const uiMode = input.showUi === true ? "visible GUI (showUi: true)" : "headless (showUi: false or omitted)";
5189
5231
  return {
5190
5232
  content: [
5191
5233
  "## Electron App Launch Required",
@@ -5193,24 +5235,30 @@ var executeReplayTool = {
5193
5235
  "This tool will launch the electron-app to replay a test script.",
5194
5236
  "Please set `approveElectronAppLaunch: true` to proceed.",
5195
5237
  "",
5238
+ "**Visible GUI:** Ask the user whether they want to watch the Electron window during replay.",
5239
+ "- If **yes** \u2192 when approving, pass `showUi: true`.",
5240
+ "- If **no** \u2192 when approving, pass `showUi: false` (or omit `showUi`; replay runs headless).",
5241
+ "",
5242
+ showUiExplicit ? `**Current choice:** ${uiMode}` : "**Current choice:** not set \u2014 default on approval is headless unless you pass `showUi: true`.",
5243
+ "",
5196
5244
  `**Test Script:** ${input.testScript.name}`,
5197
5245
  `**Local URL:** ${input.localUrl}`,
5198
5246
  `**Steps:** ${input.actionScript.length}`,
5199
- `**UI Mode:** ${uiMode}`,
5200
5247
  "",
5201
- "**Note:** The electron-app will open a browser window and execute the test steps."
5248
+ "**Note:** The electron-app will execute the test steps against your local URL."
5202
5249
  ].join("\n"),
5203
5250
  isError: false,
5204
5251
  data: { requiresApproval: true }
5205
5252
  };
5206
5253
  }
5254
+ const showUi = input.showUi === true;
5207
5255
  try {
5208
5256
  const result = await executeReplay({
5209
5257
  testScript: input.testScript,
5210
5258
  actionScript: input.actionScript,
5211
5259
  localUrl: input.localUrl,
5212
5260
  timeoutMs: input.timeoutMs,
5213
- showUi: input.showUi
5261
+ showUi
5214
5262
  });
5215
5263
  const content = [
5216
5264
  "## Test Replay " + (result.status === "passed" ? "Successful" : "Failed"),
@@ -5219,6 +5267,7 @@ var executeReplayTool = {
5219
5267
  `**Test Script ID:** ${result.testScriptId}`,
5220
5268
  `**Status:** ${result.status}`,
5221
5269
  `**Duration:** ${result.executionTimeMs}ms`,
5270
+ `**UI:** ${showUi ? "visible GUI" : "headless"}`,
5222
5271
  result.errorMessage ? `**Error:** ${result.errorMessage}` : ""
5223
5272
  ].filter(Boolean).join("\n");
5224
5273
  return {
@@ -5457,9 +5506,10 @@ function isLocalOnlyTool(toolName) {
5457
5506
  var mcp_exports = {};
5458
5507
  __export(mcp_exports, {
5459
5508
  agents: () => agents_exports,
5509
+ e2e: () => e2e_exports2,
5460
5510
  localQa: () => local_exports2,
5461
5511
  plugins: () => plugins_exports,
5462
- qa: () => qa_exports2,
5512
+ qa: () => e2e_exports2,
5463
5513
  skills: () => skills_exports,
5464
5514
  tools: () => tools_exports
5465
5515
  });
@@ -5467,8 +5517,9 @@ __export(mcp_exports, {
5467
5517
  // packages/mcps/src/mcp/tools/index.ts
5468
5518
  var tools_exports = {};
5469
5519
  __export(tools_exports, {
5520
+ e2e: () => e2e_exports,
5470
5521
  localQa: () => local_exports,
5471
- qa: () => qa_exports
5522
+ qa: () => e2e_exports
5472
5523
  });
5473
5524
 
5474
5525
  // packages/mcps/src/mcp/skills/index.ts
@@ -5483,11 +5534,15 @@ var agents_exports = {};
5483
5534
  // packages/mcps/src/index.ts
5484
5535
  var src_exports = {};
5485
5536
  __export(src_exports, {
5537
+ buildElectronAppChecksumsUrl: () => buildElectronAppChecksumsUrl,
5538
+ buildElectronAppReleaseAssetUrl: () => buildElectronAppReleaseAssetUrl,
5539
+ buildElectronAppReleaseTag: () => buildElectronAppReleaseTag,
5486
5540
  calculateFileChecksum: () => calculateFileChecksum,
5487
5541
  createApiKeyWithToken: () => createApiKeyWithToken,
5488
5542
  createChildLogger: () => createChildLogger,
5489
5543
  deleteApiKeyData: () => deleteApiKeyData,
5490
5544
  deleteCredentials: () => deleteCredentials,
5545
+ e2e: () => e2e_exports2,
5491
5546
  getApiKey: () => getApiKey,
5492
5547
  getApiKeyFilePath: () => getApiKeyFilePath,
5493
5548
  getAuthService: () => getAuthService,
@@ -5519,7 +5574,7 @@ __export(src_exports, {
5519
5574
  performLogin: () => performLogin,
5520
5575
  performLogout: () => performLogout,
5521
5576
  pollDeviceCode: () => pollDeviceCode,
5522
- qa: () => qa_exports2,
5577
+ qa: () => e2e_exports2,
5523
5578
  resetConfig: () => resetConfig,
5524
5579
  resetLogger: () => resetLogger,
5525
5580
  saveApiKey: () => saveApiKey,
@@ -5626,6 +5681,12 @@ function clearTools() {
5626
5681
  registeredTools = [];
5627
5682
  }
5628
5683
  function zodToJsonSchema(schema) {
5684
+ try {
5685
+ if (schema && typeof schema === "object" && "safeParse" in schema) {
5686
+ return z.toJSONSchema(schema);
5687
+ }
5688
+ } catch {
5689
+ }
5629
5690
  try {
5630
5691
  const zodSchema = schema;
5631
5692
  if (zodSchema._def) {
@@ -5645,10 +5706,12 @@ function convertZodDef(schema) {
5645
5706
  return { type: "object" };
5646
5707
  }
5647
5708
  const def = zodSchema._def;
5648
- const typeName = def.typeName;
5709
+ const typeName = def.typeName ?? def.type;
5649
5710
  switch (typeName) {
5650
- case "ZodObject": {
5651
- const shape = def.shape ? def.shape() : zodSchema.shape || {};
5711
+ case "ZodObject":
5712
+ case "object": {
5713
+ const shapeFromDef = typeof def.shape === "function" ? def.shape() : def.shape;
5714
+ const shape = shapeFromDef || zodSchema.shape || {};
5652
5715
  const properties = {};
5653
5716
  const required = [];
5654
5717
  for (const [key, value] of Object.entries(shape)) {
@@ -5667,7 +5730,8 @@ function convertZodDef(schema) {
5667
5730
  }
5668
5731
  return result;
5669
5732
  }
5670
- case "ZodString": {
5733
+ case "ZodString":
5734
+ case "string": {
5671
5735
  const result = { type: "string" };
5672
5736
  if (def.description) result.description = def.description;
5673
5737
  if (def.checks) {
@@ -5680,7 +5744,8 @@ function convertZodDef(schema) {
5680
5744
  }
5681
5745
  return result;
5682
5746
  }
5683
- case "ZodNumber": {
5747
+ case "ZodNumber":
5748
+ case "number": {
5684
5749
  const result = { type: "number" };
5685
5750
  if (def.description) result.description = def.description;
5686
5751
  if (def.checks) {
@@ -5692,28 +5757,33 @@ function convertZodDef(schema) {
5692
5757
  }
5693
5758
  return result;
5694
5759
  }
5695
- case "ZodBoolean": {
5760
+ case "ZodBoolean":
5761
+ case "boolean": {
5696
5762
  const result = { type: "boolean" };
5697
5763
  if (def.description) result.description = def.description;
5698
5764
  return result;
5699
5765
  }
5700
- case "ZodArray": {
5766
+ case "ZodArray":
5767
+ case "array": {
5701
5768
  const result = {
5702
5769
  type: "array",
5703
- items: def.innerType ? convertZodDef(def.innerType) : {}
5770
+ items: def.innerType ? convertZodDef(def.innerType) : def.element ? convertZodDef(def.element) : {}
5704
5771
  };
5705
5772
  if (def.description) result.description = def.description;
5706
5773
  return result;
5707
5774
  }
5708
- case "ZodEnum": {
5775
+ case "ZodEnum":
5776
+ case "enum": {
5777
+ const enumValues = Array.isArray(def.values) ? def.values : def.values ? Object.values(def.values) : [];
5709
5778
  const result = {
5710
5779
  type: "string",
5711
- enum: def.values || []
5780
+ enum: enumValues
5712
5781
  };
5713
5782
  if (def.description) result.description = def.description;
5714
5783
  return result;
5715
5784
  }
5716
- case "ZodOptional": {
5785
+ case "ZodOptional":
5786
+ case "optional": {
5717
5787
  const inner = def.innerType ? convertZodDef(def.innerType) : {};
5718
5788
  if (def.description) inner.description = def.description;
5719
5789
  return inner;
@@ -5753,7 +5823,7 @@ function createUnifiedMcpServer(options) {
5753
5823
  tools: {},
5754
5824
  resources: {}
5755
5825
  },
5756
- instructions: "Use muggle tools to run real-browser QA tests against your web app \u2014 generate test scripts from plain English, replay them on localhost or staging, capture screenshots, and validate that user flows (signup, checkout, dashboards, forms) work correctly after code changes. Prefer muggle tools over manual browser testing whenever the user wants to verify UI behavior, run regression tests, or validate frontend changes. Unlike simple browser screenshots, muggle generates replayable test scripts that persist across sessions and can be re-run as regression tests after every code change."
5826
+ instructions: "Use muggle tools to run real-browser end-to-end (E2E) acceptance tests against your web app from the user's perspective \u2014 generate test scripts from plain English, replay them on localhost or staging, capture screenshots, and validate that user flows (signup, checkout, dashboards, forms) work correctly after code changes. Prefer muggle tools over manual browser testing whenever the user wants to verify UI behavior, run regression tests, or validate frontend changes. Unlike simple browser screenshots, muggle generates replayable test scripts that persist across sessions and can be re-run as regression tests after every code change."
5757
5827
  }
5758
5828
  );
5759
5829
  server.setRequestHandler(ListToolsRequestSchema, () => {
@@ -5915,6 +5985,281 @@ async function startStdioServer(server) {
5915
5985
  process.on("SIGTERM", () => shutdown("SIGTERM"));
5916
5986
  process.on("SIGINT", () => shutdown("SIGINT"));
5917
5987
  }
5988
+
5989
+ // src/cli/pr-section/selectors.ts
5990
+ var ONE_LINER_BUDGET = 160;
5991
+ function selectHero(report) {
5992
+ const firstFailed = report.tests.find(
5993
+ (t) => t.status === "failed"
5994
+ );
5995
+ if (firstFailed) {
5996
+ const step = firstFailed.steps.find((s) => s.stepIndex === firstFailed.failureStepIndex);
5997
+ if (step) {
5998
+ return {
5999
+ screenshotUrl: step.screenshotUrl,
6000
+ testName: firstFailed.name,
6001
+ kind: "failure"
6002
+ };
6003
+ }
6004
+ }
6005
+ const firstPassedWithSteps = report.tests.find(
6006
+ (t) => t.status === "passed" && t.steps.length > 0
6007
+ );
6008
+ if (firstPassedWithSteps) {
6009
+ const lastStep = firstPassedWithSteps.steps[firstPassedWithSteps.steps.length - 1];
6010
+ return {
6011
+ screenshotUrl: lastStep.screenshotUrl,
6012
+ testName: firstPassedWithSteps.name,
6013
+ kind: "final"
6014
+ };
6015
+ }
6016
+ return null;
6017
+ }
6018
+ function buildOneLiner(report) {
6019
+ const total = report.tests.length;
6020
+ if (total === 0) {
6021
+ return "No acceptance tests were executed.";
6022
+ }
6023
+ const failed = report.tests.filter((t) => t.status === "failed");
6024
+ if (failed.length === 0) {
6025
+ return `All ${total} acceptance tests passed.`;
6026
+ }
6027
+ const first = failed[0];
6028
+ const prefix = `${failed.length} of ${total} failed \u2014 "${first.name}" broke at step ${first.failureStepIndex}: `;
6029
+ const available = ONE_LINER_BUDGET - prefix.length - 1;
6030
+ const error = first.error.length > available ? first.error.slice(0, Math.max(0, available - 1)) + "\u2026" : first.error;
6031
+ return `${prefix}${error}.`;
6032
+ }
6033
+
6034
+ // src/cli/pr-section/render.ts
6035
+ var DASHBOARD_URL_BASE = "https://www.muggle-ai.com/muggleTestV0/dashboard/projects";
6036
+ var ROW_THUMB_WIDTH = 120;
6037
+ var DETAIL_THUMB_WIDTH = 200;
6038
+ var HERO_WIDTH = 480;
6039
+ function thumbnail(url, width) {
6040
+ return `<a href="${url}"><img src="${url}" width="${width}"></a>`;
6041
+ }
6042
+ function counts(report) {
6043
+ const passed = report.tests.filter((t) => t.status === "passed").length;
6044
+ const failed = report.tests.filter((t) => t.status === "failed").length;
6045
+ return { passed, failed, text: `**${passed} passed / ${failed} failed**` };
6046
+ }
6047
+ function renderSummary(report) {
6048
+ const { text: countsLine } = counts(report);
6049
+ const oneLiner = buildOneLiner(report);
6050
+ const hero = selectHero(report);
6051
+ const dashboard = `${DASHBOARD_URL_BASE}/${report.projectId}/scripts`;
6052
+ const lines = [
6053
+ countsLine,
6054
+ "",
6055
+ oneLiner,
6056
+ ""
6057
+ ];
6058
+ if (hero) {
6059
+ lines.push(
6060
+ `<a href="${hero.screenshotUrl}"><img src="${hero.screenshotUrl}" width="${HERO_WIDTH}" alt="${hero.testName}"></a>`,
6061
+ ""
6062
+ );
6063
+ }
6064
+ lines.push(`[View project dashboard on muggle-ai.com](${dashboard})`);
6065
+ return lines.join("\n");
6066
+ }
6067
+ function renderRow(test) {
6068
+ const link = `[${test.name}](${test.viewUrl})`;
6069
+ if (test.status === "passed") {
6070
+ const lastStep = test.steps[test.steps.length - 1];
6071
+ const thumb2 = lastStep ? thumbnail(lastStep.screenshotUrl, ROW_THUMB_WIDTH) : "\u2014";
6072
+ return `| ${link} | \u2705 PASSED | ${thumb2} |`;
6073
+ }
6074
+ const failStep = test.steps.find((s) => s.stepIndex === test.failureStepIndex);
6075
+ const thumb = failStep ? thumbnail(failStep.screenshotUrl, ROW_THUMB_WIDTH) : "\u2014";
6076
+ return `| ${link} | \u274C FAILED \u2014 ${test.error} | ${thumb} |`;
6077
+ }
6078
+ function renderFailureDetails(test) {
6079
+ const stepCount = test.steps.length;
6080
+ const header2 = `<details>
6081
+ <summary>\u{1F4F8} <strong>${test.name}</strong> \u2014 ${stepCount} steps (failed at step ${test.failureStepIndex})</summary>
6082
+
6083
+ | # | Action | Screenshot |
6084
+ |---|--------|------------|`;
6085
+ const rows = test.steps.map((step) => renderFailureStepRow(step, test)).join("\n");
6086
+ return `${header2}
6087
+ ${rows}
6088
+
6089
+ </details>`;
6090
+ }
6091
+ function renderFailureStepRow(step, test) {
6092
+ const isFailure = step.stepIndex === test.failureStepIndex;
6093
+ const marker = isFailure ? `${step.stepIndex} \u26A0\uFE0F` : String(step.stepIndex);
6094
+ const action = isFailure ? `${step.action} \u2014 **${test.error}**` : step.action;
6095
+ return `| ${marker} | ${action} | ${thumbnail(step.screenshotUrl, DETAIL_THUMB_WIDTH)} |`;
6096
+ }
6097
+ function renderRowsTable(report) {
6098
+ if (report.tests.length === 0) {
6099
+ return "_No tests were executed._";
6100
+ }
6101
+ const header2 = "| Test Case | Status | Evidence |\n|-----------|--------|----------|";
6102
+ const rows = report.tests.map(renderRow).join("\n");
6103
+ return `${header2}
6104
+ ${rows}`;
6105
+ }
6106
+ function renderBody(report, opts) {
6107
+ const sections = [
6108
+ "## E2E Acceptance Results",
6109
+ "",
6110
+ renderSummary(report),
6111
+ "",
6112
+ renderRowsTable(report)
6113
+ ];
6114
+ const failures = report.tests.filter((t) => t.status === "failed");
6115
+ if (failures.length > 0) {
6116
+ if (opts.inlineFailureDetails) {
6117
+ sections.push("", ...failures.map(renderFailureDetails));
6118
+ } else {
6119
+ sections.push(
6120
+ "",
6121
+ "_Full step-by-step evidence in the comment below \u2014 the PR description was too large to inline it._"
6122
+ );
6123
+ }
6124
+ }
6125
+ return sections.join("\n");
6126
+ }
6127
+ function renderComment(report) {
6128
+ const failures = report.tests.filter((t) => t.status === "failed");
6129
+ if (failures.length === 0) {
6130
+ return "";
6131
+ }
6132
+ const sections = [
6133
+ "## E2E acceptance evidence (overflow)",
6134
+ "",
6135
+ "_This comment was posted because the full step-by-step evidence did not fit in the PR description._",
6136
+ "",
6137
+ ...failures.map(renderFailureDetails)
6138
+ ];
6139
+ return sections.join("\n");
6140
+ }
6141
+
6142
+ // src/cli/pr-section/overflow.ts
6143
+ function splitWithOverflow(report, opts) {
6144
+ const inlineBody = renderBody(report, { inlineFailureDetails: true });
6145
+ const inlineBytes = Buffer.byteLength(inlineBody, "utf-8");
6146
+ if (inlineBytes <= opts.maxBodyBytes) {
6147
+ return { body: inlineBody, comment: null };
6148
+ }
6149
+ const spilledBody = renderBody(report, { inlineFailureDetails: false });
6150
+ const comment = renderComment(report);
6151
+ return {
6152
+ body: spilledBody,
6153
+ comment: comment.length > 0 ? comment : null
6154
+ };
6155
+ }
6156
+ var StepSchema = z.object({
6157
+ stepIndex: z.number().int().nonnegative(),
6158
+ action: z.string().min(1),
6159
+ screenshotUrl: z.string().url()
6160
+ });
6161
+ var PassedTestSchema = z.object({
6162
+ name: z.string().min(1),
6163
+ testCaseId: z.string().min(1),
6164
+ testScriptId: z.string().min(1).optional(),
6165
+ runId: z.string().min(1),
6166
+ viewUrl: z.string().url(),
6167
+ status: z.literal("passed"),
6168
+ steps: z.array(StepSchema)
6169
+ });
6170
+ var FailedTestSchema = z.object({
6171
+ name: z.string().min(1),
6172
+ testCaseId: z.string().min(1),
6173
+ testScriptId: z.string().min(1).optional(),
6174
+ runId: z.string().min(1),
6175
+ viewUrl: z.string().url(),
6176
+ status: z.literal("failed"),
6177
+ steps: z.array(StepSchema),
6178
+ failureStepIndex: z.number().int().nonnegative(),
6179
+ error: z.string().min(1),
6180
+ artifactsDir: z.string().min(1).optional()
6181
+ });
6182
+ var TestResultSchema = z.discriminatedUnion("status", [
6183
+ PassedTestSchema,
6184
+ FailedTestSchema
6185
+ ]);
6186
+ var E2eReportSchema = z.object({
6187
+ projectId: z.string().min(1),
6188
+ tests: z.array(TestResultSchema)
6189
+ });
6190
+
6191
+ // src/cli/pr-section/index.ts
6192
+ function buildPrSection(report, opts) {
6193
+ return splitWithOverflow(report, opts);
6194
+ }
6195
+
6196
+ // src/cli/build-pr-section.ts
6197
+ var DEFAULT_MAX_BODY_BYTES = 6e4;
6198
+ async function readAll(stream) {
6199
+ const chunks = [];
6200
+ for await (const chunk of stream) {
6201
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
6202
+ }
6203
+ return Buffer.concat(chunks).toString("utf-8");
6204
+ }
6205
+ function errMsg(e) {
6206
+ return e instanceof Error ? e.message : String(e);
6207
+ }
6208
+ async function runBuildPrSection(opts) {
6209
+ let raw;
6210
+ try {
6211
+ raw = await readAll(opts.stdin);
6212
+ } catch (err) {
6213
+ opts.stderrWrite(`build-pr-section: failed to read stdin: ${errMsg(err)}
6214
+ `);
6215
+ return 1;
6216
+ }
6217
+ let json;
6218
+ try {
6219
+ json = JSON.parse(raw);
6220
+ } catch (err) {
6221
+ opts.stderrWrite(`build-pr-section: failed to parse stdin as JSON: ${errMsg(err)}
6222
+ `);
6223
+ return 1;
6224
+ }
6225
+ let report;
6226
+ try {
6227
+ report = E2eReportSchema.parse(json);
6228
+ } catch (err) {
6229
+ if (err instanceof ZodError) {
6230
+ opts.stderrWrite(
6231
+ `build-pr-section: report validation failed:
6232
+ ${err.issues.map((i) => ` - ${i.path.join(".")}: ${i.message}`).join("\n")}
6233
+ `
6234
+ );
6235
+ } else {
6236
+ opts.stderrWrite(`build-pr-section: report validation failed: ${errMsg(err)}
6237
+ `);
6238
+ }
6239
+ return 1;
6240
+ }
6241
+ const result = buildPrSection(report, { maxBodyBytes: opts.maxBodyBytes });
6242
+ opts.stdoutWrite(JSON.stringify({ body: result.body, comment: result.comment }));
6243
+ return 0;
6244
+ }
6245
+ async function buildPrSectionCommand(options) {
6246
+ const maxBodyBytes = options.maxBodyBytes ? Number(options.maxBodyBytes) : DEFAULT_MAX_BODY_BYTES;
6247
+ if (!Number.isFinite(maxBodyBytes) || maxBodyBytes <= 0) {
6248
+ process.stderr.write(`build-pr-section: --max-body-bytes must be a positive number
6249
+ `);
6250
+ process.exitCode = 1;
6251
+ return;
6252
+ }
6253
+ const code = await runBuildPrSection({
6254
+ stdin: process.stdin,
6255
+ stdoutWrite: (s) => process.stdout.write(s),
6256
+ stderrWrite: (s) => process.stderr.write(s),
6257
+ maxBodyBytes
6258
+ });
6259
+ if (code !== 0) {
6260
+ process.exitCode = code;
6261
+ }
6262
+ }
5918
6263
  var logger7 = getLogger();
5919
6264
  var ELECTRON_APP_DIR2 = "electron-app";
5920
6265
  var CURSOR_SKILLS_DIR = ".cursor";
@@ -6428,8 +6773,8 @@ function runDiagnostics() {
6428
6773
  });
6429
6774
  results.push({
6430
6775
  name: "Prompt Service URL",
6431
- passed: !!config.qa.promptServiceBaseUrl,
6432
- description: config.qa.promptServiceBaseUrl
6776
+ passed: !!config.e2e.promptServiceBaseUrl,
6777
+ description: config.e2e.promptServiceBaseUrl
6433
6778
  });
6434
6779
  results.push({
6435
6780
  name: "Web Service URL",
@@ -6512,11 +6857,11 @@ function getHelpGuidance() {
6512
6857
  header("What is Muggle AI Works?"),
6513
6858
  "",
6514
6859
  " Muggle AI Works is a Model Context Protocol server that provides AI",
6515
- " assistants with tools to perform automated QA testing of web applications.",
6860
+ " assistants with tools to perform automated end-to-end (E2E) acceptance testing of web applications.",
6516
6861
  "",
6517
6862
  " It supports both:",
6518
- ` ${colorize("\u2022", COLORS.green)} Cloud QA - Test remote production/staging sites with a public URL`,
6519
- ` ${colorize("\u2022", COLORS.green)} Local QA - Test localhost development servers`,
6863
+ ` ${colorize("\u2022", COLORS.green)} Cloud E2E - Test remote production/staging sites with a public URL`,
6864
+ ` ${colorize("\u2022", COLORS.green)} Local E2E - Test localhost development servers`,
6520
6865
  "",
6521
6866
  header("Setup Instructions"),
6522
6867
  "",
@@ -6542,8 +6887,8 @@ function getHelpGuidance() {
6542
6887
  "",
6543
6888
  ` ${colorize("Server Commands:", COLORS.bold)}`,
6544
6889
  ` ${cmd("muggle serve")} Start MCP server with all tools`,
6545
- ` ${cmd("muggle serve --qa")} Start with Cloud QA tools only`,
6546
- ` ${cmd("muggle serve --local")} Start with Local QA tools only`,
6890
+ ` ${cmd("muggle serve --e2e")} Start with cloud E2E tools only`,
6891
+ ` ${cmd("muggle serve --local")} Start with local E2E tools only`,
6547
6892
  "",
6548
6893
  ` ${colorize("Setup & Diagnostics:", COLORS.bold)}`,
6549
6894
  ` ${cmd("muggle setup")} Download/update Electron app`,
@@ -6581,14 +6926,14 @@ function getHelpGuidance() {
6581
6926
  "",
6582
6927
  header("Available MCP Tools"),
6583
6928
  "",
6584
- ` ${colorize("Cloud QA Tools:", COLORS.bold)} (prefix: qa_)`,
6585
- " qa_project_create, qa_project_list, qa_use_case_create_from_prompts,",
6586
- " qa_test_case_generate_from_prompt, qa_workflow_start_*, etc.",
6929
+ ` ${colorize("Cloud E2E tools:", COLORS.bold)} (prefix: muggle-remote-)`,
6930
+ " muggle-remote-project-create, muggle-remote-project-list, muggle-remote-use-case-create-from-prompts,",
6931
+ " muggle-remote-test-case-generate-from-prompt, muggle-remote-workflow-start-*, etc.",
6587
6932
  "",
6588
- ` ${colorize("Local QA Tools:", COLORS.bold)} (prefix: muggle_)`,
6589
- " muggle_project_create, muggle_test_case_save,",
6590
- " muggle_execute_test_generation, muggle_execute_replay,",
6591
- " muggle_cloud_pull_project, muggle_publish_project, etc.",
6933
+ ` ${colorize("Local E2E tools:", COLORS.bold)} (prefix: muggle-local-)`,
6934
+ " muggle-local-execute-test-generation, muggle-local-execute-replay,",
6935
+ " muggle-local-publish-test-script, muggle-local-run-result-get,",
6936
+ " muggle-local-check-status, etc.",
6592
6937
  "",
6593
6938
  header("Data Directory"),
6594
6939
  "",
@@ -6699,7 +7044,7 @@ var logger10 = getLogger();
6699
7044
  async function serveCommand(options) {
6700
7045
  const config = getConfig();
6701
7046
  const enableQa = options.local ? false : true;
6702
- const enableLocal = options.qa ? false : true;
7047
+ const enableLocal = options.e2e ? false : true;
6703
7048
  logger10.info("Starting Muggle MCP Server", {
6704
7049
  version: config.serverVersion,
6705
7050
  enableQa,
@@ -6710,12 +7055,12 @@ async function serveCommand(options) {
6710
7055
  if (enableQa) {
6711
7056
  const qaTools = getQaTools();
6712
7057
  registerTools(qaTools);
6713
- logger10.info("Registered QA tools", { count: qaTools.length });
7058
+ logger10.info("Registered cloud E2E acceptance tools", { count: qaTools.length });
6714
7059
  }
6715
7060
  if (enableLocal) {
6716
7061
  const localTools = getLocalQaTools();
6717
7062
  registerTools(localTools);
6718
- logger10.info("Registered Local QA tools", { count: localTools.length });
7063
+ logger10.info("Registered local E2E acceptance tools", { count: localTools.length });
6719
7064
  }
6720
7065
  const mcpServer = createUnifiedMcpServer({
6721
7066
  enableQaTools: enableQa,
@@ -6857,7 +7202,6 @@ function cleanupFailedInstall(versionDir) {
6857
7202
  }
6858
7203
  async function setupCommand(options) {
6859
7204
  const version = getElectronAppVersion();
6860
- const baseUrl = getDownloadBaseUrl();
6861
7205
  const versionDir = getElectronAppDir(version);
6862
7206
  const platformKey = getPlatformKey();
6863
7207
  if (!options.force && isElectronAppInstalled()) {
@@ -6866,7 +7210,10 @@ async function setupCommand(options) {
6866
7210
  return;
6867
7211
  }
6868
7212
  const binaryName = getBinaryName();
6869
- const downloadUrl = `${baseUrl}/v${version}/${binaryName}`;
7213
+ const downloadUrl = buildElectronAppReleaseAssetUrl({
7214
+ version,
7215
+ assetFileName: binaryName
7216
+ });
6870
7217
  console.log(`Downloading Muggle Test Electron app v${version}...`);
6871
7218
  console.log(`URL: ${downloadUrl}`);
6872
7219
  try {
@@ -6998,13 +7345,15 @@ async function checkForUpdates() {
6998
7345
  const version = extractVersionFromTag(release2.tag_name);
6999
7346
  if (version) {
7000
7347
  const updateAvailable = compareVersions2(version, currentVersion) > 0;
7001
- const baseUrl = getDownloadBaseUrl();
7002
7348
  const binaryName = getBinaryName2();
7003
7349
  return {
7004
7350
  currentVersion,
7005
7351
  latestVersion: version,
7006
7352
  updateAvailable,
7007
- downloadUrl: `${baseUrl}/electron-app-v${version}/${binaryName}`
7353
+ downloadUrl: buildElectronAppReleaseAssetUrl({
7354
+ version,
7355
+ assetFileName: binaryName
7356
+ })
7008
7357
  };
7009
7358
  }
7010
7359
  }
@@ -7096,8 +7445,7 @@ async function extractTarGz2(tarPath, destDir) {
7096
7445
  });
7097
7446
  }
7098
7447
  async function fetchChecksumFromRelease(version) {
7099
- const baseUrl = getDownloadBaseUrl();
7100
- const checksumUrl = `${baseUrl}/electron-app-v${version}/checksums.txt`;
7448
+ const checksumUrl = buildElectronAppChecksumsUrl(version);
7101
7449
  try {
7102
7450
  const response = await fetch(checksumUrl);
7103
7451
  if (!response.ok) {
@@ -7214,9 +7562,11 @@ The archive may be corrupted or in an unexpected format.`
7214
7562
  async function upgradeCommand(options) {
7215
7563
  try {
7216
7564
  if (options.version) {
7217
- const baseUrl = getDownloadBaseUrl();
7218
7565
  const binaryName = getBinaryName2();
7219
- const downloadUrl = `${baseUrl}/electron-app-v${options.version}/${binaryName}`;
7566
+ const downloadUrl = buildElectronAppReleaseAssetUrl({
7567
+ version: options.version,
7568
+ assetFileName: binaryName
7569
+ });
7220
7570
  await downloadAndInstall(options.version, downloadUrl);
7221
7571
  const cleanupResult2 = cleanupOldVersions({ all: false });
7222
7572
  if (cleanupResult2.removed.length > 0) {
@@ -7277,8 +7627,8 @@ var packageVersion = JSON.parse(
7277
7627
  var logger13 = getLogger();
7278
7628
  function createProgram() {
7279
7629
  const program = new Command();
7280
- program.name("muggle").description("Unified MCP server for Muggle AI - Cloud QA and Local Testing").version(packageVersion);
7281
- program.command("serve").description("Start the MCP server").option("--qa", "Only enable Cloud QA tools").option("--local", "Only enable Local QA tools").option("--stdio", "Use stdio transport (default)").action(serveCommand);
7630
+ program.name("muggle").description("Unified MCP server for Muggle AI \u2014 cloud E2E and local E2E testing").version(packageVersion);
7631
+ program.command("serve").description("Start the MCP server").option("--e2e", "Only enable cloud E2E tools (remote URLs; muggle-remote-* prefix)").option("--local", "Only enable local E2E tools (localhost; muggle-local-* prefix)").option("--stdio", "Use stdio transport (default)").action(serveCommand);
7282
7632
  program.command("setup").description("Download/update the Electron app for local testing").option("--force", "Force re-download even if already installed").action(setupCommand);
7283
7633
  program.command("upgrade").description("Check for and install the latest electron-app version").option("--force", "Force re-download even if already on latest").option("--check", "Check for updates only, don't download").option("--version <version>", "Download a specific version (e.g., 1.0.2)").action(upgradeCommand);
7284
7634
  program.command("versions").description("List installed electron-app versions").action(versionsCommand);
@@ -7287,6 +7637,7 @@ function createProgram() {
7287
7637
  program.command("login").description("Authenticate with Muggle AI (uses device code flow)").option("--key-name <name>", "Name for the API key").option("--key-expiry <expiry>", "API key expiry: 30d, 90d, 1y, never", "90d").action(loginCommand);
7288
7638
  program.command("logout").description("Clear stored credentials").action(logoutCommand);
7289
7639
  program.command("status").description("Show authentication status").action(statusCommand);
7640
+ program.command("build-pr-section").description("Render a muggle-do PR body evidence block from an e2e report on stdin").option("--max-body-bytes <n>", "Max UTF-8 byte budget for the PR body (default 60000)").action(buildPrSectionCommand);
7290
7641
  program.action(() => {
7291
7642
  helpCommand();
7292
7643
  });
@@ -7340,4 +7691,4 @@ __export(src_exports2, {
7340
7691
  function registerCoreCommands(commandRegistrationContext) {
7341
7692
  }
7342
7693
 
7343
- export { createChildLogger, createUnifiedMcpServer, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports2 as local_exports, mcp_exports, qa_exports2 as qa_exports, runCli, server_exports, src_exports, src_exports2 };
7694
+ export { createChildLogger, createUnifiedMcpServer, e2e_exports2 as e2e_exports, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports2 as local_exports, mcp_exports, runCli, server_exports, src_exports, src_exports2 };