@muggleai/works 4.4.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -13
- package/dist/{chunk-PMI2DI3V.js → chunk-MNCBJEPQ.js} +311 -35
- package/dist/cli.js +1 -1
- package/dist/index.js +1 -1
- package/dist/plugin/.claude-plugin/plugin.json +1 -1
- package/dist/plugin/.cursor-plugin/plugin.json +1 -1
- package/dist/plugin/README.md +1 -0
- package/dist/plugin/skills/do/open-prs.md +35 -74
- package/dist/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +181 -0
- package/dist/plugin/skills/muggle-test/SKILL.md +44 -48
- package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +51 -1
- package/dist/plugin/skills/muggle-test-import/SKILL.md +122 -23
- package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +196 -0
- package/dist/plugin/skills/muggle-test-regenerate-missing/evals/evals.json +58 -0
- package/dist/plugin/skills/muggle-test-regenerate-missing/evals/trigger-eval.json +22 -0
- package/dist/release-manifest.json +7 -0
- package/package.json +7 -7
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.cursor-plugin/plugin.json +1 -1
- package/plugin/README.md +1 -0
- package/plugin/skills/do/open-prs.md +35 -74
- package/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +181 -0
- package/plugin/skills/muggle-test/SKILL.md +44 -48
- package/plugin/skills/muggle-test-feature-local/SKILL.md +51 -1
- package/plugin/skills/muggle-test-import/SKILL.md +122 -23
- package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +196 -0
- package/plugin/skills/muggle-test-regenerate-missing/evals/evals.json +58 -0
- package/plugin/skills/muggle-test-regenerate-missing/evals/trigger-eval.json +22 -0
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@ This installs:
|
|
|
42
42
|
- `/muggle:muggle-do` — autonomous dev pipeline (requirements to PR)
|
|
43
43
|
- `/muggle:muggle-test` — change-driven E2E acceptance testing (local or remote, with PR posting)
|
|
44
44
|
- `/muggle:muggle-test-feature-local` — local quick E2E acceptance testing
|
|
45
|
+
- `/muggle:muggle-test-regenerate-missing` — bulk-regenerate test scripts for every test case that has no active script
|
|
45
46
|
- `/muggle:muggle-status` — health check for muggle-works plugins (Electron app, MCP server, and auth)
|
|
46
47
|
- `/muggle:muggle-repair` — diagnose and fix broken installation
|
|
47
48
|
- `/muggle:muggle-upgrade` — update to the latest version
|
|
@@ -246,24 +247,40 @@ Project Management (muggle-remote-project-*)
|
|
|
246
247
|
Use Cases (muggle-remote-use-case-*)
|
|
247
248
|
|
|
248
249
|
|
|
249
|
-
| Tool
|
|
250
|
-
|
|
|
251
|
-
| `muggle-remote-use-case-list`
|
|
252
|
-
| `muggle-remote-use-case-create
|
|
253
|
-
| `muggle-remote-use-case-
|
|
254
|
-
| `muggle-remote-use-case-
|
|
250
|
+
| Tool | Purpose |
|
|
251
|
+
| ---------------------------------------------- | -------------------------------------------------------- |
|
|
252
|
+
| `muggle-remote-use-case-list` | List use cases |
|
|
253
|
+
| `muggle-remote-use-case-create` | Persist a fully-specified use case (no LLM) |
|
|
254
|
+
| `muggle-remote-use-case-create-from-prompts` | Create from natural language |
|
|
255
|
+
| `muggle-remote-use-case-prompt-preview` | Preview before creating |
|
|
256
|
+
| `muggle-remote-use-case-update-from-prompt` | Regenerate from new prompt |
|
|
257
|
+
| `muggle-remote-use-case-bulk-preview-submit` | Async batch-preview via OpenAI Batch API (~50% cheaper) |
|
|
255
258
|
|
|
256
259
|
|
|
257
260
|
Test Cases (muggle-remote-test-case-*)
|
|
258
261
|
|
|
259
262
|
|
|
260
|
-
| Tool | Purpose
|
|
261
|
-
| ---------------------------------------------- |
|
|
262
|
-
| `muggle-remote-test-case-list` | List all test cases
|
|
263
|
-
| `muggle-remote-test-case-list-by-use-case` | List by use case
|
|
264
|
-
| `muggle-remote-test-case-get` | Get test case details
|
|
265
|
-
| `muggle-remote-test-case-create` | Create test case
|
|
266
|
-
| `muggle-remote-test-case-generate-from-prompt` | Generate from prompt
|
|
263
|
+
| Tool | Purpose |
|
|
264
|
+
| ---------------------------------------------- | -------------------------------------------------------- |
|
|
265
|
+
| `muggle-remote-test-case-list` | List all test cases |
|
|
266
|
+
| `muggle-remote-test-case-list-by-use-case` | List by use case |
|
|
267
|
+
| `muggle-remote-test-case-get` | Get test case details |
|
|
268
|
+
| `muggle-remote-test-case-create` | Create test case |
|
|
269
|
+
| `muggle-remote-test-case-generate-from-prompt` | Generate from prompt |
|
|
270
|
+
| `muggle-remote-test-case-bulk-preview-submit` | Async batch-preview via OpenAI Batch API (~50% cheaper) |
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
Bulk Preview Jobs (muggle-remote-bulk-preview-job-*)
|
|
274
|
+
|
|
275
|
+
Manage async jobs started by the `*-bulk-preview-submit` tools above. Submit returns a
|
|
276
|
+
`jobId` immediately; poll `-get` until a terminal status, then persist the results via
|
|
277
|
+
`muggle-remote-use-case-create` / `muggle-remote-test-case-create`.
|
|
278
|
+
|
|
279
|
+
| Tool | Purpose |
|
|
280
|
+
| --------------------------------------- | --------------------------------------- |
|
|
281
|
+
| `muggle-remote-bulk-preview-job-get` | Poll a bulk-preview job for status/results |
|
|
282
|
+
| `muggle-remote-bulk-preview-job-list` | List bulk-preview jobs for a project |
|
|
283
|
+
| `muggle-remote-bulk-preview-job-cancel` | Cooperatively cancel an in-flight job |
|
|
267
284
|
|
|
268
285
|
|
|
269
286
|
Test Scripts and Workflows (muggle-remote-workflow-*)
|
|
@@ -479,6 +496,7 @@ muggle-ai-works/
|
|
|
479
496
|
│ │ ├── muggle/ # /muggle:muggle — command router and menu
|
|
480
497
|
│ │ ├── muggle-do/ # /muggle:muggle-do — autonomous dev pipeline
|
|
481
498
|
│ │ ├── muggle-test-feature-local/ # /muggle:muggle-test-feature-local
|
|
499
|
+
│ │ ├── muggle-test-regenerate-missing/# /muggle:muggle-test-regenerate-missing
|
|
482
500
|
│ │ ├── muggle-status/ # /muggle:muggle-status
|
|
483
501
|
│ │ ├── muggle-repair/ # /muggle:muggle-repair
|
|
484
502
|
│ │ └── muggle-upgrade/ # /muggle:muggle-upgrade
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as fs3 from 'fs';
|
|
2
|
-
import { readFileSync, existsSync,
|
|
2
|
+
import { readFileSync, existsSync, mkdirSync, writeFileSync, rmSync, readdirSync, createWriteStream, statSync } from 'fs';
|
|
3
3
|
import * as os3 from 'os';
|
|
4
4
|
import { platform, arch, homedir } from 'os';
|
|
5
5
|
import * as path2 from 'path';
|
|
6
|
-
import { dirname, resolve
|
|
6
|
+
import { join, dirname, resolve } from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import winston from 'winston';
|
|
9
9
|
import axios, { AxiosError } from 'axios';
|
|
@@ -34,6 +34,49 @@ var DATA_DIR_NAME = ".muggle-ai";
|
|
|
34
34
|
function getDataDir() {
|
|
35
35
|
return path2.join(os3.homedir(), DATA_DIR_NAME);
|
|
36
36
|
}
|
|
37
|
+
var DEV_MANIFEST = {
|
|
38
|
+
release: "dev",
|
|
39
|
+
buildId: "dev",
|
|
40
|
+
commitSha: "dev",
|
|
41
|
+
buildTime: "1970-01-01T00:00:00Z",
|
|
42
|
+
serviceName: "muggle-ai-works-mcp"
|
|
43
|
+
};
|
|
44
|
+
var MANIFEST_PATH = (() => {
|
|
45
|
+
try {
|
|
46
|
+
return join(dirname(fileURLToPath(import.meta.url)), "release-manifest.json");
|
|
47
|
+
} catch {
|
|
48
|
+
return "release-manifest.json";
|
|
49
|
+
}
|
|
50
|
+
})();
|
|
51
|
+
var cachedManifest;
|
|
52
|
+
function isValidManifest(value) {
|
|
53
|
+
if (typeof value !== "object" || value === null) return false;
|
|
54
|
+
const m = value;
|
|
55
|
+
return typeof m.release === "string" && typeof m.buildId === "string" && typeof m.commitSha === "string" && typeof m.buildTime === "string" && typeof m.serviceName === "string";
|
|
56
|
+
}
|
|
57
|
+
function readReleaseManifest() {
|
|
58
|
+
if (cachedManifest !== void 0) return cachedManifest;
|
|
59
|
+
try {
|
|
60
|
+
const raw = readFileSync(MANIFEST_PATH, "utf8");
|
|
61
|
+
const parsed = JSON.parse(raw);
|
|
62
|
+
if (!isValidManifest(parsed)) {
|
|
63
|
+
cachedManifest = DEV_MANIFEST;
|
|
64
|
+
return cachedManifest;
|
|
65
|
+
}
|
|
66
|
+
cachedManifest = parsed;
|
|
67
|
+
return cachedManifest;
|
|
68
|
+
} catch (err) {
|
|
69
|
+
const code = err.code;
|
|
70
|
+
if (code !== "ENOENT") {
|
|
71
|
+
console.warn(
|
|
72
|
+
"release_manifest: unexpected error reading manifest, using DEV fallback",
|
|
73
|
+
{ error: err.message }
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
cachedManifest = DEV_MANIFEST;
|
|
77
|
+
return cachedManifest;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
37
80
|
|
|
38
81
|
// packages/mcps/src/shared/config.ts
|
|
39
82
|
var DEFAULT_PROMPT_SERVICE_PRODUCTION_URL = "https://promptservice.muggle-ai.com";
|
|
@@ -303,9 +346,10 @@ function getConfig() {
|
|
|
303
346
|
if (configInstance) {
|
|
304
347
|
return configInstance;
|
|
305
348
|
}
|
|
349
|
+
const manifest = readReleaseManifest();
|
|
306
350
|
configInstance = {
|
|
307
351
|
serverName: "muggle",
|
|
308
|
-
serverVersion:
|
|
352
|
+
serverVersion: manifest.release,
|
|
309
353
|
logLevel: process.env.LOG_LEVEL ?? "info",
|
|
310
354
|
auth0: buildAuth0Config(),
|
|
311
355
|
e2e: buildE2eConfig(),
|
|
@@ -425,6 +469,14 @@ __export(e2e_exports2, {
|
|
|
425
469
|
ApiKeyRevokeInputSchema: () => ApiKeyRevokeInputSchema,
|
|
426
470
|
AuthLoginInputSchema: () => AuthLoginInputSchema,
|
|
427
471
|
AuthPollInputSchema: () => AuthPollInputSchema,
|
|
472
|
+
BulkPreviewJobCancelInputSchema: () => BulkPreviewJobCancelInputSchema,
|
|
473
|
+
BulkPreviewJobGetInputSchema: () => BulkPreviewJobGetInputSchema,
|
|
474
|
+
BulkPreviewJobKindSchema: () => BulkPreviewJobKindSchema,
|
|
475
|
+
BulkPreviewJobListInputSchema: () => BulkPreviewJobListInputSchema,
|
|
476
|
+
BulkPreviewJobStatusSchema: () => BulkPreviewJobStatusSchema,
|
|
477
|
+
BulkPreviewPromptSchema: () => BulkPreviewPromptSchema,
|
|
478
|
+
BulkPreviewSubmitTestCaseInputSchema: () => BulkPreviewSubmitTestCaseInputSchema,
|
|
479
|
+
BulkPreviewSubmitUseCaseInputSchema: () => BulkPreviewSubmitUseCaseInputSchema,
|
|
428
480
|
EmptyInputSchema: () => EmptyInputSchema,
|
|
429
481
|
GatewayError: () => GatewayError,
|
|
430
482
|
IdSchema: () => IdSchema,
|
|
@@ -467,11 +519,11 @@ __export(e2e_exports2, {
|
|
|
467
519
|
TestCaseListInputSchema: () => TestCaseListInputSchema,
|
|
468
520
|
TestScriptGetInputSchema: () => TestScriptGetInputSchema,
|
|
469
521
|
TestScriptListInputSchema: () => TestScriptListInputSchema,
|
|
470
|
-
TestScriptListPaginatedInputSchema: () => TestScriptListPaginatedInputSchema,
|
|
471
522
|
TokenPackageIdSchema: () => TokenPackageIdSchema,
|
|
472
523
|
TokenUsageFilterTypeSchema: () => TokenUsageFilterTypeSchema,
|
|
473
524
|
UseCaseCandidatesApproveInputSchema: () => UseCaseCandidatesApproveInputSchema,
|
|
474
525
|
UseCaseCreateFromPromptsInputSchema: () => UseCaseCreateFromPromptsInputSchema,
|
|
526
|
+
UseCaseCreateInputSchema: () => UseCaseCreateInputSchema,
|
|
475
527
|
UseCaseDiscoveryMemoryGetInputSchema: () => UseCaseDiscoveryMemoryGetInputSchema,
|
|
476
528
|
UseCaseGetInputSchema: () => UseCaseGetInputSchema,
|
|
477
529
|
UseCaseListInputSchema: () => UseCaseListInputSchema,
|
|
@@ -2854,8 +2906,10 @@ var LocalRunUploadInputSchema = z.object({
|
|
|
2854
2906
|
|
|
2855
2907
|
// packages/mcps/src/mcp/e2e/contracts/index.ts
|
|
2856
2908
|
var PaginationInputSchema = z.object({
|
|
2857
|
-
page: z.number().int().positive().
|
|
2858
|
-
pageSize: z.number().int().positive().max(100).
|
|
2909
|
+
page: z.number().int().positive().default(1).describe("Page number, 1-based. Defaults to 1 (the first page)."),
|
|
2910
|
+
pageSize: z.number().int().positive().max(100).default(10).describe("Number of items per page. Defaults to 10, max 100."),
|
|
2911
|
+
sortBy: z.enum(["createdAt", "updatedAt"]).default("createdAt").describe("Field to sort by. Defaults to createdAt (stable under concurrent writes)."),
|
|
2912
|
+
sortOrder: z.enum(["asc", "desc"]).default("desc").describe("Sort direction. Defaults to desc (newest first).")
|
|
2859
2913
|
});
|
|
2860
2914
|
var IdSchema = MuggleEntityIdSchema;
|
|
2861
2915
|
var RunBatchIdSchema = MuggleEntityIdSchema.describe("Bulk replay run batch ID (UUID)");
|
|
@@ -2961,15 +3015,67 @@ var UseCasePromptPreviewInputSchema = z.object({
|
|
|
2961
3015
|
});
|
|
2962
3016
|
var UseCaseCreateFromPromptsInputSchema = z.object({
|
|
2963
3017
|
projectId: IdSchema.describe("Project ID (UUID) to create use cases for"),
|
|
2964
|
-
|
|
2965
|
-
instruction: z.string().min(1).describe("Natural language instruction describing the use case")
|
|
2966
|
-
})).min(1).describe("Array of prompts to generate use cases from")
|
|
3018
|
+
instructions: z.array(z.string().min(1)).min(1).describe('Natural-language instructions \u2014 one use case is generated per string (e.g., ["As a user, I can log in"])')
|
|
2967
3019
|
});
|
|
2968
3020
|
var UseCaseUpdateFromPromptInputSchema = z.object({
|
|
2969
3021
|
projectId: IdSchema.describe("Project ID (UUID)"),
|
|
2970
3022
|
useCaseId: IdSchema.describe("Use case ID (UUID) to update"),
|
|
2971
3023
|
instruction: z.string().min(1).describe("Natural language instruction to regenerate the use case from")
|
|
2972
3024
|
});
|
|
3025
|
+
var UseCaseCreateInputSchema = z.object({
|
|
3026
|
+
projectId: IdSchema.describe("Project ID (UUID) the use case belongs to"),
|
|
3027
|
+
title: z.string().min(1).describe("Use case title"),
|
|
3028
|
+
description: z.string().min(1).describe("Description of the use case, including actor and preconditions"),
|
|
3029
|
+
userStory: z.string().min(1).describe("One-line user story from the end-user point of view"),
|
|
3030
|
+
url: z.string().url().optional().describe("URL where the use case takes place (defaults to project URL)"),
|
|
3031
|
+
useCaseBreakdown: z.array(z.object({
|
|
3032
|
+
requirement: z.string().min(1).describe("One requirement of the use case"),
|
|
3033
|
+
acceptanceCriteria: z.string().min(1).describe("Concrete, measurable acceptance criteria for the requirement")
|
|
3034
|
+
})).describe("Main/alternative/error flows broken down as requirement + acceptance criteria pairs"),
|
|
3035
|
+
status: z.enum(["DRAFT", "IN_REVIEW", "APPROVED", "IMPLEMENTED", "ARCHIVED"]).describe("Use case status"),
|
|
3036
|
+
priority: z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]).describe("Use case priority"),
|
|
3037
|
+
source: z.enum(["PRD_FILE", "SITEMAP", "CRAWLER", "PROMPT", "MANUAL"]).describe("How this use case was produced"),
|
|
3038
|
+
category: z.string().optional().describe("Optional category")
|
|
3039
|
+
});
|
|
3040
|
+
var BulkPreviewPromptSchema = z.object({
|
|
3041
|
+
clientRef: z.string().max(128).optional().describe("Optional caller-supplied reference echoed back on results"),
|
|
3042
|
+
instruction: z.string().min(1).max(4e3).describe("Natural language instruction (max 4000 chars)")
|
|
3043
|
+
});
|
|
3044
|
+
var BulkPreviewSubmitUseCaseInputSchema = z.object({
|
|
3045
|
+
projectId: IdSchema.describe("Project ID (UUID) the use cases belong to"),
|
|
3046
|
+
prompts: z.array(BulkPreviewPromptSchema).min(1).max(100).describe("Prompts to generate use cases from (max 100 per request)")
|
|
3047
|
+
});
|
|
3048
|
+
var BulkPreviewSubmitTestCaseInputSchema = z.object({
|
|
3049
|
+
projectId: IdSchema.describe("Project ID (UUID)"),
|
|
3050
|
+
useCaseId: IdSchema.describe("Parent use case ID (UUID) the test cases will belong to"),
|
|
3051
|
+
prompts: z.array(BulkPreviewPromptSchema).min(1).max(100).describe("Prompts to generate test cases from (max 100 per request)")
|
|
3052
|
+
});
|
|
3053
|
+
var BulkPreviewJobStatusSchema = z.enum([
|
|
3054
|
+
"queued",
|
|
3055
|
+
"submitted",
|
|
3056
|
+
"running",
|
|
3057
|
+
"succeeded",
|
|
3058
|
+
"partial",
|
|
3059
|
+
"failed",
|
|
3060
|
+
"cancelled",
|
|
3061
|
+
"expired"
|
|
3062
|
+
]);
|
|
3063
|
+
var BulkPreviewJobKindSchema = z.enum(["useCase", "testCase"]);
|
|
3064
|
+
var BulkPreviewJobGetInputSchema = z.object({
|
|
3065
|
+
projectId: IdSchema.describe("Project ID (UUID)"),
|
|
3066
|
+
jobId: IdSchema.describe("Bulk-preview job ID (UUID)")
|
|
3067
|
+
});
|
|
3068
|
+
var BulkPreviewJobCancelInputSchema = z.object({
|
|
3069
|
+
projectId: IdSchema.describe("Project ID (UUID)"),
|
|
3070
|
+
jobId: IdSchema.describe("Bulk-preview job ID (UUID) to cancel")
|
|
3071
|
+
});
|
|
3072
|
+
var BulkPreviewJobListInputSchema = z.object({
|
|
3073
|
+
projectId: IdSchema.describe("Project ID (UUID) to list bulk-preview jobs for"),
|
|
3074
|
+
status: z.array(BulkPreviewJobStatusSchema).optional().describe("Optional filter \u2014 only return jobs matching any of these statuses"),
|
|
3075
|
+
kind: BulkPreviewJobKindSchema.optional().describe("Optional filter \u2014 only return jobs of this kind"),
|
|
3076
|
+
limit: z.number().int().min(1).max(100).optional().describe("Max jobs to return (default 20, max 100)"),
|
|
3077
|
+
cursor: z.string().optional().describe("Pagination cursor returned by a previous call")
|
|
3078
|
+
});
|
|
2973
3079
|
var TestCaseListInputSchema = z.object({
|
|
2974
3080
|
projectId: IdSchema.describe("Project ID (UUID) to list test cases for")
|
|
2975
3081
|
}).merge(PaginationInputSchema);
|
|
@@ -3006,9 +3112,6 @@ var TestScriptListInputSchema = z.object({
|
|
|
3006
3112
|
var TestScriptGetInputSchema = z.object({
|
|
3007
3113
|
testScriptId: IdSchema.describe("Test script ID (UUID) to retrieve")
|
|
3008
3114
|
});
|
|
3009
|
-
var TestScriptListPaginatedInputSchema = z.object({
|
|
3010
|
-
projectId: IdSchema.describe("Project ID (UUID) to list test scripts for")
|
|
3011
|
-
}).merge(PaginationInputSchema);
|
|
3012
3115
|
var ActionScriptGetInputSchema = z.object({
|
|
3013
3116
|
actionScriptId: IdSchema.describe("Action script ID (UUID) to retrieve")
|
|
3014
3117
|
});
|
|
@@ -3180,6 +3283,61 @@ var GatewayError = class extends Error {
|
|
|
3180
3283
|
this.details = params.details;
|
|
3181
3284
|
}
|
|
3182
3285
|
};
|
|
3286
|
+
|
|
3287
|
+
// packages/mcps/src/shared/host_detection.ts
|
|
3288
|
+
function detectMcpHost(env = process.env) {
|
|
3289
|
+
if (env.CLAUDE_CODE || env.CLAUDECODE || env.CLAUDE_CODE_SSE_PORT) {
|
|
3290
|
+
return "claude-code";
|
|
3291
|
+
}
|
|
3292
|
+
if (env.CURSOR_TRACE_ID || env.CURSOR_MCP) {
|
|
3293
|
+
return "cursor";
|
|
3294
|
+
}
|
|
3295
|
+
if (env.CODEX || env.OPENAI_CODEX) {
|
|
3296
|
+
return "codex";
|
|
3297
|
+
}
|
|
3298
|
+
if (env.WINDSURF_MCP || env.WINDSURF || env.CODEIUM_API_KEY) {
|
|
3299
|
+
return "windsurf";
|
|
3300
|
+
}
|
|
3301
|
+
return "unknown";
|
|
3302
|
+
}
|
|
3303
|
+
var INSTALL_ID_FILENAME = "install-id.json";
|
|
3304
|
+
var cachedInstallId;
|
|
3305
|
+
function installIdFilePath() {
|
|
3306
|
+
return join(getDataDir(), INSTALL_ID_FILENAME);
|
|
3307
|
+
}
|
|
3308
|
+
function generateAndPersist(filePath) {
|
|
3309
|
+
const installId = randomUUID();
|
|
3310
|
+
const contents = { installId };
|
|
3311
|
+
mkdirSync(dirname(filePath), { recursive: true });
|
|
3312
|
+
writeFileSync(filePath, JSON.stringify(contents, null, 2) + "\n", "utf8");
|
|
3313
|
+
return installId;
|
|
3314
|
+
}
|
|
3315
|
+
function isValid(value) {
|
|
3316
|
+
return typeof value === "object" && value !== null && typeof value.installId === "string" && value.installId.length > 0;
|
|
3317
|
+
}
|
|
3318
|
+
function getInstallId() {
|
|
3319
|
+
if (cachedInstallId !== void 0) return cachedInstallId;
|
|
3320
|
+
const filePath = installIdFilePath();
|
|
3321
|
+
if (!existsSync(filePath)) {
|
|
3322
|
+
cachedInstallId = generateAndPersist(filePath);
|
|
3323
|
+
return cachedInstallId;
|
|
3324
|
+
}
|
|
3325
|
+
try {
|
|
3326
|
+
const raw = readFileSync(filePath, "utf8");
|
|
3327
|
+
const parsed = JSON.parse(raw);
|
|
3328
|
+
if (isValid(parsed)) {
|
|
3329
|
+
cachedInstallId = parsed.installId;
|
|
3330
|
+
return cachedInstallId;
|
|
3331
|
+
}
|
|
3332
|
+
cachedInstallId = generateAndPersist(filePath);
|
|
3333
|
+
return cachedInstallId;
|
|
3334
|
+
} catch {
|
|
3335
|
+
cachedInstallId = generateAndPersist(filePath);
|
|
3336
|
+
return cachedInstallId;
|
|
3337
|
+
}
|
|
3338
|
+
}
|
|
3339
|
+
|
|
3340
|
+
// packages/mcps/src/mcp/e2e/upstream-client.ts
|
|
3183
3341
|
var ALLOWED_UPSTREAM_PREFIXES = [
|
|
3184
3342
|
"/v1/protected/muggle-test/",
|
|
3185
3343
|
"/v1/protected/wallet/",
|
|
@@ -3226,8 +3384,17 @@ var PromptServiceClient = class {
|
|
|
3226
3384
|
* @returns Headers object.
|
|
3227
3385
|
*/
|
|
3228
3386
|
buildHeaders(credentials, correlationId) {
|
|
3387
|
+
const manifest = readReleaseManifest();
|
|
3388
|
+
const installId = getInstallId();
|
|
3389
|
+
const host = detectMcpHost();
|
|
3229
3390
|
const headers = {
|
|
3230
|
-
"X-Correlation-Id": correlationId
|
|
3391
|
+
"X-Correlation-Id": correlationId,
|
|
3392
|
+
"X-Client-Service-Name": manifest.serviceName,
|
|
3393
|
+
"X-Client-Release": manifest.release,
|
|
3394
|
+
"X-Client-Build-Id": manifest.buildId,
|
|
3395
|
+
"X-Client-Commit-Sha": manifest.commitSha,
|
|
3396
|
+
"X-Client-Install-Id": installId,
|
|
3397
|
+
"X-Client-Host": host
|
|
3231
3398
|
};
|
|
3232
3399
|
if (credentials.bearerToken) {
|
|
3233
3400
|
headers["Authorization"] = credentials.bearerToken.startsWith("Bearer ") ? credentials.bearerToken : `Bearer ${credentials.bearerToken}`;
|
|
@@ -3476,14 +3643,19 @@ var projectTools = [
|
|
|
3476
3643
|
},
|
|
3477
3644
|
{
|
|
3478
3645
|
name: "muggle-remote-project-list",
|
|
3479
|
-
description: "List
|
|
3646
|
+
description: "List projects accessible to the authenticated user. Returns up to 10 items per page by default (max 100). Response includes pagination metadata (totalCount, totalPages, hasMore) \u2014 check `hasMore` to decide whether to fetch additional pages.",
|
|
3480
3647
|
inputSchema: ProjectListInputSchema,
|
|
3481
3648
|
mapToUpstream: (input) => {
|
|
3482
3649
|
const data = input;
|
|
3483
3650
|
return {
|
|
3484
3651
|
method: "GET",
|
|
3485
3652
|
path: `${MUGGLE_TEST_PREFIX}/projects`,
|
|
3486
|
-
queryParams: {
|
|
3653
|
+
queryParams: {
|
|
3654
|
+
page: data.page,
|
|
3655
|
+
pageSize: data.pageSize,
|
|
3656
|
+
sortBy: data.sortBy,
|
|
3657
|
+
sortOrder: data.sortOrder
|
|
3658
|
+
}
|
|
3487
3659
|
};
|
|
3488
3660
|
}
|
|
3489
3661
|
},
|
|
@@ -3528,14 +3700,20 @@ var useCaseTools = [
|
|
|
3528
3700
|
},
|
|
3529
3701
|
{
|
|
3530
3702
|
name: "muggle-remote-use-case-list",
|
|
3531
|
-
description: "List
|
|
3703
|
+
description: "List use cases for a project. Returns up to 10 items per page by default (max 100). Response includes pagination metadata (totalCount, totalPages, hasMore) \u2014 check `hasMore` to decide whether to fetch additional pages.",
|
|
3532
3704
|
inputSchema: UseCaseListInputSchema,
|
|
3533
3705
|
mapToUpstream: (input) => {
|
|
3534
3706
|
const data = input;
|
|
3535
3707
|
return {
|
|
3536
3708
|
method: "GET",
|
|
3537
3709
|
path: `${MUGGLE_TEST_PREFIX}/use-cases`,
|
|
3538
|
-
queryParams: {
|
|
3710
|
+
queryParams: {
|
|
3711
|
+
projectId: data.projectId,
|
|
3712
|
+
page: data.page,
|
|
3713
|
+
pageSize: data.pageSize,
|
|
3714
|
+
sortBy: data.sortBy,
|
|
3715
|
+
sortOrder: data.sortOrder
|
|
3716
|
+
}
|
|
3539
3717
|
};
|
|
3540
3718
|
}
|
|
3541
3719
|
},
|
|
@@ -3573,7 +3751,10 @@ var useCaseTools = [
|
|
|
3573
3751
|
return {
|
|
3574
3752
|
method: "POST",
|
|
3575
3753
|
path: `${MUGGLE_TEST_PREFIX}/projects/${data.projectId}/use-cases/prompts/bulk`,
|
|
3576
|
-
body: {
|
|
3754
|
+
body: {
|
|
3755
|
+
projectId: data.projectId,
|
|
3756
|
+
prompts: data.instructions.map((instruction) => ({ instruction }))
|
|
3757
|
+
}
|
|
3577
3758
|
};
|
|
3578
3759
|
}
|
|
3579
3760
|
},
|
|
@@ -3589,19 +3770,62 @@ var useCaseTools = [
|
|
|
3589
3770
|
body: { instruction: data.instruction }
|
|
3590
3771
|
};
|
|
3591
3772
|
}
|
|
3773
|
+
},
|
|
3774
|
+
{
|
|
3775
|
+
name: "muggle-remote-use-case-create",
|
|
3776
|
+
description: "Create a single use case from a fully-specified payload. Use this to persist use cases returned by muggle-remote-use-case-bulk-preview-submit \u2014 no LLM is invoked.",
|
|
3777
|
+
inputSchema: UseCaseCreateInputSchema,
|
|
3778
|
+
mapToUpstream: (input) => {
|
|
3779
|
+
const data = input;
|
|
3780
|
+
return {
|
|
3781
|
+
method: "POST",
|
|
3782
|
+
path: `${MUGGLE_TEST_PREFIX}/use-cases`,
|
|
3783
|
+
body: {
|
|
3784
|
+
projectId: data.projectId,
|
|
3785
|
+
title: data.title,
|
|
3786
|
+
description: data.description,
|
|
3787
|
+
userStory: data.userStory,
|
|
3788
|
+
url: data.url,
|
|
3789
|
+
useCaseBreakdown: data.useCaseBreakdown,
|
|
3790
|
+
status: data.status,
|
|
3791
|
+
priority: data.priority,
|
|
3792
|
+
source: data.source,
|
|
3793
|
+
category: data.category
|
|
3794
|
+
}
|
|
3795
|
+
};
|
|
3796
|
+
}
|
|
3797
|
+
},
|
|
3798
|
+
{
|
|
3799
|
+
name: "muggle-remote-use-case-bulk-preview-submit",
|
|
3800
|
+
description: "Submit an async bulk-preview job that uses the OpenAI Batch API to generate use cases from many prompts at ~50% of normal LLM cost. Returns a job ID immediately; poll with muggle-remote-bulk-preview-job-get until the job reaches a terminal status, then persist each successful result via muggle-remote-use-case-create.",
|
|
3801
|
+
inputSchema: BulkPreviewSubmitUseCaseInputSchema,
|
|
3802
|
+
mapToUpstream: (input) => {
|
|
3803
|
+
const data = input;
|
|
3804
|
+
return {
|
|
3805
|
+
method: "POST",
|
|
3806
|
+
path: `${MUGGLE_TEST_PREFIX}/projects/${data.projectId}/use-cases/prompts/bulk-preview`,
|
|
3807
|
+
body: { prompts: data.prompts }
|
|
3808
|
+
};
|
|
3809
|
+
}
|
|
3592
3810
|
}
|
|
3593
3811
|
];
|
|
3594
3812
|
var testCaseTools = [
|
|
3595
3813
|
{
|
|
3596
3814
|
name: "muggle-remote-test-case-list",
|
|
3597
|
-
description: "List test cases for a project.",
|
|
3815
|
+
description: "List test cases for a project. Returns up to 10 items per page by default (max 100). Response includes pagination metadata (totalCount, totalPages, hasMore) \u2014 check `hasMore` to decide whether to fetch additional pages.",
|
|
3598
3816
|
inputSchema: TestCaseListInputSchema,
|
|
3599
3817
|
mapToUpstream: (input) => {
|
|
3600
3818
|
const data = input;
|
|
3601
3819
|
return {
|
|
3602
3820
|
method: "GET",
|
|
3603
3821
|
path: `${MUGGLE_TEST_PREFIX}/test-cases`,
|
|
3604
|
-
queryParams: {
|
|
3822
|
+
queryParams: {
|
|
3823
|
+
projectId: data.projectId,
|
|
3824
|
+
page: data.page,
|
|
3825
|
+
pageSize: data.pageSize,
|
|
3826
|
+
sortBy: data.sortBy,
|
|
3827
|
+
sortOrder: data.sortOrder
|
|
3828
|
+
}
|
|
3605
3829
|
};
|
|
3606
3830
|
}
|
|
3607
3831
|
},
|
|
@@ -3668,44 +3892,95 @@ var testCaseTools = [
|
|
|
3668
3892
|
}
|
|
3669
3893
|
};
|
|
3670
3894
|
}
|
|
3895
|
+
},
|
|
3896
|
+
{
|
|
3897
|
+
name: "muggle-remote-test-case-bulk-preview-submit",
|
|
3898
|
+
description: "Submit an async bulk-preview job that uses the OpenAI Batch API to generate test cases for a single use case from many prompts at ~50% of normal LLM cost. Returns a job ID immediately; poll with muggle-remote-bulk-preview-job-get until the job reaches a terminal status, then persist each successful result via muggle-remote-test-case-create. Note: one input prompt may fan out to 1\u20135 test cases.",
|
|
3899
|
+
inputSchema: BulkPreviewSubmitTestCaseInputSchema,
|
|
3900
|
+
mapToUpstream: (input) => {
|
|
3901
|
+
const data = input;
|
|
3902
|
+
return {
|
|
3903
|
+
method: "POST",
|
|
3904
|
+
path: `${MUGGLE_TEST_PREFIX}/projects/${data.projectId}/use-cases/${data.useCaseId}/test-cases/prompts/bulk-preview`,
|
|
3905
|
+
body: { prompts: data.prompts }
|
|
3906
|
+
};
|
|
3907
|
+
}
|
|
3671
3908
|
}
|
|
3672
3909
|
];
|
|
3673
|
-
var
|
|
3910
|
+
var bulkPreviewTools = [
|
|
3674
3911
|
{
|
|
3675
|
-
name: "muggle-remote-
|
|
3676
|
-
description: "
|
|
3677
|
-
inputSchema:
|
|
3912
|
+
name: "muggle-remote-bulk-preview-job-get",
|
|
3913
|
+
description: "Get the current status and (when terminal) results of a bulk-preview job. Poll this after submitting a bulk-preview job \u2014 every 10\u201315 seconds is fine. Terminal statuses: succeeded, partial, failed, cancelled, expired.",
|
|
3914
|
+
inputSchema: BulkPreviewJobGetInputSchema,
|
|
3678
3915
|
mapToUpstream: (input) => {
|
|
3679
3916
|
const data = input;
|
|
3680
3917
|
return {
|
|
3681
3918
|
method: "GET",
|
|
3682
|
-
path: `${MUGGLE_TEST_PREFIX}/
|
|
3683
|
-
queryParams: { projectId: data.projectId, testCaseId: data.testCaseId, page: data.page, pageSize: data.pageSize }
|
|
3919
|
+
path: `${MUGGLE_TEST_PREFIX}/projects/${data.projectId}/bulk-preview-jobs/${data.jobId}`
|
|
3684
3920
|
};
|
|
3685
3921
|
}
|
|
3686
3922
|
},
|
|
3687
3923
|
{
|
|
3688
|
-
name: "muggle-remote-
|
|
3689
|
-
description: "
|
|
3690
|
-
inputSchema:
|
|
3924
|
+
name: "muggle-remote-bulk-preview-job-list",
|
|
3925
|
+
description: "List bulk-preview jobs for a project, optionally filtered by status or kind.",
|
|
3926
|
+
inputSchema: BulkPreviewJobListInputSchema,
|
|
3691
3927
|
mapToUpstream: (input) => {
|
|
3692
3928
|
const data = input;
|
|
3693
3929
|
return {
|
|
3694
3930
|
method: "GET",
|
|
3695
|
-
path: `${MUGGLE_TEST_PREFIX}/
|
|
3931
|
+
path: `${MUGGLE_TEST_PREFIX}/projects/${data.projectId}/bulk-preview-jobs`,
|
|
3932
|
+
queryParams: {
|
|
3933
|
+
status: data.status?.join(","),
|
|
3934
|
+
kind: data.kind,
|
|
3935
|
+
limit: data.limit,
|
|
3936
|
+
cursor: data.cursor
|
|
3937
|
+
}
|
|
3696
3938
|
};
|
|
3697
3939
|
}
|
|
3698
3940
|
},
|
|
3699
3941
|
{
|
|
3700
|
-
name: "muggle-remote-
|
|
3701
|
-
description: "
|
|
3702
|
-
inputSchema:
|
|
3942
|
+
name: "muggle-remote-bulk-preview-job-cancel",
|
|
3943
|
+
description: "Request cancellation of a bulk-preview job. Cancellation is cooperative \u2014 the harvester picks it up on its next tick and moves the job to status=cancelled.",
|
|
3944
|
+
inputSchema: BulkPreviewJobCancelInputSchema,
|
|
3945
|
+
mapToUpstream: (input) => {
|
|
3946
|
+
const data = input;
|
|
3947
|
+
return {
|
|
3948
|
+
method: "DELETE",
|
|
3949
|
+
path: `${MUGGLE_TEST_PREFIX}/projects/${data.projectId}/bulk-preview-jobs/${data.jobId}`
|
|
3950
|
+
};
|
|
3951
|
+
}
|
|
3952
|
+
}
|
|
3953
|
+
];
|
|
3954
|
+
var testScriptTools = [
|
|
3955
|
+
{
|
|
3956
|
+
name: "muggle-remote-test-script-list",
|
|
3957
|
+
description: "List test scripts for a project, optionally filtered by test case. Returns up to 10 items per page by default (max 100). Response includes pagination metadata (totalCount, totalPages, hasMore) \u2014 check `hasMore` to decide whether to fetch additional pages.",
|
|
3958
|
+
inputSchema: TestScriptListInputSchema,
|
|
3703
3959
|
mapToUpstream: (input) => {
|
|
3704
3960
|
const data = input;
|
|
3705
3961
|
return {
|
|
3706
3962
|
method: "GET",
|
|
3707
|
-
path: `${MUGGLE_TEST_PREFIX}/test-scripts
|
|
3708
|
-
queryParams: {
|
|
3963
|
+
path: `${MUGGLE_TEST_PREFIX}/test-scripts`,
|
|
3964
|
+
queryParams: {
|
|
3965
|
+
projectId: data.projectId,
|
|
3966
|
+
testCaseId: data.testCaseId,
|
|
3967
|
+
page: data.page,
|
|
3968
|
+
pageSize: data.pageSize,
|
|
3969
|
+
sortBy: data.sortBy,
|
|
3970
|
+
sortOrder: data.sortOrder
|
|
3971
|
+
}
|
|
3972
|
+
};
|
|
3973
|
+
}
|
|
3974
|
+
},
|
|
3975
|
+
{
|
|
3976
|
+
name: "muggle-remote-test-script-get",
|
|
3977
|
+
description: "Get details of a specific test script.",
|
|
3978
|
+
inputSchema: TestScriptGetInputSchema,
|
|
3979
|
+
mapToUpstream: (input) => {
|
|
3980
|
+
const data = input;
|
|
3981
|
+
return {
|
|
3982
|
+
method: "GET",
|
|
3983
|
+
path: `${MUGGLE_TEST_PREFIX}/test-scripts/${data.testScriptId}`
|
|
3709
3984
|
};
|
|
3710
3985
|
}
|
|
3711
3986
|
}
|
|
@@ -4691,6 +4966,7 @@ var allQaToolDefinitions = [
|
|
|
4691
4966
|
...projectTools,
|
|
4692
4967
|
...useCaseTools,
|
|
4693
4968
|
...testCaseTools,
|
|
4969
|
+
...bulkPreviewTools,
|
|
4694
4970
|
...testScriptTools,
|
|
4695
4971
|
...actionScriptTools,
|
|
4696
4972
|
...workflowTools,
|
package/dist/cli.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { src_exports2 as commands, createChildLogger, createUnifiedMcpServer, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, server_exports as server, src_exports as shared } from './chunk-
|
|
1
|
+
export { src_exports2 as commands, createChildLogger, createUnifiedMcpServer, e2e_exports as e2e, getConfig, getLocalQaTools, getLogger, getQaTools, local_exports as localQa, mcp_exports as mcp, e2e_exports as qa, server_exports as server, src_exports as shared } from './chunk-MNCBJEPQ.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.
|
|
4
|
+
"version": "4.5.0",
|
|
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.
|
|
5
|
+
"version": "4.5.0",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Muggle AI",
|
|
8
8
|
"email": "support@muggle-ai.com"
|
package/dist/plugin/README.md
CHANGED
|
@@ -28,6 +28,7 @@ Type `muggle` to discover the full command family.
|
|
|
28
28
|
| `/muggle:muggle-test` | Change-driven E2E acceptance router: detects code changes, maps to use cases, runs test generation locally or remotely, publishes to dashboard, opens in browser, posts E2E acceptance results to PR. |
|
|
29
29
|
| `/muggle:muggle-test-feature-local` | Test a feature on localhost with AI-driven browser automation. Offers publish to cloud after each run. |
|
|
30
30
|
| `/muggle:muggle-test-import` | Import existing tests into Muggle Test — from Playwright/Cypress specs, PRDs, Gherkin feature files, test plan docs, or any test artifact. |
|
|
31
|
+
| `/muggle:muggle-test-regenerate-missing` | Bulk-regenerate test scripts for every test case in a project that doesn't currently have an active script. Scans DRAFT + GENERATION_PENDING, confirms the list with the user, and dispatches remote generation workflows for each. |
|
|
31
32
|
| `/muggle:muggle-status` | Health check for Electron browser test runner, MCP server, and authentication. |
|
|
32
33
|
| `/muggle:muggle-repair` | Diagnose and fix broken installation automatically. |
|
|
33
34
|
| `/muggle:muggle-upgrade` | Update Electron browser test runner and MCP server to latest version. |
|