@muggleai/works 4.8.4 → 4.9.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.
Files changed (36) hide show
  1. package/dist/{chunk-44I5ROCB.js → chunk-4KQZLF56.js} +219 -7
  2. package/dist/{chunk-OMLNCNSZ.js → chunk-6N7OIBAS.js} +7 -2
  3. package/dist/cli.js +2 -2
  4. package/dist/index.js +2 -2
  5. package/dist/plugin/.claude-plugin/plugin.json +8 -1
  6. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  7. package/dist/plugin/agents/acceptance-tester.md +103 -0
  8. package/dist/plugin/scripts/ensure-electron-app.sh +37 -1
  9. package/dist/plugin/skills/muggle/SKILL.md +24 -8
  10. package/dist/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +17 -0
  11. package/dist/plugin/skills/muggle-preferences/SKILL.md +82 -0
  12. package/dist/plugin/skills/muggle-status/SKILL.md +17 -0
  13. package/dist/plugin/skills/muggle-test/SKILL.md +26 -0
  14. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +50 -19
  15. package/dist/plugin/skills/muggle-test-import/SKILL.md +20 -0
  16. package/dist/plugin/skills/muggle-test-prepare/SKILL.md +276 -0
  17. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +18 -0
  18. package/dist/plugin/skills/muggle-upgrade/SKILL.md +9 -1
  19. package/dist/release-manifest.json +4 -4
  20. package/dist/src-7GB7WIFT.js +1 -0
  21. package/package.json +1 -1
  22. package/plugin/.claude-plugin/plugin.json +8 -1
  23. package/plugin/.cursor-plugin/plugin.json +1 -1
  24. package/plugin/agents/acceptance-tester.md +103 -0
  25. package/plugin/scripts/ensure-electron-app.sh +37 -1
  26. package/plugin/skills/muggle/SKILL.md +24 -8
  27. package/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +17 -0
  28. package/plugin/skills/muggle-preferences/SKILL.md +82 -0
  29. package/plugin/skills/muggle-status/SKILL.md +17 -0
  30. package/plugin/skills/muggle-test/SKILL.md +26 -0
  31. package/plugin/skills/muggle-test-feature-local/SKILL.md +50 -19
  32. package/plugin/skills/muggle-test-import/SKILL.md +20 -0
  33. package/plugin/skills/muggle-test-prepare/SKILL.md +276 -0
  34. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +18 -0
  35. package/plugin/skills/muggle-upgrade/SKILL.md +9 -1
  36. package/dist/src-ZRUONWKV.js +0 -1
