@muggleai/works 4.8.0 → 4.8.2

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 (39) hide show
  1. package/README.md +5 -3
  2. package/dist/{chunk-OUI734ME.js → chunk-44I5ROCB.js} +44 -16
  3. package/dist/{chunk-2ZDLQAO4.js → chunk-OMLNCNSZ.js} +60 -27
  4. package/dist/cli.js +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  7. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  8. package/dist/plugin/README.md +1 -1
  9. package/dist/plugin/skills/do/e2e-acceptance.md +28 -17
  10. package/dist/plugin/skills/do/impact-analysis.md +9 -1
  11. package/dist/plugin/skills/do/open-prs.md +23 -1
  12. package/dist/plugin/skills/do/pre-flight.md +104 -0
  13. package/dist/plugin/skills/do/requirements.md +11 -1
  14. package/dist/plugin/skills/do/unit-tests.md +9 -1
  15. package/dist/plugin/skills/do/validate-code.md +9 -1
  16. package/dist/plugin/skills/muggle-do/SKILL.md +66 -22
  17. package/dist/plugin/skills/muggle-test/SKILL.md +13 -1
  18. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +13 -2
  19. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +14 -21
  20. package/dist/plugin/skills/muggle-upgrade/SKILL.md +1 -1
  21. package/dist/release-manifest.json +4 -4
  22. package/dist/{src-2IDMKEJ5.js → src-ZRUONWKV.js} +1 -1
  23. package/package.json +6 -6
  24. package/plugin/.claude-plugin/plugin.json +1 -1
  25. package/plugin/.cursor-plugin/plugin.json +1 -1
  26. package/plugin/README.md +1 -1
  27. package/plugin/skills/do/e2e-acceptance.md +28 -17
  28. package/plugin/skills/do/impact-analysis.md +9 -1
  29. package/plugin/skills/do/open-prs.md +23 -1
  30. package/plugin/skills/do/pre-flight.md +104 -0
  31. package/plugin/skills/do/requirements.md +11 -1
  32. package/plugin/skills/do/unit-tests.md +9 -1
  33. package/plugin/skills/do/validate-code.md +9 -1
  34. package/plugin/skills/muggle-do/SKILL.md +66 -22
  35. package/plugin/skills/muggle-test/SKILL.md +13 -1
  36. package/plugin/skills/muggle-test-feature-local/SKILL.md +13 -2
  37. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +14 -21
  38. package/plugin/skills/muggle-upgrade/SKILL.md +1 -1
  39. package/scripts/postinstall.mjs +45 -0
package/README.md CHANGED
@@ -55,7 +55,9 @@ This installs:
55
55
  npm install -g @muggleai/works
56
56
  ```
57
57
 
58
- Then configure your MCP client:
58
+ For Cursor, that's it — the install automatically configures `~/.cursor/mcp.json` and syncs `muggle-*` skills to `~/.cursor/skills/`. Just restart Cursor.
59
+
60
+ For other MCP clients, add this to your client's config:
59
61
 
60
62
  ```json
61
63
  {
@@ -71,7 +73,7 @@ Then configure your MCP client:
71
73
  }
