@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.
Files changed (28) hide show
  1. package/README.md +31 -13
  2. package/dist/{chunk-PMI2DI3V.js → chunk-MNCBJEPQ.js} +311 -35
  3. package/dist/cli.js +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  6. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  7. package/dist/plugin/README.md +1 -0
  8. package/dist/plugin/skills/do/open-prs.md +35 -74
  9. package/dist/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +181 -0
  10. package/dist/plugin/skills/muggle-test/SKILL.md +44 -48
  11. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +51 -1
  12. package/dist/plugin/skills/muggle-test-import/SKILL.md +122 -23
  13. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +196 -0
  14. package/dist/plugin/skills/muggle-test-regenerate-missing/evals/evals.json +58 -0
  15. package/dist/plugin/skills/muggle-test-regenerate-missing/evals/trigger-eval.json +22 -0
  16. package/dist/release-manifest.json +7 -0
  17. package/package.json +7 -7
  18. package/plugin/.claude-plugin/plugin.json +1 -1
  19. package/plugin/.cursor-plugin/plugin.json +1 -1
  20. package/plugin/README.md +1 -0
  21. package/plugin/skills/do/open-prs.md +35 -74
  22. package/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +181 -0
  23. package/plugin/skills/muggle-test/SKILL.md +44 -48
  24. package/plugin/skills/muggle-test-feature-local/SKILL.md +51 -1
  25. package/plugin/skills/muggle-test-import/SKILL.md +122 -23
  26. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +196 -0
  27. package/plugin/skills/muggle-test-regenerate-missing/evals/evals.json +58 -0
  28. 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 | Purpose |
250
- | -------------------------------------------- | ---------------------------- |
251
- | `muggle-remote-use-case-list` | List use cases |
252
- | `muggle-remote-use-case-create-from-prompts` | Create from natural language |
253
- | `muggle-remote-use-case-prompt-preview` | Preview before creating |
254
- | `muggle-remote-use-case-update-from-prompt` | Regenerate from new prompt |
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, rmSync, mkdirSync, readdirSync, createWriteStream, writeFileSync, statSync } from 'fs';
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, join } from 'path';
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: "1.0.0",
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().optional().describe("Page number (1-based)"),
2858
- pageSize: z.number().int().positive().max(100).optional().describe("Number of items per page")
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
- prompts: z.array(z.object({
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 all projects accessible to the authenticated user.",
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: { page: data.page, pageSize: data.pageSize }
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 all use cases for a project.",
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: { projectId: data.projectId, page: data.page, pageSize: data.pageSize }
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: { projectId: data.projectId, prompts: data.prompts }
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: { projectId: data.projectId, page: data.page, pageSize: data.pageSize }
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 testScriptTools = [
3910
+ var bulkPreviewTools = [
3674
3911
  {
3675
- name: "muggle-remote-test-script-list",
3676
- description: "List test scripts for a project, optionally filtered by test case.",
3677
- inputSchema: TestScriptListInputSchema,
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}/test-scripts`,
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-test-script-get",
3689
- description: "Get details of a specific test script.",
3690
- inputSchema: TestScriptGetInputSchema,
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}/test-scripts/${data.testScriptId}`
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-test-script-list-paginated",
3701
- description: "List test scripts with full pagination support.",
3702
- inputSchema: TestScriptListPaginatedInputSchema,
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/paginated`,
3708
- queryParams: { projectId: data.projectId, page: data.page, pageSize: data.pageSize }
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
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from './chunk-PMI2DI3V.js';
2
+ import { runCli } from './chunk-MNCBJEPQ.js';
3
3
 
4
4
  // src/cli/main.ts
5
5
  runCli().catch((error) => {
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-PMI2DI3V.js';
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.0",
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.4.0",
5
+ "version": "4.5.0",
6
6
  "author": {
7
7
  "name": "Muggle AI",
8
8
  "email": "support@muggle-ai.com"
@@ -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. |