@@ -28,6 +28,13 @@ var __export = (target, all) => {
28
28
  // packages/mcps/src/index.ts
29
29
  var src_exports = {};
30
30
  __export(src_exports, {
31
+ DEFAULT_PREFERENCES: () => DEFAULT_PREFERENCES,
32
+ PREFERENCES_FILE_NAME: () => PREFERENCES_FILE_NAME,
33
+ PREFERENCES_PROJECT_DIR_NAME: () => PREFERENCES_PROJECT_DIR_NAME,
34
+ PREFERENCES_SCHEMA: () => PREFERENCES_SCHEMA,
35
+ PREFERENCES_VERSION: () => PREFERENCES_VERSION,
36
+ PreferenceKey: () => PreferenceKey,
37
+ PreferenceValue: () => PreferenceValue,
31
38
  buildElectronAppChecksumsUrl: () => buildElectronAppChecksumsUrl,
32
39
  buildElectronAppReleaseAssetUrl: () => buildElectronAppReleaseAssetUrl,
33
40
  buildElectronAppReleaseTag: () => buildElectronAppReleaseTag,
@@ -37,6 +44,7 @@ __export(src_exports, {
37
44
  deleteApiKeyData: () => deleteApiKeyData,
38
45
  deleteCredentials: () => deleteCredentials,
39
46
  e2e: () => e2e_exports2,
47
+ formatPreferencesOneLiner: () => formatPreferencesOneLiner,
40
48
  getApiKey: () => getApiKey,
41
49
  getApiKeyFilePath: () => getApiKeyFilePath,
42
50
  getAuthService: () => getAuthService,
@@ -60,6 +68,7 @@ __export(src_exports, {
60
68
  getValidCredentials: () => getValidCredentials,
61
69
  hasApiKey: () => hasApiKey,
62
70
  isElectronAppInstalled: () => isElectronAppInstalled,
71
+ isFirstRun: () => isFirstRun,
63
72
  loadApiKeyData: () => loadApiKeyData,
64
73
  loadCredentials: () => loadCredentials,
65
74
  localQa: () => local_exports2,
@@ -69,14 +78,20 @@ __export(src_exports, {
69
78
  performLogout: () => performLogout,
70
79
  pollDeviceCode: () => pollDeviceCode,
71
80
  qa: () => e2e_exports2,
81
+ readGlobalPreferences: () => readGlobalPreferences,
82
+ readProjectPreferences: () => readProjectPreferences,
72
83
  resetConfig: () => resetConfig,
73
84
  resetLogger: () => resetLogger,
85
+ resetPreference: () => resetPreference,
86
+ resolvePreferences: () => resolvePreferences,
74
87
  saveApiKey: () => saveApiKey,
75
88
  saveApiKeyData: () => saveApiKeyData,
76
89
  saveCredentials: () => saveCredentials,
77
90
  startDeviceCodeFlow: () => startDeviceCodeFlow,
78
91
  toolRequiresAuth: () => toolRequiresAuth,
79
- verifyFileChecksum: () => verifyFileChecksum
92
+ validatePreference: () => validatePreference,
93
+ verifyFileChecksum: () => verifyFileChecksum,
94
+ writePreferences: () => writePreferences
80
95
  });
81
96
  var DATA_DIR_NAME = ".muggle-ai";
82
97
  function getDataDir() {
@@ -3466,17 +3481,17 @@ var PromptServiceClient = class {
3466
3481
  * @param path - Path to validate.
3467
3482
  * @throws GatewayError if path is not allowed.
3468
3483
  */
3469
- validatePath(path10) {
3470
- const isAllowed = ALLOWED_UPSTREAM_PREFIXES.some((prefix) => path10.startsWith(prefix));
3484
+ validatePath(path11) {
3485
+ const isAllowed = ALLOWED_UPSTREAM_PREFIXES.some((prefix) => path11.startsWith(prefix));
3471
3486
  if (!isAllowed) {
3472
3487
  const logger6 = getLogger();
3473
3488
  logger6.error("Path not in allowlist", {
3474
- path: path10,
3489
+ path: path11,
3475
3490
  allowedPrefixes: ALLOWED_UPSTREAM_PREFIXES
3476
3491
  });
3477
3492
  throw new GatewayError({
3478
3493
  code: "FORBIDDEN" /* FORBIDDEN */,
3479
- message: `Path '${path10}' is not allowed`
3494
+ message: `Path '${path11}' is not allowed`
3480
3495
  });
3481
3496
  }
3482
3497
  }
@@ -5268,6 +5283,173 @@ var CleanupSessionsInputSchema = z.object({
5268
5283
  });
5269
5284
  var EmptyInputSchema2 = z.object({});
5270
5285
 
5286
+ // packages/mcps/src/shared/preferences-types.ts
5287
+ var PreferenceKey = /* @__PURE__ */ ((PreferenceKey2) => {
5288
+ PreferenceKey2["AutoLogin"] = "autoLogin";
5289
+ PreferenceKey2["AutoSelectProject"] = "autoSelectProject";
5290
+ PreferenceKey2["ShowElectronBrowser"] = "showElectronBrowser";
5291
+ PreferenceKey2["OpenTestResultsAfterRun"] = "openTestResultsAfterRun";
5292
+ PreferenceKey2["DefaultExecutionMode"] = "defaultExecutionMode";
5293
+ PreferenceKey2["AutoPublishLocalResults"] = "autoPublishLocalResults";
5294
+ PreferenceKey2["SuggestRelatedUseCases"] = "suggestRelatedUseCases";
5295
+ PreferenceKey2["SuggestRelatedTestCases"] = "suggestRelatedTestCases";
5296
+ PreferenceKey2["AutoDetectChanges"] = "autoDetectChanges";
5297
+ PreferenceKey2["PostPRVisualWalkthrough"] = "postPRVisualWalkthrough";
5298
+ PreferenceKey2["CheckForUpdates"] = "checkForUpdates";
5299
+ PreferenceKey2["VerboseOutput"] = "verboseOutput";
5300
+ return PreferenceKey2;
5301
+ })(PreferenceKey || {});
5302
+ var PreferenceValue = /* @__PURE__ */ ((PreferenceValue2) => {
5303
+ PreferenceValue2["Always"] = "always";
5304
+ PreferenceValue2["Ask"] = "ask";
5305
+ PreferenceValue2["Never"] = "never";
5306
+ return PreferenceValue2;
5307
+ })(PreferenceValue || {});
5308
+
5309
+ // packages/mcps/src/mcp/local/contracts/preferences-schemas.ts
5310
+ var preferenceKeyValues = Object.values(PreferenceKey);
5311
+ var preferenceValueValues = Object.values(PreferenceValue);
5312
+ var PreferencesSetInputSchema = z.object({
5313
+ key: z.enum(preferenceKeyValues).describe("The preference key to set."),
5314
+ value: z.enum(preferenceValueValues).describe("The value: always, ask, or never."),
5315
+ scope: z.enum(["global", "project"]).default("global").describe("Write to global (~/.muggle-ai/) or project (.muggle-ai/) preferences."),
5316
+ cwd: z.string().optional().describe("Project root directory. Required when scope is 'project'.")
5317
+ });
5318
+
5319
+ // packages/mcps/src/shared/preferences-constants.ts
5320
+ var PREFERENCES_FILE_NAME = "preferences.json";
5321
+ var PREFERENCES_PROJECT_DIR_NAME = ".muggle-ai";
5322
+ var PREFERENCES_VERSION = 1;
5323
+ var DEFAULT_PREFERENCES = {
5324
+ ["autoLogin" /* AutoLogin */]: "ask" /* Ask */,
5325
+ ["autoSelectProject" /* AutoSelectProject */]: "ask" /* Ask */,
5326
+ ["showElectronBrowser" /* ShowElectronBrowser */]: "ask" /* Ask */,
5327
+ ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: "ask" /* Ask */,
5328
+ ["defaultExecutionMode" /* DefaultExecutionMode */]: "ask" /* Ask */,
5329
+ ["autoPublishLocalResults" /* AutoPublishLocalResults */]: "ask" /* Ask */,
5330
+ ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: "ask" /* Ask */,
5331
+ ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: "ask" /* Ask */,
5332
+ ["autoDetectChanges" /* AutoDetectChanges */]: "ask" /* Ask */,
5333
+ ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: "ask" /* Ask */,
5334
+ ["checkForUpdates" /* CheckForUpdates */]: "ask" /* Ask */,
5335
+ ["verboseOutput" /* VerboseOutput */]: "ask" /* Ask */
5336
+ };
5337
+ var PREFERENCES_SCHEMA = {
5338
+ ["autoLogin" /* AutoLogin */]: {
5339
+ description: "When a tool requires auth and saved credentials exist, reuse them without prompting"
5340
+ },
5341
+ ["autoSelectProject" /* AutoSelectProject */]: {
5342
+ description: "When a skill needs a Muggle project and one was used previously in this repo, reuse it without prompting"
5343
+ },
5344
+ ["showElectronBrowser" /* ShowElectronBrowser */]: {
5345
+ description: "When running local E2E tests, show the Electron browser window"
5346
+ },
5347
+ ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: {
5348
+ description: "After a local E2E test run completes, open the per-run results page on Muggle dashboard"
5349
+ },
5350
+ ["defaultExecutionMode" /* DefaultExecutionMode */]: {
5351
+ description: "When a skill supports both local and remote test execution, which to default to"
5352
+ },
5353
+ ["autoPublishLocalResults" /* AutoPublishLocalResults */]: {
5354
+ description: "After a local E2E test run completes, upload results to Muggle cloud for team visibility"
5355
+ },
5356
+ ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: {
5357
+ description: "After creating or running a use case, suggest related use cases to add"
5358
+ },
5359
+ ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: {
5360
+ description: "After creating or running a test case, suggest related test cases to add"
5361
+ },
5362
+ ["autoDetectChanges" /* AutoDetectChanges */]: {
5363
+ description: "When running muggle-test, scan local git changes and map to affected test cases"
5364
+ },
5365
+ ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: {
5366
+ description: "After test results are available, post visual walkthrough comment with screenshots to the PR"
5367
+ },
5368
+ ["checkForUpdates" /* CheckForUpdates */]: {
5369
+ description: "At session start, check if a newer Muggle version is available and notify"
5370
+ },
5371
+ ["verboseOutput" /* VerboseOutput */]: {
5372
+ description: "Show detailed progress logs during skill and tool execution"
5373
+ }
5374
+ };
5375
+
5376
+ // packages/mcps/src/shared/preferences.ts
5377
+ function isFirstRun(dataDirOverride) {
5378
+ const filePath = path2.join(dataDirOverride ?? getDataDir(), PREFERENCES_FILE_NAME);
5379
+ return !fs5.existsSync(filePath);
5380
+ }
5381
+ function readPreferencesFile(filePath) {
5382
+ try {
5383
+ if (!fs5.existsSync(filePath)) {
5384
+ return {};
5385
+ }
5386
+ const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
5387
+ return raw.preferences ?? {};
5388
+ } catch (error) {
5389
+ getLogger().warn("Failed to read preferences file", {
5390
+ path: filePath,
5391
+ error: error instanceof Error ? error.message : String(error)
5392
+ });
5393
+ return {};
5394
+ }
5395
+ }
5396
+ function readGlobalPreferences(dataDirOverride) {
5397
+ const filePath = path2.join(dataDirOverride ?? getDataDir(), PREFERENCES_FILE_NAME);
5398
+ const partial = readPreferencesFile(filePath);
5399
+ return { ...DEFAULT_PREFERENCES, ...partial };
5400
+ }
5401
+ function readProjectPreferences(cwd) {
5402
+ const filePath = path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_FILE_NAME);
5403
+ return readPreferencesFile(filePath);
5404
+ }
5405
+ function resolvePreferences(dataDirOverride, cwd) {
5406
+ const global = readGlobalPreferences(dataDirOverride);
5407
+ if (!cwd) {
5408
+ return global;
5409
+ }
5410
+ const project = readProjectPreferences(cwd);
5411
+ return { ...global, ...project };
5412
+ }
5413
+ function writePreferences(prefs, scope, dataDirOverride, cwd) {
5414
+ const dir = scope === "project" && cwd ? path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride ?? getDataDir();
5415
+ fs5.mkdirSync(dir, { recursive: true });
5416
+ const filePath = path2.join(dir, PREFERENCES_FILE_NAME);
5417
+ const existing = readPreferencesFile(filePath);
5418
+ const merged = { ...existing, ...prefs };
5419
+ const file = {
5420
+ version: PREFERENCES_VERSION,
5421
+ preferences: merged
5422
+ };
5423
+ fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
5424
+ `, "utf-8");
5425
+ }
5426
+ function resetPreference(key, scope, dataDirOverride, cwd) {
5427
+ const dir = scope === "project" && cwd ? path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride ?? getDataDir();
5428
+ const filePath = path2.join(dir, PREFERENCES_FILE_NAME);
5429
+ if (!fs5.existsSync(filePath)) {
5430
+ return;
5431
+ }
5432
+ if (!key) {
5433
+ const file2 = { version: PREFERENCES_VERSION, preferences: {} };
5434
+ fs5.writeFileSync(filePath, `${JSON.stringify(file2, null, 2)}
5435
+ `, "utf-8");
5436
+ return;
5437
+ }
5438
+ const existing = readPreferencesFile(filePath);
5439
+ delete existing[key];
5440
+ const file = { version: PREFERENCES_VERSION, preferences: existing };
5441
+ fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
5442
+ `, "utf-8");
5443
+ }
5444
+ function validatePreference(key, value) {
5445
+ const validKeys = Object.values(PreferenceKey);
5446
+ const validValues = Object.values(PreferenceValue);
5447
+ return validKeys.includes(key) && validValues.includes(value);
5448
+ }
5449
+ function formatPreferencesOneLiner(prefs) {
5450
+ return Object.entries(prefs).map(([key, value]) => `${key}=${value}`).join(" ");
5451
+ }
5452
+
5271
5453
  // packages/mcps/src/mcp/tools/local/tool-registry.ts
5272
5454
  function createChildLogger2(correlationId) {
5273
5455
  const logger6 = getLogger();
@@ -5709,6 +5891,33 @@ var publishTestScriptTool = {
5709
5891
  }
5710
5892
  }
5711
5893
  };
5894
+ var preferencesSetTool = {
5895
+ name: "muggle-local-preferences-set",
5896
+ description: "Set a Muggle AI user preference. Preferences control automation behavior (auto-login, show browser, suggest test cases, etc.). Values: 'always' (proceed without asking), 'ask' (prompt each time), 'never' (skip without asking). Scope: 'global' writes to ~/.muggle-ai/preferences.json, 'project' writes to .muggle-ai/preferences.json in the repo root.",
5897
+ inputSchema: PreferencesSetInputSchema,
5898
+ execute: async (ctx) => {
5899
+ const logger6 = createChildLogger2(ctx.correlationId);
5900
+ logger6.info("Executing muggle-local-preferences-set");
5901
+ const input = PreferencesSetInputSchema.parse(ctx.input);
5902
+ writePreferences(
5903
+ { [input.key]: input.value },
5904
+ input.scope,
5905
+ void 0,
5906
+ input.cwd
5907
+ );
5908
+ const resolved = resolvePreferences(void 0, input.cwd);
5909
+ const oneLiner = formatPreferencesOneLiner(resolved);
5910
+ const content = [
5911
+ `**${input.key}** set to **${input.value}** (${input.scope}).`,
5912
+ "",
5913
+ `Preferences file: ~/.muggle-ai/${PREFERENCES_FILE_NAME}`,
5914
+ "",
5915
+ "Current resolved preferences:",
5916
+ `\`${oneLiner}\``
5917
+ ].join("\n");
5918
+ return { content, isError: false };
5919
+ }
5920
+ };
5712
5921
  var allLocalQaTools = [
5713
5922
  // Status tools
5714
5923
  checkStatusTool,
@@ -5724,7 +5933,9 @@ var allLocalQaTools = [
5724
5933
  executeReplayTool,
5725
5934
  cancelExecutionTool,
5726
5935
  // Publishing tools
5727
- publishTestScriptTool
5936
+ publishTestScriptTool,
5937
+ // Preferences tools
5938
+ preferencesSetTool
5728
5939
  ];
5729
5940
  var toolMap = new Map(
5730
5941
  allLocalQaTools.map((tool) => [tool.name, tool])
@@ -5871,6 +6082,7 @@ __export(local_exports2, {
5871
6082
  LocalWorkflowFileEntityType: () => LocalWorkflowFileEntityType,
5872
6083
  LocalWorkflowRunStatus: () => LocalWorkflowRunStatus,
5873
6084
  MuggleEntityIdSchema: () => MuggleEntityIdSchema,
6085
+ PreferencesSetInputSchema: () => PreferencesSetInputSchema,
5874
6086
  PublishTestScriptInputSchema: () => PublishTestScriptInputSchema,
5875
6087
  RunResultGetInputSchema: () => RunResultGetInputSchema,
5876
6088
  RunResultListInputSchema: () => RunResultListInputSchema,
@@ -5939,4 +6151,4 @@ var plugins_exports = {};
5939
6151
  // packages/mcps/src/mcp/agents/index.ts
5940
6152
  var agents_exports = {};
5941
6153
 
5942
- export { __export, __require, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports2 as e2e_exports, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir2 as getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, loadApiKeyData, loadCredentials, local_exports2 as local_exports, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, resetConfig, resetLogger, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, verifyFileChecksum };
6154
+ export { DEFAULT_PREFERENCES, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PreferenceKey, PreferenceValue, __export, __require, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports2 as e2e_exports, formatPreferencesOneLiner, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir2 as getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports2 as local_exports, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, readGlobalPreferences, readProjectPreferences, resetConfig, resetLogger, resetPreference, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, validatePreference, verifyFileChecksum, writePreferences };
@@ -1,4 +1,4 @@
1
- import { __export, getLogger, getConfig, createChildLogger, buildElectronAppReleaseAssetUrl, getAuthService, hasApiKey, getElectronAppVersion, getElectronAppDir, getPlatformKey, isElectronAppInstalled, getElectronAppChecksums, getChecksumForPlatform, verifyFileChecksum, calculateFileChecksum, getQaTools, getLocalQaTools, performLogout, performLogin, toolRequiresAuth, getCallerCredentials, getDataDir, getBundledElectronAppVersion, getElectronAppVersionSource, getCredentialsFilePath, buildElectronAppChecksumsUrl, __require } from './chunk-44I5ROCB.js';
1
+ import { __export, getLogger, getConfig, createChildLogger, buildElectronAppReleaseAssetUrl, getAuthService, hasApiKey, getElectronAppVersion, getElectronAppDir, getPlatformKey, isFirstRun, writePreferences, DEFAULT_PREFERENCES, getDataDir, PREFERENCES_FILE_NAME, isElectronAppInstalled, getElectronAppChecksums, getChecksumForPlatform, verifyFileChecksum, calculateFileChecksum, getQaTools, getLocalQaTools, performLogout, performLogin, toolRequiresAuth, getCallerCredentials, getBundledElectronAppVersion, getElectronAppVersionSource, getCredentialsFilePath, buildElectronAppChecksumsUrl, __require } from './chunk-4KQZLF56.js';
2
2
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
3
3
  import { ListToolsRequestSchema, CallToolRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema } from '@modelcontextprotocol/sdk/types.js';
4
4
  import { v4 } from 'uuid';
@@ -686,7 +686,7 @@ async function resolveGsScreenshotUrls(report, opts) {
686
686
  if (gsUrls.length === 0) {
687
687
  return report;
688
688
  }
689
- const mcps = await import('./src-ZRUONWKV.js');
689
+ const mcps = await import('./src-7GB7WIFT.js');
690
690
  const credentials = await mcps.getCallerCredentialsAsync();
691
691
  if (!credentials.bearerToken && !credentials.apiKey) {
692
692
  stderrWrite(
@@ -1777,6 +1777,11 @@ async function setupCommand(options) {
1777
1777
  const versionDir = getElectronAppDir(version);
1778
1778
  const platformKey = getPlatformKey();
1779
1779
  upsertCursorMcpConfig();
1780
+ if (isFirstRun()) {
1781
+ writePreferences(DEFAULT_PREFERENCES, "global");
1782
+ console.log(`Default preferences written to ${path4.join(getDataDir(), PREFERENCES_FILE_NAME)}`);
1783
+ console.log("All preferences default to 'ask'. You can customize them anytime by editing the file or telling the agent to change a preference.");
1784
+ }
1780
1785
  if (!options.force && isElectronAppInstalled()) {
1781
1786
  console.log(`Electron app v${version} is already installed at ${versionDir}`);
1782
1787
  console.log("Use --force to re-download.");
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './chunk-OMLNCNSZ.js';
3
- import './chunk-44I5ROCB.js';
2
+ import { runCli } from './chunk-6N7OIBAS.js';
3
+ import './chunk-4KQZLF56.js';
4
4
 
5
5
  // src/cli/main.ts
6
6
  runCli().catch((error) => {
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-OMLNCNSZ.js';
2
- export { createChildLogger, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, src_exports as shared } from './chunk-44I5ROCB.js';
1
+ export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-6N7OIBAS.js';
2
+ export { createChildLogger, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, src_exports as shared } from './chunk-4KQZLF56.js';
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "muggle",
3
3
  "description": "Run real-browser end-to-end (E2E) acceptance tests on your web app from any AI coding agent. Generate test scripts from plain English, replay them on localhost, capture screenshots, and validate user flows like signup, checkout, and dashboards. Works across Claude Code, Cursor, Codex, and Windsurf.",
4
- "version": "4.8.4",
4
+ "version": "4.9.1",
5
5
  "author": {
6
6
  "name": "Muggle AI",
7
7
  "email": "support@muggle-ai.com"
@@ -9,6 +9,13 @@
9
9
  "homepage": "https://www.muggletest.com",
10
10
  "repository": "https://github.com/multiplex-ai/muggle-ai-works",
11
11
  "license": "MIT",
12
+ "agents": [
13
+ {
14
+ "name": "acceptance-tester",
15
+ "path": "agents/acceptance-tester.md",
16
+ "description": "E2E acceptance testing agent — runs real-browser tests, reports structured results with blocking issues and suggested fixes, imports test artifacts, manages preferences, and operates the Muggle AI suite."
17
+ }
18
+ ],
12
19
  "keywords": [
13
20
  "acceptance-testing",
14
21
  "testing",
@@ -2,7 +2,7 @@
2
2
  "name": "muggle",
3
3
  "displayName": "Muggle AI",
4
4
  "description": "Ship quality products with AI-powered end-to-end (E2E) acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
5
- "version": "4.8.4",
5
+ "version": "4.9.1",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: acceptance-tester
3
+ description: >-
4
+ E2E acceptance testing agent — runs real-browser tests against web apps and
5
+ reports structured results with blocking issues and suggested fixes. Also
6
+ imports existing test artifacts, manages Muggle preferences, and operates
7
+ the Muggle AI suite (status checks, repairs). Dispatch this agent when the
8
+ team needs acceptance test feedback, test coverage for a feature, or Muggle
9
+ suite operations.
10
+ model: sonnet
11
+ tools: ["Read", "Write", "Edit", "Bash", "Grep", "Glob"]
12
+ ---
13
+
14
+ # Acceptance Tester
15
+
16
+ You are the team's acceptance testing specialist. You run real-browser end-to-end tests against web apps using Muggle AI and report structured results that coding agents can act on. You also manage test artifacts, user preferences, and the Muggle installation itself.
17
+
18
+ You operate through skills — never call raw MCP tools directly.
19
+
20
+ ## Skills
21
+
22
+ | Skill | When to use |
23
+ |-------|-------------|
24
+ | `muggle-test` | Run acceptance tests. Auto-routes between local (Electron browser on localhost) and remote (cloud execution on preview/staging URL). Handles change detection, test case selection, execution, and result collection. |
25
+ | `muggle-test-import` | Import existing test artifacts into Muggle Test — Playwright specs, Cypress tests, PRDs, Gherkin feature files, test plan documents. |
26
+ | `muggle-preferences` | View, set, or reset the 12 preference knobs that control Muggle behavior. |
27
+ | `muggle-repair` | Diagnose and fix broken Muggle installation components. |
28
+ | `muggle-status` | Check health of the Muggle installation — Electron app, MCP server, auth, CLI version. |
29
+
30
+ Select the skill based on what the orchestrator asks you to do. If the task doesn't clearly map to one skill, ask for clarification.
31
+
32
+ ## Input Contract
33
+
34
+ The orchestrator provides a dispatch prompt with:
35
+
36
+ - **What to do:** Run tests, import tests, manage preferences, check status, or repair.
37
+ - **For testing:** Target URL and what to test — a feature description, acceptance criteria, or "run regression on recent changes."
38
+ - **For import:** Path to test files or documents to import.
39
+ - **For preferences:** Which preference to view or change, or "show all."
40
+ - **For status/repair:** No additional input needed.
41
+
42
+ ## Output Contract
43
+
44
+ ### When Running Tests
45
+
46
+ Always return two sections:
47
+
48
+ **Section 1 — Test Summary**
49
+
50
+ ```
51
+ ## Test Summary
52
+ - **Tests:** {total} total — {passed} passed, {failed} failed
53
+ - **Verdict:** PASS | FAIL
54
+ - **Dashboard:** {link to Muggle dashboard, if results were published}
55
+ ```
56
+
57
+ **Section 2 — Per-Test Highlights**
58
+
59
+ Order failures first, then passes.
60
+
61
+ For each **failed** test:
62
+
63
+ ```
64
+ ### {Test Name} — FAIL
65
+ - **Blocking issue:** {What went wrong from the user's perspective. Describe the observable symptom — what the user sees or doesn't see.}
66
+ - **Suggested fix:** {What the coding agent should investigate. Reference UI flows and components, not specific file paths — the acceptance tester operates at the UI layer.}
67
+ ```
68
+
69
+ For each **passed** test, list the name only:
70
+
71
+ ```
72
+ ### {Test Name} — PASS
73
+ ```
74
+
75
+ ### When Importing Tests
76
+
77
+ Report:
78
+ - What was imported — count of use cases and test cases, source format, target project
79
+ - Any skipped or failed imports with reasons
80
+ - Suggested next step (e.g., "run the imported tests to verify")
81
+
82
+ ### When Managing Preferences
83
+
84
+ Report:
85
+ - Current preference values (if listing)
86
+ - Confirmation of the change (if setting or resetting)
87
+
88
+ ### When Checking Status or Repairing
89
+
90
+ Report:
91
+ - Component-by-component health status
92
+ - Actions taken (for repair)
93
+ - Recommendations
94
+
95
+ ## Behavior Rules
96
+
97
+ 1. **Always run tests before reporting.** Never speculate on whether something passes or fails.
98
+ 2. **Report ALL failures.** Do not stop at the first failure or summarize multiple failures into one.
99
+ 3. **Describe problems from the user's perspective.** "The checkout button is not clickable after adding an item to the cart" — not "onClick handler missing on Button component in CartPage.tsx."
100
+ 4. **Do not modify application code.** Report findings; coding agents act on them.
101
+ 5. **Do not decide when to run.** The orchestrator dispatches; you execute and report.
102
+ 6. **Do not choose local vs remote.** The `muggle-test` skill handles routing based on the target URL and user preferences.
103
+ 7. **Use skills, not raw MCP tools.** The orchestrator pastes the full skill text into your dispatch prompt. Follow the skill's instructions exactly.
@@ -64,7 +64,43 @@ version_check() {
64
64
  }
65
65
  version_check || true
66
66
 
67
- context="<EXTREMELY_IMPORTANT>\nYou have access to Muggle AI — a real-browser E2E acceptance testing tool.\n\nWhenever the user asks you to test, validate, verify, or check if their web app works — use the muggle MCP tools. This includes:\n- Testing user flows (signup, login, checkout, forms, dashboards)\n- Verifying UI changes didn't break anything\n- Running regression tests after code changes\n- Validating frontend behavior on localhost or a dev server\n- Checking if a feature works before merging a PR\n\nMuggle launches a real Electron browser that clicks buttons, fills forms, navigates pages, and captures screenshots. It generates replayable test scripts that persist across sessions.\n\nDo NOT write test code (Playwright, Cypress, Selenium) or try to test UI manually when muggle tools are available. Use the muggle skill or muggle MCP tools instead — they are faster, capture visual evidence, and produce reusable test scripts.\n\nTrigger phrases: 'test my app', 'check if it works', 'run E2E acceptance tests', 'validate the UI', 'verify the flow', 'regression test', 'make sure it still works', 'test before merging'.\n</EXTREMELY_IMPORTANT>${upgrade_notice}"
67
+ # --- Preferences injection ---
68
+ prefs_global_file="${HOME}/.muggle-ai/preferences.json"
69
+ prefs_line=""
70
+ prefs_file_note=""
71
+
72
+ if [ -f "$prefs_global_file" ]; then
73
+ # Extract preferences object keys and values into a compact one-liner.
74
+ # Uses node for reliable JSON parsing (already required for muggle).
75
+ prefs_line=$(node -e "
76
+ const fs = require('fs');
77
+ try {
78
+ const g = JSON.parse(fs.readFileSync('${prefs_global_file}', 'utf-8')).preferences || {};
79
+ const defaults = {
80
+ autoLogin:'ask', autoSelectProject:'ask', showElectronBrowser:'ask',
81
+ openTestResultsAfterRun:'ask', defaultExecutionMode:'ask', autoPublishLocalResults:'ask',
82
+ suggestRelatedUseCases:'ask', suggestRelatedTestCases:'ask', autoDetectChanges:'ask',
83
+ postPRVisualWalkthrough:'ask', checkForUpdates:'ask', verboseOutput:'ask'
84
+ };
85
+ const cwd = process.env.CLAUDE_CWD || process.env.CURSOR_CWD || process.cwd();
86
+ const pPath = require('path').join(cwd, '.muggle-ai', 'preferences.json');
87
+ let p = {};
88
+ try { p = JSON.parse(fs.readFileSync(pPath, 'utf-8')).preferences || {}; } catch {}
89
+ const merged = { ...defaults, ...g, ...p };
90
+ const hasProject = Object.keys(p).length > 0;
91
+ const note = hasProject ? ', project overrides active' : '';
92
+ const line = Object.entries(merged).map(([k,v]) => k+'='+v).join(' ');
93
+ console.log('Muggle Preferences (~/.muggle-ai/preferences.json' + note + '):\\\\n' + line);
94
+ } catch { console.log(''); }
95
+ " 2>/dev/null || true)
96
+ if [ -n "$prefs_line" ]; then
97
+ prefs_file_note="\\n\\n${prefs_line}"
98
+ fi
99
+ else
100
+ prefs_file_note="\\n\\nMuggle Preferences: not configured. Run \\\`muggle setup\\\` or tell the agent to set preferences."
101
+ fi
102
+
103
+ context="<EXTREMELY_IMPORTANT>\nYou have access to Muggle AI — a real-browser E2E acceptance testing tool.\n\nWhenever the user asks you to test, validate, verify, or check if their web app works — use the muggle MCP tools. This includes:\n- Testing user flows (signup, login, checkout, forms, dashboards)\n- Verifying UI changes didn't break anything\n- Running regression tests after code changes\n- Validating frontend behavior on localhost or a dev server\n- Checking if a feature works before merging a PR\n\nMuggle launches a real Electron browser that clicks buttons, fills forms, navigates pages, and captures screenshots. It generates replayable test scripts that persist across sessions.\n\nDo NOT write test code (Playwright, Cypress, Selenium) or try to test UI manually when muggle tools are available. Use the muggle skill or muggle MCP tools instead — they are faster, capture visual evidence, and produce reusable test scripts.\n\nTrigger phrases: 'test my app', 'check if it works', 'run E2E acceptance tests', 'validate the UI', 'verify the flow', 'regression test', 'make sure it still works', 'test before merging'.\n</EXTREMELY_IMPORTANT>${upgrade_notice}${prefs_file_note}"
68
104
 
69
105
  escaped_context=$(escape_for_json "$context")
70
106
 
@@ -7,15 +7,30 @@ description: Muggle AI command router and menu. Use when user types "muggle" or
7
7
 
8
8
  Use this as the top-level Muggle command router.
9
9
 
10
+ ## Preferences
11
+
12
+ User preferences are available in the session context (injected at session start). Look for the line starting with `Muggle Preferences` — it contains key=value pairs like `autoLogin=ask showElectronBrowser=always ...`.
13
+
14
+ If no preferences line is present, treat all preferences as `"ask"`.
15
+
16
+ When you reach a decision gated by a preference:
17
+ - **`always`** → proceed without asking the user
18
+ - **`never`** → skip without asking the user
19
+ - **`ask`** → ask the user, then offer: "Want me to remember this choice for future sessions?" If yes, call `muggle-local-preferences-set` with the key, their chosen value, and scope `global`.
20
+
21
+ This skill uses these preferences:
22
+
23
+ | Preference | Decision it gates |
24
+ |------------|------------------|
25
+ | `checkForUpdates` | Check for newer Muggle version |
26
+
10
27
  ## Menu
11
28
 
12
- When user asks for "muggle" with no specific subcommand, use `AskQuestion` to present the command set as clickable options:
29
+ When user asks for "muggle" with no specific subcommand, use `AskQuestion` to present these four options:
13
30
 
14
- - "Test my changes — change-driven E2E acceptance testing (local or remote)" → `muggle-test`
15
- - "Test a feature on localhost — run a single E2E test locally" → `muggle-test-feature-local`
16
- - "Autonomous dev pipeline — requirements to PR" → `muggle-do`
17
- - "Health check — verify installation status" → `muggle-status`
18
- - "Repair — fix broken installation" → `muggle-repair`
31
+ - "Test a feature — run E2E acceptance tests locally or remotely" → `muggle-test-feature-local` (local) or `muggle-test` (remote/change-driven)
32
+ - "Build something — implement a feature with E2E acceptance tests and a visual PR" → `muggle-do`
33
+ - "Health check — verify installation and fix issues" → `muggle-status` (then `muggle-repair` if issues found)
19
34
  - "Upgrade — update to latest version" → `muggle-upgrade`
20
35
 
21
36
  ## Routing
@@ -26,7 +41,8 @@ If the user intent clearly matches one command, route directly — no menu neede
26
41
  - repair/fix/install broken → `muggle-repair`
27
42
  - upgrade/update latest → `muggle-upgrade`
28
43
  - test my changes/acceptance test my work/test before push/post E2E acceptance results to PR/test on staging/test on preview → `muggle-test`
29
- - test localhost/validate single feature → `muggle-test-feature-local`
30
- - build/implement from request → `muggle-do`
44
+ - test localhost/validate single feature/test a feature → `muggle-test-feature-local`
45
+ - build/implement from request/end-to-end → `muggle-do`
46
+ - post results to PR/attach walkthrough/visual evidence on PR → `muggle-pr-visual-walkthrough`
31
47
 
32
48
  If intent is ambiguous, use `AskQuestion` with the most likely options rather than asking the user to type a clarification.
@@ -17,6 +17,23 @@ This is the **canonical PR-walkthrough workflow** shared across every Muggle ent
17
17
 
18
18
  Rendering is always done by `muggle build-pr-section`, a battle-tested CLI that handles deterministic markdown layout, per-step screenshots, and automatic fit-vs-overflow (oversized content spills into a follow-up comment). Never hand-write the walkthrough markdown.
19
19
 
20
+ ## Preferences
21
+
22
+ User preferences are available in the session context (injected at session start). Look for the line starting with `Muggle Preferences` — it contains key=value pairs like `autoLogin=ask showElectronBrowser=always ...`.
23
+
24
+ If no preferences line is present, treat all preferences as `"ask"`.
25
+
26
+ When you reach a decision gated by a preference:
27
+ - **`always`** → proceed without asking the user
28
+ - **`never`** → skip without asking the user
29
+ - **`ask`** → ask the user, then offer: "Want me to remember this choice for future sessions?" If yes, call `muggle-local-preferences-set` with the key, their chosen value, and scope `global`.
30
+
31
+ This skill uses these preferences:
32
+
33
+ | Preference | Decision it gates |
34
+ |------------|------------------|
35
+ | `postPRVisualWalkthrough` | Post visual walkthrough to PR |
36
+
20
37
  ## Input contract: the `E2eReport` JSON
21
38
 
22
39
  Every caller must build an `E2eReport` JSON object and have it in conversation context before invoking this skill. The schema is defined in `src/cli/pr-section/types.ts` (`E2eReportSchema`) and enforced by the CLI with Zod — malformed input exits non-zero with a descriptive stderr message.