@muggleai/works 4.8.0 → 4.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -3
- package/dist/{chunk-OUI734ME.js → chunk-44I5ROCB.js} +44 -16
- package/dist/{chunk-2ZDLQAO4.js → chunk-OMLNCNSZ.js} +60 -27
- package/dist/cli.js +2 -2
- package/dist/index.js +2 -2
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/README.md +1 -1
- package/dist/plugin/skills/muggle-test/SKILL.md +13 -1
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +13 -2
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +14 -21
- package/dist/plugin/skills/muggle-upgrade/SKILL.md +1 -1
- package/dist/release-manifest.json +4 -4
- package/dist/{src-2IDMKEJ5.js → src-ZRUONWKV.js} +1 -1
- package/package.json +6 -6
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/README.md +1 -1
- package/plugin/skills/muggle-test/SKILL.md +13 -1
- package/plugin/skills/muggle-test-feature-local/SKILL.md +13 -2
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +14 -21
- package/plugin/skills/muggle-upgrade/SKILL.md +1 -1
- 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
|
-
|
|
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
|
-
|
|
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-
|
|
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
|
|
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,
|
|
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
|
|
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-
|
|
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
|
|
810
|
+
return path4.join(getDataDir(), ELECTRON_APP_DIR);
|
|
811
811
|
}
|
|
812
812
|
function getCursorSkillsDir() {
|
|
813
|
-
return
|
|
813
|
+
return path4.join(homedir(), CURSOR_SKILLS_DIR, CURSOR_SKILLS_SUBDIR);
|
|
814
814
|
}
|
|
815
815
|
function getInstallManifestPath() {
|
|
816
|
-
return
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
1109
|
+
return path4.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
|
|
1110
1110
|
case "win32":
|
|
1111
|
-
return
|
|
1111
|
+
return path4.join(versionDir, "MuggleAI.exe");
|
|
1112
1112
|
case "linux":
|
|
1113
|
-
return
|
|
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 =
|
|
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: "
|
|
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
|
|
1643
|
+
return path4.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
|
|
1644
1644
|
case "win32":
|
|
1645
|
-
return
|
|
1645
|
+
return path4.join(versionDir, "MuggleAI.exe");
|
|
1646
1646
|
case "linux":
|
|
1647
|
-
return
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
1963
|
+
return path4.join(versionDir, "MuggleAI.app", "Contents", "MacOS", "MuggleAI");
|
|
1931
1964
|
case "win32":
|
|
1932
|
-
return
|
|
1965
|
+
return path4.join(versionDir, "MuggleAI.exe");
|
|
1933
1966
|
case "linux":
|
|
1934
|
-
return
|
|
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 =
|
|
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 =
|
|
2121
|
+
const metadataPath = path4.join(versionDir, INSTALL_METADATA_FILE_NAME2);
|
|
2089
2122
|
writeInstallMetadata2({
|
|
2090
2123
|
metadataPath,
|
|
2091
2124
|
version,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { src_exports as commands, createUnifiedMcpServer, server_exports as server } from './chunk-
|
|
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-
|
|
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.
|
|
4
|
+
"version": "4.8.1",
|
|
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.
|
|
5
|
+
"version": "4.8.1",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/dist/plugin/README.md
CHANGED
|
@@ -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
|
|
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
|
|
|
@@ -200,6 +200,17 @@ If nothing detected, ask as free text: "Your local app should be running. What's
|
|
|
200
200
|
|
|
201
201
|
Before execution, fetch full test case details for all selected test cases by issuing **all** `muggle-remote-test-case-get` calls in parallel (single message, multiple tool calls).
|
|
202
202
|
|
|
203
|
+
### Determine `freshSession` per test case
|
|
204
|
+
|
|
205
|
+
Before executing each test case, inspect its content (title, goal, instructions, preconditions) for signals that it requires a **clean browser state** — no prior cookies, localStorage, or logged-in session. Set `freshSession: true` when the test case involves any of:
|
|
206
|
+
|
|
207
|
+
- **Registration / sign-up** — creating a new account
|
|
208
|
+
- **Login / authentication** — verifying the login flow itself (not a test that merely *uses* login as a prerequisite)
|
|
209
|
+
- **Cookie consent / GDPR banners** — verifying first-visit consent prompts
|
|
210
|
+
- **Onboarding flows** — first-time user experiences that only appear on a fresh session
|
|
211
|
+
|
|
212
|
+
If none of the above apply, omit `freshSession` (defaults to `false`, preserving any existing session state). Evaluate this per test case — in a batch, some may need it and others may not.
|
|
213
|
+
|
|
203
214
|
### Run sequentially (Electron constraint)
|
|
204
215
|
|
|
205
216
|
Execution itself **must** be sequential because there is only one local Electron browser. For each test case, in order:
|
|
@@ -208,6 +219,7 @@ Execution itself **must** be sequential because there is only one local Electron
|
|
|
208
219
|
- `testCase`: Full test case object from the parallel fetch above
|
|
209
220
|
- `localUrl`: User's local URL from the pre-flight question
|
|
210
221
|
- `showUi`: omit (default visible) unless the user explicitly asked for headless, then pass `false`
|
|
222
|
+
- `freshSession`: `true` if the test case requires a clean browser state (see above), omit otherwise
|
|
211
223
|
2. Store the returned `runId`
|
|
212
224
|
|
|
213
225
|
If a generation fails, log it and continue to the next. Do not abort the batch.
|
|
@@ -231,7 +243,7 @@ Store every `viewUrl` — these are used in the next steps.
|
|
|
231
243
|
### Report summary
|
|
232
244
|
|
|
233
245
|
```
|
|
234
|
-
Test Case Status Duration Steps View on Muggle
|
|
246
|
+
Test Case Status Duration Steps View Steps on Muggle AI
|
|
235
247
|
─────────────────────────────────────────────────────────────────────────
|
|
236
248
|
Login with valid creds PASSED 12.3s 8 https://www.muggle-ai.com/...
|
|
237
249
|
Login with invalid creds PASSED 9.1s 6 https://www.muggle-ai.com/...
|
|
@@ -86,17 +86,28 @@ Remind them: local URL is only the execution target, not tied to cloud project c
|
|
|
86
86
|
|
|
87
87
|
### 5. Load data for the chosen path
|
|
88
88
|
|
|
89
|
+
**Determine `freshSession`**
|
|
90
|
+
|
|
91
|
+
Before calling either execution tool, inspect the test case content (title, goal, instructions, preconditions) for signals that the test requires a **clean browser state** — no prior cookies, localStorage, or logged-in session. Pass `freshSession: true` when the test case involves any of:
|
|
92
|
+
|
|
93
|
+
- **Registration / sign-up** — creating a new account
|
|
94
|
+
- **Login / authentication** — verifying the login flow itself (not a test that merely *uses* login as a prerequisite)
|
|
95
|
+
- **Cookie consent / GDPR banners** — verifying first-visit consent prompts
|
|
96
|
+
- **Onboarding flows** — first-time user experiences that only appear on a fresh session
|
|
97
|
+
|
|
98
|
+
If none of the above apply, omit `freshSession` (defaults to `false`, preserving any existing session state).
|
|
99
|
+
|
|
89
100
|
**Generate**
|
|
90
101
|
|
|
91
102
|
1. `muggle-remote-test-case-get`
|
|
92
|
-
2. `muggle-local-execute-test-generation` with that test case + `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`timeoutMs`** — see below)
|
|
103
|
+
2. `muggle-local-execute-test-generation` with that test case + `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`freshSession`** — see above; **`timeoutMs`** — see below)
|
|
93
104
|
|
|
94
105
|
**Replay**
|
|
95
106
|
|
|
96
107
|
1. `muggle-remote-test-script-get` — note `actionScriptId`
|
|
97
108
|
2. `muggle-remote-action-script-get` with that id — full `actionScript`
|
|
98
109
|
**Use the API response as-is.** Do not edit, shorten, or rebuild `actionScript`; replay needs full `label` paths for element lookup.
|
|
99
|
-
3. `muggle-local-execute-replay` with `testScript`, `actionScript`, `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`timeoutMs`** — see below)
|
|
110
|
+
3. `muggle-local-execute-replay` with `testScript`, `actionScript`, `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`freshSession`** — see above; **`timeoutMs`** — see below)
|
|
100
111
|
|
|
101
112
|
### Local execution timeout (`timeoutMs`)
|
|
102
113
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: muggle-test-regenerate-missing
|
|
3
|
-
description: "Bulk-regenerate test scripts for every test case in a Muggle AI project that doesn't currently have an active script. Scans the project, finds test cases stuck in DRAFT or GENERATION_PENDING (no usable script attached), shows the user the list, and on approval kicks off remote test script generation
|
|
3
|
+
description: "Bulk-regenerate test scripts for every test case in a Muggle AI project that doesn't currently have an active script. Scans the project, finds test cases stuck in DRAFT or GENERATION_PENDING (no usable script attached), shows the user the list, and on approval kicks off bulk remote test script generation via the Muggle cloud. Use this skill whenever the user asks to 'regenerate missing scripts', 'fill in missing test scripts', 'generate scripts for test cases without one', 'regen all the test cases that don't have scripts', 'rebuild scripts for stale test cases', 'fix test cases with no script', 'bulk regenerate', or any phrasing that means 'kick off script generation across a project for the cases that need it'. Triggers on: 'regenerate missing test scripts', 'generate scripts for all empty test cases', 'fill the gaps in my test scripts', 'bulk test script regen', 'all my test cases without active scripts'. This is the go-to skill for project-wide script catch-up — it handles discovery, filtering, confirmation, and remote workflow dispatch end-to-end."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Muggle Test — Regenerate Missing Test Scripts
|
|
@@ -116,27 +116,22 @@ After selection, call `AskQuestion` once more for a final confirmation:
|
|
|
116
116
|
|
|
117
117
|
Only proceed after the user picks "Yes".
|
|
118
118
|
|
|
119
|
-
### Step 6 — Dispatch Remote Generations
|
|
119
|
+
### Step 6 — Dispatch Remote Generations (Bulk)
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Send a single bulk request instead of dispatching one workflow per test case:
|
|
122
122
|
|
|
123
|
-
1. Call `muggle-remote-test-
|
|
124
|
-
2. Call `muggle-remote-workflow-start-test-script-generation` with:
|
|
123
|
+
1. Call `muggle-remote-workflow-start-test-script-generation-bulk` with:
|
|
125
124
|
- `projectId` — from Step 2
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
- `goal`, `precondition`, `instructions`, `expectedResult` — straight from the test case. If `precondition` is empty, pass `"None"` (the schema requires a non-empty string).
|
|
131
|
-
3. Capture the returned workflow runtime ID and store it alongside the test case.
|
|
125
|
+
- `name` — `"muggle-test-regenerate-missing: bulk ({count} test cases)"` where `{count}` is the number of selected test cases
|
|
126
|
+
- `testCaseIds` — array of all selected test case IDs from Step 5
|
|
127
|
+
2. The backend handles looking up full test case details (goal, precondition, instructions, expectedResult, url), so there is no need to call `muggle-remote-test-case-get` per test case.
|
|
128
|
+
3. Parse the response to get the `items` array with per-test-case status. Each item contains the test case ID, dispatch status, and (when successful) the workflow runtime ID.
|
|
132
129
|
|
|
133
|
-
**Failure handling:**
|
|
134
|
-
|
|
135
|
-
**Pacing:** Muggle's cloud handles parallelism on its side, so you don't need to throttle. Just dispatch sequentially as fast as the API will accept them.
|
|
130
|
+
**Failure handling:** the bulk API returns per-item status in the response `items` array. Individual test cases may fail (validation error, missing field, etc.) while others succeed. Surface failures in the Step 7 report — partial progress beats no progress.
|
|
136
131
|
|
|
137
132
|
### Step 7 — Report
|
|
138
133
|
|
|
139
|
-
After
|
|
134
|
+
After the bulk dispatch returns, build a summary table from the response `items` array. Each item contains a test case ID, dispatch status, and (when successful) a workflow runtime ID. Cross-reference with the test case list from Step 3 to fill in titles and use case names:
|
|
140
135
|
|
|
141
136
|
```
|
|
142
137
|
Test Case Use Case Prev Status Dispatch Runtime
|
|
@@ -149,7 +144,7 @@ Apply expired coupon Checkout Flow GENERATION_PEND. ❌ fa
|
|
|
149
144
|
Total: 17 dispatched | 16 started | 1 failed
|
|
150
145
|
```
|
|
151
146
|
|
|
152
|
-
For failures: include a one-line error excerpt and (where possible) a hint at the cause (e.g., "missing instructions field — edit the test case in the dashboard, then re-run this skill").
|
|
147
|
+
For failures: include a one-line error excerpt from the item's error field and (where possible) a hint at the cause (e.g., "missing instructions field — edit the test case in the dashboard, then re-run this skill").
|
|
153
148
|
|
|
154
149
|
### Step 8 — Open the Dashboard
|
|
155
150
|
|
|
@@ -183,8 +178,7 @@ Add item to cart rt-ghi789 COMPLETED 12
|
|
|
183
178
|
| Auth | `muggle-remote-auth-status`, `muggle-remote-auth-login`, `muggle-remote-auth-poll` |
|
|
184
179
|
| Project | `muggle-remote-project-list`, `muggle-remote-project-create` |
|
|
185
180
|
| Scan | `muggle-remote-test-case-list` (paginated) |
|
|
186
|
-
|
|
|
187
|
-
| Dispatch | `muggle-remote-workflow-start-test-script-generation` |
|
|
181
|
+
| Dispatch | `muggle-remote-workflow-start-test-script-generation-bulk` |
|
|
188
182
|
| Status (optional) | `muggle-remote-wf-get-ts-gen-latest-run`, `muggle-remote-wf-get-latest-ts-gen-by-tc` |
|
|
189
183
|
| Browser | `open` (shell command) |
|
|
190
184
|
|
|
@@ -193,9 +187,8 @@ Add item to cart rt-ghi789 COMPLETED 12
|
|
|
193
187
|
- **The user MUST select the project** — present projects via `AskQuestion`, never infer from cwd, repo name, or URL guesses.
|
|
194
188
|
- **The user MUST approve which test cases to regenerate** — show the candidates via `AskQuestion`, let them deselect, then confirm again before any dispatch. Bulk-regenerating without approval can waste meaningful workflow budget.
|
|
195
189
|
- **Default filter is `DRAFT` + `GENERATION_PENDING`** — never include `GENERATING`, `ACTIVE`, `DEPRECATED`, `ARCHIVED`, `REPLAYING`, or `REPLAY_PENDING` unless the user explicitly says so. `GENERATING` already has a workflow in flight and dispatching another races against it. `ACTIVE` test cases already have working scripts. The rest reflect deliberate user decisions or in-flight replays the skill should not interfere with.
|
|
196
|
-
- **Use `muggle-remote-test-
|
|
197
|
-
- **Failures don't abort the batch** —
|
|
198
|
-
- **Never throttle artificially** — dispatch sequentially as fast as the API accepts. Muggle's cloud handles parallelism.
|
|
190
|
+
- **Use the bulk endpoint for dispatch** — call `muggle-remote-workflow-start-test-script-generation-bulk` once with all selected test case IDs rather than dispatching one-by-one. The backend resolves full test case details internally.
|
|
191
|
+
- **Failures don't abort the batch** — the bulk API returns per-item status. Surface failures in the report. Partial progress beats no progress.
|
|
199
192
|
- **Open the dashboard, don't poll by default** — the runs page is the canonical view of progress. Only poll if the user explicitly asks.
|
|
200
193
|
- **Use `AskQuestion` for every selection** — never ask the user to type a number.
|
|
201
194
|
- **Can be invoked at any state** — if the user already has a project chosen in conversation context, skip Step 2 and go straight to scanning.
|
|
@@ -10,7 +10,7 @@ Update all Muggle AI components to the latest published version.
|
|
|
10
10
|
## Steps
|
|
11
11
|
|
|
12
12
|
1. Run `/muggle:muggle-status` checks to capture current versions.
|
|
13
|
-
2. Run `muggle
|
|
13
|
+
2. Run `muggle upgrade` to check GitHub releases for the latest electron-app version and download it.
|
|
14
14
|
3. Report the upgrade results:
|
|
15
15
|
- Previous version vs new version for each component.
|
|
16
16
|
- Whether the upgrade succeeded or failed.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"release": "4.8.
|
|
3
|
-
"buildId": "run-
|
|
4
|
-
"commitSha": "
|
|
5
|
-
"buildTime": "2026-04-
|
|
2
|
+
"release": "4.8.1",
|
|
3
|
+
"buildId": "run-20-1",
|
|
4
|
+
"commitSha": "8cd42b4b70c049e44510010003084227b04229a8",
|
|
5
|
+
"buildTime": "2026-04-14T21:44:51Z",
|
|
6
6
|
"serviceName": "muggle-ai-works-mcp"
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports as e2e, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, loadApiKeyData, loadCredentials, local_exports as localQa, mcp_exports as mcp, openBrowserUrl, performLogin, performLogout, pollDeviceCode, e2e_exports as qa, resetConfig, resetLogger, saveApiKey, saveApiKeyData, saveCredentials, startDeviceCodeFlow, toolRequiresAuth, verifyFileChecksum } from './chunk-
|
|
1
|
+
export { buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports as e2e, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, loadApiKeyData, loadCredentials, local_exports as localQa, mcp_exports as mcp, openBrowserUrl, performLogin, performLogout, pollDeviceCode, e2e_exports as qa, resetConfig, resetLogger, saveApiKey, saveApiKeyData, saveCredentials, startDeviceCodeFlow, toolRequiresAuth, verifyFileChecksum } from './chunk-44I5ROCB.js';
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@muggleai/works",
|
|
3
3
|
"mcpName": "io.github.multiplex-ai/muggle",
|
|
4
|
-
"version": "4.8.
|
|
4
|
+
"version": "4.8.1",
|
|
5
5
|
"description": "Ship quality products with AI-powered E2E acceptance testing that validates your web app like a real user — from Claude Code and Cursor to PR.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "dist/index.js",
|
|
@@ -41,14 +41,14 @@
|
|
|
41
41
|
"test:watch": "vitest"
|
|
42
42
|
},
|
|
43
43
|
"muggleConfig": {
|
|
44
|
-
"electronAppVersion": "1.0.
|
|
44
|
+
"electronAppVersion": "1.0.59",
|
|
45
45
|
"downloadBaseUrl": "https://github.com/multiplex-ai/muggle-ai-works/releases/download",
|
|
46
46
|
"runtimeTargetDefault": "production",
|
|
47
47
|
"checksums": {
|
|
48
|
-
"darwin-arm64": "
|
|
49
|
-
"darwin-x64": "
|
|
50
|
-
"win32-x64": "
|
|
51
|
-
"linux-x64": "
|
|
48
|
+
"darwin-arm64": "c6da3f7f6b6875174a70a6c065554ed051ff99f731470e161ab71d9a9e568a87",
|
|
49
|
+
"darwin-x64": "ff93b24724fd415b99c40bcce2cc90a31e453a6408aad45a63480ff92606e7b0",
|
|
50
|
+
"win32-x64": "59bc67ea0a067fb4a204b87c73bf8cc387e705307f77ec96202822ff14b587fa",
|
|
51
|
+
"linux-x64": "714c93f586ac423377d9061924d2f703c175e3b541ef6ad22c96f6c20d3f4ea0"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
@@ -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
|
+
"version": "4.8.1",
|
|
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.
|
|
5
|
+
"version": "4.8.1",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/plugin/README.md
CHANGED
|
@@ -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
|
|
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
|
|
|
@@ -200,6 +200,17 @@ If nothing detected, ask as free text: "Your local app should be running. What's
|
|
|
200
200
|
|
|
201
201
|
Before execution, fetch full test case details for all selected test cases by issuing **all** `muggle-remote-test-case-get` calls in parallel (single message, multiple tool calls).
|
|
202
202
|
|
|
203
|
+
### Determine `freshSession` per test case
|
|
204
|
+
|
|
205
|
+
Before executing each test case, inspect its content (title, goal, instructions, preconditions) for signals that it requires a **clean browser state** — no prior cookies, localStorage, or logged-in session. Set `freshSession: true` when the test case involves any of:
|
|
206
|
+
|
|
207
|
+
- **Registration / sign-up** — creating a new account
|
|
208
|
+
- **Login / authentication** — verifying the login flow itself (not a test that merely *uses* login as a prerequisite)
|
|
209
|
+
- **Cookie consent / GDPR banners** — verifying first-visit consent prompts
|
|
210
|
+
- **Onboarding flows** — first-time user experiences that only appear on a fresh session
|
|
211
|
+
|
|
212
|
+
If none of the above apply, omit `freshSession` (defaults to `false`, preserving any existing session state). Evaluate this per test case — in a batch, some may need it and others may not.
|
|
213
|
+
|
|
203
214
|
### Run sequentially (Electron constraint)
|
|
204
215
|
|
|
205
216
|
Execution itself **must** be sequential because there is only one local Electron browser. For each test case, in order:
|
|
@@ -208,6 +219,7 @@ Execution itself **must** be sequential because there is only one local Electron
|
|
|
208
219
|
- `testCase`: Full test case object from the parallel fetch above
|
|
209
220
|
- `localUrl`: User's local URL from the pre-flight question
|
|
210
221
|
- `showUi`: omit (default visible) unless the user explicitly asked for headless, then pass `false`
|
|
222
|
+
- `freshSession`: `true` if the test case requires a clean browser state (see above), omit otherwise
|
|
211
223
|
2. Store the returned `runId`
|
|
212
224
|
|
|
213
225
|
If a generation fails, log it and continue to the next. Do not abort the batch.
|
|
@@ -231,7 +243,7 @@ Store every `viewUrl` — these are used in the next steps.
|
|
|
231
243
|
### Report summary
|
|
232
244
|
|
|
233
245
|
```
|
|
234
|
-
Test Case Status Duration Steps View on Muggle
|
|
246
|
+
Test Case Status Duration Steps View Steps on Muggle AI
|
|
235
247
|
─────────────────────────────────────────────────────────────────────────
|
|
236
248
|
Login with valid creds PASSED 12.3s 8 https://www.muggle-ai.com/...
|
|
237
249
|
Login with invalid creds PASSED 9.1s 6 https://www.muggle-ai.com/...
|
|
@@ -86,17 +86,28 @@ Remind them: local URL is only the execution target, not tied to cloud project c
|
|
|
86
86
|
|
|
87
87
|
### 5. Load data for the chosen path
|
|
88
88
|
|
|
89
|
+
**Determine `freshSession`**
|
|
90
|
+
|
|
91
|
+
Before calling either execution tool, inspect the test case content (title, goal, instructions, preconditions) for signals that the test requires a **clean browser state** — no prior cookies, localStorage, or logged-in session. Pass `freshSession: true` when the test case involves any of:
|
|
92
|
+
|
|
93
|
+
- **Registration / sign-up** — creating a new account
|
|
94
|
+
- **Login / authentication** — verifying the login flow itself (not a test that merely *uses* login as a prerequisite)
|
|
95
|
+
- **Cookie consent / GDPR banners** — verifying first-visit consent prompts
|
|
96
|
+
- **Onboarding flows** — first-time user experiences that only appear on a fresh session
|
|
97
|
+
|
|
98
|
+
If none of the above apply, omit `freshSession` (defaults to `false`, preserving any existing session state).
|
|
99
|
+
|
|
89
100
|
**Generate**
|
|
90
101
|
|
|
91
102
|
1. `muggle-remote-test-case-get`
|
|
92
|
-
2. `muggle-local-execute-test-generation` with that test case + `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`timeoutMs`** — see below)
|
|
103
|
+
2. `muggle-local-execute-test-generation` with that test case + `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`freshSession`** — see above; **`timeoutMs`** — see below)
|
|
93
104
|
|
|
94
105
|
**Replay**
|
|
95
106
|
|
|
96
107
|
1. `muggle-remote-test-script-get` — note `actionScriptId`
|
|
97
108
|
2. `muggle-remote-action-script-get` with that id — full `actionScript`
|
|
98
109
|
**Use the API response as-is.** Do not edit, shorten, or rebuild `actionScript`; replay needs full `label` paths for element lookup.
|
|
99
|
-
3. `muggle-local-execute-replay` with `testScript`, `actionScript`, `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`timeoutMs`** — see below)
|
|
110
|
+
3. `muggle-local-execute-replay` with `testScript`, `actionScript`, `localUrl` (optional: `showUi: false` for headless — defaults to visible; **`freshSession`** — see above; **`timeoutMs`** — see below)
|
|
100
111
|
|
|
101
112
|
### Local execution timeout (`timeoutMs`)
|
|
102
113
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: muggle-test-regenerate-missing
|
|
3
|
-
description: "Bulk-regenerate test scripts for every test case in a Muggle AI project that doesn't currently have an active script. Scans the project, finds test cases stuck in DRAFT or GENERATION_PENDING (no usable script attached), shows the user the list, and on approval kicks off remote test script generation
|
|
3
|
+
description: "Bulk-regenerate test scripts for every test case in a Muggle AI project that doesn't currently have an active script. Scans the project, finds test cases stuck in DRAFT or GENERATION_PENDING (no usable script attached), shows the user the list, and on approval kicks off bulk remote test script generation via the Muggle cloud. Use this skill whenever the user asks to 'regenerate missing scripts', 'fill in missing test scripts', 'generate scripts for test cases without one', 'regen all the test cases that don't have scripts', 'rebuild scripts for stale test cases', 'fix test cases with no script', 'bulk regenerate', or any phrasing that means 'kick off script generation across a project for the cases that need it'. Triggers on: 'regenerate missing test scripts', 'generate scripts for all empty test cases', 'fill the gaps in my test scripts', 'bulk test script regen', 'all my test cases without active scripts'. This is the go-to skill for project-wide script catch-up — it handles discovery, filtering, confirmation, and remote workflow dispatch end-to-end."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Muggle Test — Regenerate Missing Test Scripts
|
|
@@ -116,27 +116,22 @@ After selection, call `AskQuestion` once more for a final confirmation:
|
|
|
116
116
|
|
|
117
117
|
Only proceed after the user picks "Yes".
|
|
118
118
|
|
|
119
|
-
### Step 6 — Dispatch Remote Generations
|
|
119
|
+
### Step 6 — Dispatch Remote Generations (Bulk)
|
|
120
120
|
|
|
121
|
-
|
|
121
|
+
Send a single bulk request instead of dispatching one workflow per test case:
|
|
122
122
|
|
|
123
|
-
1. Call `muggle-remote-test-
|
|
124
|
-
2. Call `muggle-remote-workflow-start-test-script-generation` with:
|
|
123
|
+
1. Call `muggle-remote-workflow-start-test-script-generation-bulk` with:
|
|
125
124
|
- `projectId` — from Step 2
|
|
126
|
-
- `
|
|
127
|
-
- `
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
- `goal`, `precondition`, `instructions`, `expectedResult` — straight from the test case. If `precondition` is empty, pass `"None"` (the schema requires a non-empty string).
|
|
131
|
-
3. Capture the returned workflow runtime ID and store it alongside the test case.
|
|
125
|
+
- `name` — `"muggle-test-regenerate-missing: bulk ({count} test cases)"` where `{count}` is the number of selected test cases
|
|
126
|
+
- `testCaseIds` — array of all selected test case IDs from Step 5
|
|
127
|
+
2. The backend handles looking up full test case details (goal, precondition, instructions, expectedResult, url), so there is no need to call `muggle-remote-test-case-get` per test case.
|
|
128
|
+
3. Parse the response to get the `items` array with per-test-case status. Each item contains the test case ID, dispatch status, and (when successful) the workflow runtime ID.
|
|
132
129
|
|
|
133
|
-
**Failure handling:**
|
|
134
|
-
|
|
135
|
-
**Pacing:** Muggle's cloud handles parallelism on its side, so you don't need to throttle. Just dispatch sequentially as fast as the API will accept them.
|
|
130
|
+
**Failure handling:** the bulk API returns per-item status in the response `items` array. Individual test cases may fail (validation error, missing field, etc.) while others succeed. Surface failures in the Step 7 report — partial progress beats no progress.
|
|
136
131
|
|
|
137
132
|
### Step 7 — Report
|
|
138
133
|
|
|
139
|
-
After
|
|
134
|
+
After the bulk dispatch returns, build a summary table from the response `items` array. Each item contains a test case ID, dispatch status, and (when successful) a workflow runtime ID. Cross-reference with the test case list from Step 3 to fill in titles and use case names:
|
|
140
135
|
|
|
141
136
|
```
|
|
142
137
|
Test Case Use Case Prev Status Dispatch Runtime
|
|
@@ -149,7 +144,7 @@ Apply expired coupon Checkout Flow GENERATION_PEND. ❌ fa
|
|
|
149
144
|
Total: 17 dispatched | 16 started | 1 failed
|
|
150
145
|
```
|
|
151
146
|
|
|
152
|
-
For failures: include a one-line error excerpt and (where possible) a hint at the cause (e.g., "missing instructions field — edit the test case in the dashboard, then re-run this skill").
|
|
147
|
+
For failures: include a one-line error excerpt from the item's error field and (where possible) a hint at the cause (e.g., "missing instructions field — edit the test case in the dashboard, then re-run this skill").
|
|
153
148
|
|
|
154
149
|
### Step 8 — Open the Dashboard
|
|
155
150
|
|
|
@@ -183,8 +178,7 @@ Add item to cart rt-ghi789 COMPLETED 12
|
|
|
183
178
|
| Auth | `muggle-remote-auth-status`, `muggle-remote-auth-login`, `muggle-remote-auth-poll` |
|
|
184
179
|
| Project | `muggle-remote-project-list`, `muggle-remote-project-create` |
|
|
185
180
|
| Scan | `muggle-remote-test-case-list` (paginated) |
|
|
186
|
-
|
|
|
187
|
-
| Dispatch | `muggle-remote-workflow-start-test-script-generation` |
|
|
181
|
+
| Dispatch | `muggle-remote-workflow-start-test-script-generation-bulk` |
|
|
188
182
|
| Status (optional) | `muggle-remote-wf-get-ts-gen-latest-run`, `muggle-remote-wf-get-latest-ts-gen-by-tc` |
|
|
189
183
|
| Browser | `open` (shell command) |
|
|
190
184
|
|
|
@@ -193,9 +187,8 @@ Add item to cart rt-ghi789 COMPLETED 12
|
|
|
193
187
|
- **The user MUST select the project** — present projects via `AskQuestion`, never infer from cwd, repo name, or URL guesses.
|
|
194
188
|
- **The user MUST approve which test cases to regenerate** — show the candidates via `AskQuestion`, let them deselect, then confirm again before any dispatch. Bulk-regenerating without approval can waste meaningful workflow budget.
|
|
195
189
|
- **Default filter is `DRAFT` + `GENERATION_PENDING`** — never include `GENERATING`, `ACTIVE`, `DEPRECATED`, `ARCHIVED`, `REPLAYING`, or `REPLAY_PENDING` unless the user explicitly says so. `GENERATING` already has a workflow in flight and dispatching another races against it. `ACTIVE` test cases already have working scripts. The rest reflect deliberate user decisions or in-flight replays the skill should not interfere with.
|
|
196
|
-
- **Use `muggle-remote-test-
|
|
197
|
-
- **Failures don't abort the batch** —
|
|
198
|
-
- **Never throttle artificially** — dispatch sequentially as fast as the API accepts. Muggle's cloud handles parallelism.
|
|
190
|
+
- **Use the bulk endpoint for dispatch** — call `muggle-remote-workflow-start-test-script-generation-bulk` once with all selected test case IDs rather than dispatching one-by-one. The backend resolves full test case details internally.
|
|
191
|
+
- **Failures don't abort the batch** — the bulk API returns per-item status. Surface failures in the report. Partial progress beats no progress.
|
|
199
192
|
- **Open the dashboard, don't poll by default** — the runs page is the canonical view of progress. Only poll if the user explicitly asks.
|
|
200
193
|
- **Use `AskQuestion` for every selection** — never ask the user to type a number.
|
|
201
194
|
- **Can be invoked at any state** — if the user already has a project chosen in conversation context, skip Step 2 and go straight to scanning.
|
|
@@ -10,7 +10,7 @@ Update all Muggle AI components to the latest published version.
|
|
|
10
10
|
## Steps
|
|
11
11
|
|
|
12
12
|
1. Run `/muggle:muggle-status` checks to capture current versions.
|
|
13
|
-
2. Run `muggle
|
|
13
|
+
2. Run `muggle upgrade` to check GitHub releases for the latest electron-app version and download it.
|
|
14
14
|
3. Report the upgrade results:
|
|
15
15
|
- Previous version vs new version for each component.
|
|
16
16
|
- Whether the upgrade succeeded or failed.
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -754,8 +754,53 @@ async function extractTarGz(tarPath, destDir) {
|
|
|
754
754
|
});
|
|
755
755
|
}
|
|
756
756
|
|
|
757
|
+
/**
|
|
758
|
+
* Upsert the muggle MCP server entry into ~/.cursor/mcp.json.
|
|
759
|
+
* Reads the existing config, merges in the muggle server, and writes back.
|
|
760
|
+
* Preserves any other MCP servers the user has configured.
|
|
761
|
+
*/
|
|
762
|
+
function upsertCursorMcpConfig() {
|
|
763
|
+
const cursorMcpConfigPath = join(homedir(), ".cursor", "mcp.json");
|
|
764
|
+
const cursorDir = join(homedir(), ".cursor");
|
|
765
|
+
|
|
766
|
+
/** @type {{ mcpServers?: Record<string, unknown> }} */
|
|
767
|
+
let config = {};
|
|
768
|
+
|
|
769
|
+
if (existsSync(cursorMcpConfigPath)) {
|
|
770
|
+
try {
|
|
771
|
+
const raw = readFileSync(cursorMcpConfigPath, "utf-8");
|
|
772
|
+
const parsed = JSON.parse(raw);
|
|
773
|
+
|
|
774
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
775
|
+
log(`Warning: ~/.cursor/mcp.json has unexpected shape, skipping MCP config upsert.`);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
config = parsed;
|
|
780
|
+
} catch (error) {
|
|
781
|
+
log(`Warning: ~/.cursor/mcp.json is invalid JSON, skipping MCP config upsert.`);
|
|
782
|
+
log(` Parse error: ${error.message}`);
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (!config.mcpServers) {
|
|
788
|
+
config.mcpServers = {};
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
config.mcpServers.muggle = {
|
|
792
|
+
command: "muggle",
|
|
793
|
+
args: ["serve"],
|
|
794
|
+
};
|
|
795
|
+
|
|
796
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
797
|
+
writeFileSync(cursorMcpConfigPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
798
|
+
log(`Cursor MCP config updated at ${cursorMcpConfigPath}`);
|
|
799
|
+
}
|
|
800
|
+
|
|
757
801
|
// Run postinstall
|
|
758
802
|
initLogFile();
|
|
759
803
|
removeVersionOverrideFile();
|
|
760
804
|
syncCursorSkills();
|
|
805
|
+
upsertCursorMcpConfig();
|
|
761
806
|
downloadElectronApp().catch(logError);
|