72
74
  ```
73
75
 
74
- `npm install` also syncs `muggle-*` skills to `~/.cursor/skills/` for Cursor discovery. Claude slash commands are plugin-managed, so update those with `/plugin update muggleai@muggle-works`.
76
+ Claude slash commands are plugin-managed, so update those with `/plugin update muggleai@muggle-works`.
75
77
 
76
78
  ### 2. Verify
77
79
 
@@ -519,7 +521,7 @@ muggle-ai-works/
519
521
  │ ├── verify-plugin-marketplace.mjs # Validates plugin/marketplace consistency
520
522
  │ ├── verify-compatibility-contracts.mjs # Validates long-term surface contracts
521
523
  │ ├── verify-upgrade-experience.mjs # Validates in-place upgrade behavior
522
- │ └── postinstall.mjs # npm postinstall (Electron app download)
524
+ │ └── postinstall.mjs # npm postinstall (Electron app download, Cursor MCP config, skills sync)
523
525
 
524
526
  ├── config/compatibility/ # Contract baselines (CLI/MCP/plugin/skills)
525
527
  ├── bin/ # CLI entrypoint (muggle.js → dist/cli.js)
@@ -2211,6 +2211,9 @@ async function executeElectronAppAsync(params) {
2211
2211
  if (params.showUi) {
2212
2212
  spawnArgs.push("--show-ui");
2213
2213
  }
2214
+ if (params.freshSession) {
2215
+ spawnArgs.push("--fresh-session");
2216
+ }
2214
2217
  logger4.info("Spawning electron-app for local execution", {
2215
2218
  runId: params.runId,
2216
2219
  mode,
@@ -2359,7 +2362,8 @@ async function executeTestGeneration(params) {
2359
2362
  scriptFilePath: inputFilePath,
2360
2363
  authFilePath,
2361
2364
  timeoutMs,
2362
- showUi: params.showUi
2365
+ showUi: params.showUi,
2366
+ freshSession: params.freshSession
2363
2367
  });
2364
2368
  const completedAt = Date.now();
2365
2369
  const executionTimeMs = completedAt - startedAt;
@@ -2505,7 +2509,8 @@ async function executeReplay(params) {
2505
2509
  scriptFilePath: inputFilePath,
2506
2510
  authFilePath,
2507
2511
  timeoutMs,
2508
- showUi: params.showUi
2512
+ showUi: params.showUi,
2513
+ freshSession: params.freshSession
2509
2514
  });
2510
2515
  const completedAt = Date.now();
2511
2516
  const executionTimeMs = completedAt - startedAt;
@@ -3239,6 +3244,12 @@ var WorkflowStartTestScriptGenerationInputSchema = z.object({
3239
3244
  expectedResult: z.string().min(1).describe("Expected result"),
3240
3245
  workflowParams: WorkflowParamsSchema
3241
3246
  });
3247
+ var WorkflowStartTestScriptGenerationBulkInputSchema = z.object({
3248
+ projectId: IdSchema.describe("Project ID (UUID)"),
3249
+ name: z.string().min(1).describe("Workflow name"),
3250
+ testCaseIds: z.array(IdSchema).optional().describe("Optional: targeted test case UUIDs to generate scripts for; when absent generates for all eligible test cases in the project"),
3251
+ workflowParams: WorkflowParamsSchema
3252
+ });
3242
3253
  var WorkflowGetLatestScriptGenByTestCaseInputSchema = z.object({
3243
3254
  testCaseId: IdSchema.describe("Test case ID (UUID)")
3244
3255
  });
@@ -4208,6 +4219,25 @@ var workflowTools = [
4208
4219
  };
4209
4220
  }
4210
4221
  },
4222
+ {
4223
+ name: "muggle-remote-workflow-start-test-script-generation-bulk",
4224
+ description: "Start a bulk test script generation workflow to generate scripts for multiple test cases in a single request.",
4225
+ inputSchema: WorkflowStartTestScriptGenerationBulkInputSchema,
4226
+ mapToUpstream: (input) => {
4227
+ const data = input;
4228
+ return {
4229
+ method: "POST",
4230
+ path: `${MUGGLE_TEST_PREFIX}/workflow/test-script/test-script-generation/bulk`,
4231
+ body: {
4232
+ projectId: data.projectId,
4233
+ name: data.name,
4234
+ ...data.testCaseIds && { testCaseIds: data.testCaseIds },
4235
+ ...data.workflowParams && { workflowParams: data.workflowParams }
4236
+ },
4237
+ timeoutMs: getWorkflowTimeoutMs()
4238
+ };
4239
+ }
4240
+ },
4211
4241
  {
4212
4242
  name: "muggle-remote-wf-get-ts-gen-latest-run",
4213
4243
  description: "Get the latest run status for a test script generation workflow runtime.",
@@ -5146,14 +5176,6 @@ __export(local_exports, {
5146
5176
  executeTool: () => executeTool,
5147
5177
  getTool: () => getTool
5148
5178
  });
5149
- var AuthLoginInputSchema2 = z.object({
5150
- waitForCompletion: z.boolean().optional().describe("Whether to wait for browser login completion before returning. Default: true"),
5151
- timeoutMs: z.number().int().positive().min(1e3).max(9e5).optional().describe("Maximum time to wait for login completion in milliseconds. Default: 120000")
5152
- });
5153
- var AuthPollInputSchema2 = z.object({
5154
- deviceCode: z.string().optional().describe("Device code from the login response. Optional if a login was recently started.")
5155
- });
5156
- var EmptyInputSchema2 = z.object({});
5157
5179
  var TestCaseDetailsSchema = z.object({
5158
5180
  /** Cloud test case ID. */
5159
5181
  id: MuggleEntityIdSchema.describe("Cloud test case ID (UUID)"),
@@ -5200,7 +5222,9 @@ var ExecuteTestGenerationInputSchema = z.object({
5200
5222
  /** Optional timeout. */
5201
5223
  timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds (default: 300000 = 5 min)"),
5202
5224
  /** Show the electron-app UI during execution. Default: visible window. Pass false to run headless. */
5203
- showUi: z.boolean().optional().describe("Show the electron-app UI during generation. Defaults to visible; pass false to run headless.")
5225
+ showUi: z.boolean().optional().describe("Show the electron-app UI during generation. Defaults to visible; pass false to run headless."),
5226
+ /** Clear all session storage (cookies, localStorage, etc.) before execution. Use for test cases that require a clean browser state, such as registration, login, or cookie consent flows. */
5227
+ freshSession: z.boolean().optional().describe("Clear all session storage (cookies, localStorage, etc.) before execution. Use for test cases that require a clean browser state \u2014 e.g. registration, login, or cookie consent flows. Default: false.")
5204
5228
  });
5205
5229
  var ExecuteReplayInputSchema = z.object({
5206
5230
  /** Test script metadata from muggle-remote-test-script-get. */
@@ -5212,7 +5236,9 @@ var ExecuteReplayInputSchema = z.object({
5212
5236
  /** Optional timeout. */
5213
5237
  timeoutMs: z.number().int().positive().optional().describe("Timeout in milliseconds (default: 180000 = 3 min)"),
5214
5238
  /** Show the electron-app UI during execution. Default: visible window. Pass false to run headless. */
5215
- showUi: z.boolean().optional().describe("Show the electron-app UI during replay. Defaults to visible; pass false to run headless.")
5239
+ showUi: z.boolean().optional().describe("Show the electron-app UI during replay. Defaults to visible; pass false to run headless."),
5240
+ /** Clear all session storage (cookies, localStorage, etc.) before execution. Use for test cases that require a clean browser state, such as registration, login, or cookie consent flows. */
5241
+ freshSession: z.boolean().optional().describe("Clear all session storage (cookies, localStorage, etc.) before execution. Use for test cases that require a clean browser state \u2014 e.g. registration, login, or cookie consent flows. Default: false.")
5216
5242
  });
5217
5243
  var CancelExecutionInputSchema = z.object({
5218
5244
  runId: MuggleEntityIdSchema.describe("Run ID (UUID) to cancel")
@@ -5240,6 +5266,7 @@ var ListSessionsInputSchema = z.object({
5240
5266
  var CleanupSessionsInputSchema = z.object({
5241
5267
  max_age_days: z.number().int().min(0).optional().describe("Maximum age of sessions to keep (in days). Sessions older than this will be deleted. Defaults to 30.")
5242
5268
  });
5269
+ var EmptyInputSchema2 = z.object({});
5243
5270
 
5244
5271
  // packages/mcps/src/mcp/tools/local/tool-registry.ts
5245
5272
  function createChildLogger2(correlationId) {
@@ -5464,7 +5491,8 @@ var executeTestGenerationTool = {
5464
5491
  testCase: input.testCase,
5465
5492
  localUrl: input.localUrl,
5466
5493
  timeoutMs: input.timeoutMs,
5467
- showUi
5494
+ showUi,
5495
+ freshSession: input.freshSession
5468
5496
  });
5469
5497
  const content = [
5470
5498
  "## Test Generation " + (result.status === "passed" ? "Successful" : "Failed"),
@@ -5503,7 +5531,8 @@ var executeReplayTool = {
5503
5531
  actionScript: input.actionScript,
5504
5532
  localUrl: input.localUrl,
5505
5533
  timeoutMs: input.timeoutMs,
5506
- showUi
5534
+ showUi,
5535
+ freshSession: input.freshSession
5507
5536
  });
5508
5537
  const content = [
5509
5538
  "## Test Replay " + (result.status === "passed" ? "Successful" : "Failed"),
@@ -5799,6 +5828,7 @@ __export(e2e_exports2, {
5799
5828
  WorkflowMemoryParamsSchema: () => WorkflowMemoryParamsSchema,
5800
5829
  WorkflowParamsSchema: () => WorkflowParamsSchema,
5801
5830
  WorkflowStartTestCaseDetectionInputSchema: () => WorkflowStartTestCaseDetectionInputSchema,
5831
+ WorkflowStartTestScriptGenerationBulkInputSchema: () => WorkflowStartTestScriptGenerationBulkInputSchema,
5802
5832
  WorkflowStartTestScriptGenerationInputSchema: () => WorkflowStartTestScriptGenerationInputSchema,
5803
5833
  WorkflowStartTestScriptReplayBulkInputSchema: () => WorkflowStartTestScriptReplayBulkInputSchema,
5804
5834
  WorkflowStartTestScriptReplayInputSchema: () => WorkflowStartTestScriptReplayInputSchema,
@@ -5824,8 +5854,6 @@ function getQaTools() {
5824
5854
  // packages/mcps/src/mcp/local/index.ts
5825
5855
  var local_exports2 = {};
5826
5856
  __export(local_exports2, {
5827
- AuthLoginInputSchema: () => AuthLoginInputSchema2,
5828
- AuthPollInputSchema: () => AuthPollInputSchema2,
5829
5857
  AuthService: () => AuthService,
5830
5858
  CancelExecutionInputSchema: () => CancelExecutionInputSchema,
5831
5859
  CleanupSessionsInputSchema: () => CleanupSessionsInputSchema,
@@ -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-OUI734ME.js';
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';
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';
@@ -6,12 +6,12 @@ import { z, ZodError } from 'zod';
6
6
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
7
7
  import * as fs from 'fs';
8
8
  import { readFileSync, existsSync, rmSync, mkdirSync, readdirSync, createWriteStream, writeFileSync, statSync } from 'fs';
9
- import * as path from 'path';
9
+ import * as path4 from 'path';
10
10
  import { dirname, resolve, join } from 'path';
11
11
  import { fileURLToPath } from 'url';
12
12
  import { Command } from 'commander';
13
13
  import axios from 'axios';
14
- import { platform, arch, homedir } from 'os';
14
+ import { platform, homedir, arch } from 'os';
15
15
  import { execFile } from 'child_process';
16
16
  import { pipeline } from 'stream/promises';
17
17
 
@@ -486,7 +486,7 @@ function renderResultSummary(test, projectId) {
486
486
  lines.push(`**Error:** \`${safeInlineCode(test.error)}\``);
487
487
  }
488
488
  lines.push(`**Steps:** ${test.steps.length}`);
489
- lines.push(`[View on Muggle AI dashboard \u2192](${dashboardUrl})`);
489
+ lines.push(`[View steps on Muggle AI \u2192](${dashboardUrl})`);
490
490
  return lines;
491
491
  }
492
492
  function renderBody(report, opts) {
@@ -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-2IDMKEJ5.js');
689
+ const mcps = await import('./src-ZRUONWKV.js');
690
690
  const credentials = await mcps.getCallerCredentialsAsync();
691
691
  if (!credentials.bearerToken && !credentials.apiKey) {
692
692
  stderrWrite(
@@ -807,13 +807,13 @@ var CURSOR_SKILLS_SUBDIR = "skills";
807
807
  var MUGGLE_SKILL_PREFIX = "muggle";
808
808
  var INSTALL_MANIFEST_FILE = "install-manifest.json";
809
809
  function getElectronAppBaseDir() {
810
- return path.join(getDataDir(), ELECTRON_APP_DIR);
810
+ return path4.join(getDataDir(), ELECTRON_APP_DIR);
811
811
  }
812
812
  function getCursorSkillsDir() {
813
- return path.join(homedir(), CURSOR_SKILLS_DIR, CURSOR_SKILLS_SUBDIR);
813
+ return path4.join(homedir(), CURSOR_SKILLS_DIR, CURSOR_SKILLS_SUBDIR);
814
814
  }
815
815
  function getInstallManifestPath() {
816
- return path.join(getDataDir(), INSTALL_MANIFEST_FILE);
816
+ return path4.join(getDataDir(), INSTALL_MANIFEST_FILE);
817
817
  }
818
818
  function readInstallManifest() {
819
819
  const manifestPath = getInstallManifestPath();
@@ -851,7 +851,7 @@ function listObsoleteSkills() {
851
851
  if (manifestSkills.has(entry.name)) {
852
852
  continue;
853
853
  }
854
- const skillPath = path.join(skillsDir, entry.name);
854
+ const skillPath = path4.join(skillsDir, entry.name);
855
855
  const sizeBytes = getDirectorySize(skillPath);
856
856
  obsoleteSkills.push({
857
857
  name: entry.name,
@@ -897,7 +897,7 @@ function getDirectorySize(dirPath) {
897
897
  try {
898
898
  const entries = readdirSync(dirPath, { withFileTypes: true });
899
899
  for (const entry of entries) {
900
- const fullPath = path.join(dirPath, entry.name);
900
+ const fullPath = path4.join(dirPath, entry.name);
901
901
  if (entry.isDirectory()) {
902
902
  totalSize += getDirectorySize(fullPath);
903
903
  } else if (entry.isFile()) {
@@ -950,7 +950,7 @@ function listInstalledVersions() {
950
950
  if (!/^\d+\.\d+\.\d+$/.test(entry.name)) {
951
951
  continue;
952
952
  }
953
- const versionPath = path.join(baseDir, entry.name);
953
+ const versionPath = path4.join(baseDir, entry.name);
954
954
  const sizeBytes = getDirectorySize(versionPath);
955
955
  versions.push({
956
956
  version: entry.name,
@@ -1106,11 +1106,11 @@ function getExpectedExecutablePath(versionDir) {
1106
1106
  const os = platform();
1107
1107
  switch (os) {
1108
1108
  case "darwin":
1109
- return path.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
1109
+ return path4.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
1110
1110
  case "win32":
1111
- return path.join(versionDir, "MuggleAI.exe");
1111
+ return path4.join(versionDir, "MuggleAI.exe");
1112
1112
  case "linux":
1113
- return path.join(versionDir, "MuggleAI");
1113
+ return path4.join(versionDir, "MuggleAI");
1114
1114
  default:
1115
1115
  throw new Error(`Unsupported platform: ${os}`);
1116
1116
  }
@@ -1119,7 +1119,7 @@ function verifyElectronAppInstallation() {
1119
1119
  const version = getElectronAppVersion();
1120
1120
  const versionDir = getElectronAppDir(version);
1121
1121
  const executablePath = getExpectedExecutablePath(versionDir);
1122
- const metadataPath = path.join(versionDir, ".install-metadata.json");
1122
+ const metadataPath = path4.join(versionDir, ".install-metadata.json");
1123
1123
  const result = {
1124
1124
  valid: false,
1125
1125
  versionDir,
@@ -1326,7 +1326,7 @@ function runDiagnostics() {
1326
1326
  name: "Cursor MCP Config",
1327
1327
  passed: cursorMcpConfigValidationResult.passed,
1328
1328
  description: cursorMcpConfigValidationResult.description,
1329
- suggestion: "Re-run npm install -g @muggleai/works to refresh ~/.cursor/mcp.json"
1329
+ suggestion: "Run 'muggle setup' to configure ~/.cursor/mcp.json"
1330
1330
  });
1331
1331
  return results;
1332
1332
  }
@@ -1640,11 +1640,11 @@ function getExpectedExecutablePath2(versionDir) {
1640
1640
  const os = platform();
1641
1641
  switch (os) {
1642
1642
  case "darwin":
1643
- return path.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
1643
+ return path4.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
1644
1644
  case "win32":
1645
- return path.join(versionDir, "MuggleAI.exe");
1645
+ return path4.join(versionDir, "MuggleAI.exe");
1646
1646
  case "linux":
1647
- return path.join(versionDir, "MuggleAI");
1647
+ return path4.join(versionDir, "MuggleAI");
1648
1648
  default:
1649
1649
  throw new Error(`Unsupported platform: ${os}`);
1650
1650
  }
@@ -1740,10 +1740,43 @@ function cleanupFailedInstall(versionDir) {
1740
1740
  }
1741
1741
  }
1742
1742
  }
1743
+ function upsertCursorMcpConfig() {
1744
+ const cursorMcpConfigPath = path4.join(homedir(), ".cursor", "mcp.json");
1745
+ const cursorDir = path4.join(homedir(), ".cursor");
1746
+ let config = {};
1747
+ if (existsSync(cursorMcpConfigPath)) {
1748
+ try {
1749
+ const raw = readFileSync(cursorMcpConfigPath, "utf-8");
1750
+ const parsed = JSON.parse(raw);
1751
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
1752
+ console.log("Warning: ~/.cursor/mcp.json has unexpected shape, skipping MCP config upsert.");
1753
+ return;
1754
+ }
1755
+ config = parsed;
1756
+ } catch (error) {
1757
+ const message = error instanceof Error ? error.message : String(error);
1758
+ console.log("Warning: ~/.cursor/mcp.json is invalid JSON, skipping MCP config upsert.");
1759
+ console.log(` Parse error: ${message}`);
1760
+ return;
1761
+ }
1762
+ }
1763
+ if (!config.mcpServers || typeof config.mcpServers !== "object") {
1764
+ config.mcpServers = {};
1765
+ }
1766
+ config.mcpServers.muggle = {
1767
+ command: "muggle",
1768
+ args: ["serve"]
1769
+ };
1770
+ mkdirSync(cursorDir, { recursive: true });
1771
+ writeFileSync(cursorMcpConfigPath, `${JSON.stringify(config, null, 2)}
1772
+ `, "utf-8");
1773
+ console.log(`Cursor MCP config updated at ${cursorMcpConfigPath}`);
1774
+ }
1743
1775
  async function setupCommand(options) {
1744
1776
  const version = getElectronAppVersion();
1745
1777
  const versionDir = getElectronAppDir(version);
1746
1778
  const platformKey = getPlatformKey();
1779
+ upsertCursorMcpConfig();
1747
1780
  if (!options.force && isElectronAppInstalled()) {
1748
1781
  console.log(`Electron app v${version} is already installed at ${versionDir}`);
1749
1782
  console.log("Use --force to re-download.");
@@ -1761,7 +1794,7 @@ async function setupCommand(options) {
1761
1794
  rmSync(versionDir, { recursive: true, force: true });
1762
1795
  }
1763
1796
  mkdirSync(versionDir, { recursive: true });
1764
- const tempFile = path.join(versionDir, binaryName);
1797
+ const tempFile = path4.join(versionDir, binaryName);
1765
1798
  await downloadWithRetry(downloadUrl, tempFile);
1766
1799
  console.log("Download complete, verifying checksum...");
1767
1800
  const checksums = getElectronAppChecksums();
@@ -1797,7 +1830,7 @@ The archive may be corrupted or in an unexpected format.`
1797
1830
  );
1798
1831
  }
1799
1832
  const executableChecksum = await calculateFileChecksum(executablePath);
1800
- const metadataPath = path.join(versionDir, INSTALL_METADATA_FILE_NAME);
1833
+ const metadataPath = path4.join(versionDir, INSTALL_METADATA_FILE_NAME);
1801
1834
  writeInstallMetadata({
1802
1835
  metadataPath,
1803
1836
  version,
@@ -1839,7 +1872,7 @@ function extractVersionFromTag(tag) {
1839
1872
  return match ? match[1] : null;
1840
1873
  }
1841
1874
  function getVersionOverridePath() {
1842
- return path.join(getDataDir(), VERSION_OVERRIDE_FILE);
1875
+ return path4.join(getDataDir(), VERSION_OVERRIDE_FILE);
1843
1876
  }
1844
1877
  function getEffectiveElectronAppVersion() {
1845
1878
  const overridePath = getVersionOverridePath();
@@ -1927,11 +1960,11 @@ function getExpectedExecutablePath3(versionDir) {
1927
1960
  const os = platform();
1928
1961
  switch (os) {
1929
1962
  case "darwin":
1930
- return path.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
1963
+ return path4.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
1931
1964
  case "win32":
1932
- return path.join(versionDir, "MuggleAI.exe");
1965
+ return path4.join(versionDir, "MuggleAI.exe");
1933
1966
  case "linux":
1934
- return path.join(versionDir, "MuggleAI");
1967
+ return path4.join(versionDir, "MuggleAI");
1935
1968
  default:
1936
1969
  throw new Error(`Unsupported platform: ${os}`);
1937
1970
  }
@@ -2043,7 +2076,7 @@ async function downloadAndInstall(version, downloadUrl, checksum) {
2043
2076
  if (!response.ok) {
2044
2077
  throw new Error(`Download failed: ${response.status} ${response.statusText}`);
2045
2078
  }
2046
- const tempFile = path.join(versionDir, binaryName);
2079
+ const tempFile = path4.join(versionDir, binaryName);
2047
2080
  const fileStream = createWriteStream(tempFile);
2048
2081
  if (!response.body) {
2049
2082
  throw new Error("No response body");
@@ -2085,7 +2118,7 @@ The archive may be corrupted or in an unexpected format.`
2085
2118
  );
2086
2119
  }
2087
2120
  const executableChecksum = await calculateFileChecksum(executablePath);
2088
- const metadataPath = path.join(versionDir, INSTALL_METADATA_FILE_NAME2);
2121
+ const metadataPath = path4.join(versionDir, INSTALL_METADATA_FILE_NAME2);
2089
2122
  writeInstallMetadata2({
2090
2123
  metadataPath,
2091
2124
  version,
package/dist/cli.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './chunk-2ZDLQAO4.js';
3
- import './chunk-OUI734ME.js';
2
+ import { runCli } from './chunk-OMLNCNSZ.js';
3
+ import './chunk-44I5ROCB.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-2ZDLQAO4.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-OUI734ME.js';
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,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.0",
4
+ "version": "4.8.2",
5
5
  "author": {
6
6
  "name": "Muggle AI",
7
7
  "email": "support@muggle-ai.com"
@@ -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.0",
5
+ "version": "4.8.2",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"
@@ -15,7 +15,7 @@ For npm installs:
15
15
  npm install -g @muggleai/works
16
16
  ```
17
17
 
18
- This updates the CLI and syncs `muggle-*` skills into `~/.cursor/skills/` for Cursor. Claude slash commands remain plugin-managed, so use `/plugin update muggleai@muggle-works` to refresh them.
18
+ This updates the CLI, configures Cursor MCP (`~/.cursor/mcp.json`), and syncs `muggle-*` skills into `~/.cursor/skills/`. Claude slash commands remain plugin-managed, so use `/plugin update muggleai@muggle-works` to refresh them.
19
19
 
20
20
  ## Skills
21
21
 
@@ -1,7 +1,15 @@
1
- # E2E / acceptance agent
1
+ # E2E / acceptance agent (Stage 6/7)
2
2
 
3
3
  You are running **end-to-end (E2E) acceptance** test cases against code changes using Muggle AI's local testing infrastructure. These tests simulate real users in a browser — they are not unit tests.
4
4
 
5
+ ## Turn preamble
6
+
7
+ Start the turn with:
8
+
9
+ ```
10
+ **Stage 6/7 — E2E acceptance** — running browser tests against the validation target from pre-flight.
11
+ ```
12
+
5
13
  ## Design
6
14
 
7
15
  E2E acceptance testing runs **locally** using the `test-feature-local` approach:
@@ -15,32 +23,35 @@ This guarantees E2E acceptance tests always run — no dependency on cloud repla
15
23
 
16
24
  ## Input
17
25
 
18
- You receive:
19
- - The Muggle project ID
26
+ You receive everything from `state.md` already — pre-flight resolved it:
27
+
28
+ - `localUrl` — the locally running dev server URL
29
+ - `projectId` — the chosen Muggle project
30
+ - The validation strategy (`local-e2e`, `staging-replay`, `unit-only`, `skip`)
31
+ - Test-user credential status (existing / new / skip)
20
32
  - The list of changed repos, files, and a summary of changes
21
33
  - The requirements goal
22
- - `localUrl` per repo (from `muggle-repos.json`) — the locally running dev server URL
23
34
 
24
35
  ## Your Job
25
36
 
26
- ### Step 0: Resolve Local URL
37
+ ### Step 0: Consume pre-flight (no user questions)
38
+
39
+ Read `state.md`. If the validation strategy is `unit-only` or `skip`, **do not run this stage** — skip to stage 7 and record the skip reason. Otherwise use `localUrl` directly; **do not ask the user** for it.
40
+
41
+ If `localUrl` or `projectId` is missing from `state.md`, that is a pre-flight bug. **Do not paper over it by asking the user** — escalate once with the session path and halt. The fix is to expand `pre-flight.md`, not to grow a new question here.
27
42
 
28
- Read `localUrl` for each repo from the context. If it is not provided, ask the user:
29
- > "E2E acceptance testing requires a running local server. What URL is the `<repo>` app running on? (e.g. `http://localhost:3000`)"
43
+ ### Step 0.5: Pre-flight verification probes
30
44
 
31
- **Do not skip E2E acceptance tests.** Wait for the user to provide the URL before proceeding.
45
+ Before launching Electron, run these live checks and fail loudly if any fails:
32
46
 
33
- ### Step 1: Check Authentication
47
+ 1. `curl -s -o /dev/null -w "%{http_code}" <localUrl>` — expect 2xx or 3xx. If the dev server isn't up, halt with the exact command the user needs to start it.
48
+ 2. If a backend URL is recorded, probe its health endpoint. A 5xx or unreachable backend means the dashboard will render in an error state and test results will be meaningless — halt.
49
+ 3. `muggle-remote-auth-status` — must be `authenticated`. If not, the pre-flight missed this; escalate.
50
+ 4. If test credentials were marked `existing`, confirm the Auth0 tenant in the repo's env matches the tenant the secrets were created under (recorded in `state.md`). Tenant mismatch → halt with "existing secrets target tenant X, local dev targets tenant Y — update pre-flight to collect new credentials."
34
51
 
35
- - `muggle-remote-auth-status`
36
- - If **authenticated**: print the logged-in email and ask via `AskQuestion`:
37
- > "You're logged in as **{email}**. Continue with this account?"
38
- - Option 1: "Yes, continue"
39
- - Option 2: "No, switch account"
40
- If the user picks "switch account", call `muggle-remote-auth-login` with `forceNewSession: true` then `muggle-remote-auth-poll`.
41
- - If **not signed in or expired**: `muggle-remote-auth-login` then `muggle-remote-auth-poll`
52
+ ### Step 1: Authentication already verified
42
53
 
43
- Do not skip or assume auth.
54
+ Pre-flight handled auth. If `muggle-remote-auth-status` somehow shows expired here (session clock skew, etc.), re-auth silently via `muggle-remote-auth-login` + `muggle-remote-auth-poll` — but do not ask the user "continue with this account?" again.
44
55
 
45
56
  ### Step 2: Get Test Cases
46
57
 
@@ -1,7 +1,15 @@
1
- # Impact Analysis Agent
1
+ # Impact Analysis Agent (Stage 3/7)
2
2
 
3
3
  You are analyzing git repositories to determine which ones have actual code changes that need to go through the dev cycle pipeline.
4
4
 
5
+ ## Turn preamble
6
+
7
+ Start the turn with:
8
+
9
+ ```
10
+ **Stage 3/7 — Impact analysis** — diffing each affected repo against its default branch.
11
+ ```
12
+
5
13
  ## Input
6
14
 
7
15
  You receive:
@@ -1,7 +1,29 @@
1
- # PR Creation Agent
1
+ # PR Creation Agent (Stage 7/7)
2
2
 
3
3
  You are creating pull requests for each repository that has changes after a successful dev cycle run.
4
4
 
5
+ ## Turn preamble
6
+
7
+ Start the turn with:
8
+
9
+ ```
10
+ **Stage 7/7 — Open PR** — rendering the visual walkthrough and pushing the PR.
11
+ ```
12
+
13
+ ## Non-negotiable: visual walkthrough is required
14
+
15
+ **You MUST invoke `muggle-pr-visual-walkthrough` (Mode B) to render the E2E section of the PR body.** Hand-writing the PR body with a text summary and `gh pr create` is a stage failure — reviewers rely on the dashboard links and per-step screenshots the walkthrough produces.
16
+
17
+ If the E2E stage was skipped (validation was `unit-only` or `skip`), you may omit the walkthrough section — but mark the PR title with `[UNVERIFIED]` or `[UNIT-ONLY]` accordingly, and record the reason in the PR body under `## Validation`.
18
+
19
+ Before calling `gh pr create`, self-check:
20
+
21
+ - [ ] `muggle-pr-visual-walkthrough` was invoked (or the skip reason is recorded).
22
+ - [ ] The `body` returned by the skill is embedded verbatim in the PR body.
23
+ - [ ] If `comment` is non-null, it will be posted as a follow-up after the PR is created.
24
+
25
+ If you cannot check all three, **halt** — do not create the PR. Fix the upstream stage first.
26
+
5
27
  ## Input
6
28
 
7
29
  You receive: