@muggleai/works 4.10.0 → 4.11.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 (197) hide show
  1. package/README.md +3 -3
  2. package/dist/{chunk-RK6XKSZK.js → chunk-Q7WNEMN4.js} +109 -43
  3. package/dist/{chunk-UVXOHXY2.js → chunk-ZKRHFTIC.js} +832 -89
  4. package/dist/cli.js +2 -2
  5. package/dist/index.js +2 -2
  6. package/dist/plugin/.claude-plugin/plugin.json +1 -1
  7. package/dist/plugin/.cursor-plugin/plugin.json +1 -1
  8. package/dist/plugin/README.md +4 -2
  9. package/dist/plugin/agents/acceptance-tester.md +24 -10
  10. package/dist/plugin/commands/m.md +7 -0
  11. package/dist/plugin/commands/mdo.md +7 -0
  12. package/dist/plugin/commands/mfeedback.md +7 -0
  13. package/dist/plugin/commands/mimport.md +7 -0
  14. package/dist/plugin/commands/mpr.md +7 -0
  15. package/dist/plugin/commands/mprefs.md +7 -0
  16. package/dist/plugin/commands/mregen.md +7 -0
  17. package/dist/plugin/commands/mrelease.md +7 -0
  18. package/dist/plugin/commands/mrepair.md +7 -0
  19. package/dist/plugin/commands/mstatus.md +7 -0
  20. package/dist/plugin/commands/mtest.md +7 -0
  21. package/dist/plugin/commands/mtestlocal.md +7 -0
  22. package/dist/plugin/commands/mtestprep.md +7 -0
  23. package/dist/plugin/commands/mupgrade.md +7 -0
  24. package/dist/plugin/scripts/ensure-electron-app.sh +35 -8
  25. package/dist/plugin/skills/_aliases.json +18 -0
  26. package/dist/plugin/skills/_shared/dev-server-readiness.md +231 -0
  27. package/dist/plugin/skills/_shared/failure-mode-handling.md +244 -0
  28. package/dist/plugin/skills/_shared/post-merge-cleanup.md +10 -0
  29. package/dist/plugin/skills/_shared/pr-followup-helpers.md +150 -0
  30. package/dist/plugin/skills/_shared/rebase-before-e2e.md +15 -0
  31. package/dist/plugin/skills/_shared/telemetry-emit.md +15 -0
  32. package/dist/plugin/skills/_shared/use-worktrees.md +53 -0
  33. package/dist/plugin/skills/do/build.md +51 -0
  34. package/dist/plugin/skills/do/e2e-acceptance.md +64 -19
  35. package/dist/plugin/skills/do/impact-analysis.md +47 -7
  36. package/dist/plugin/skills/do/open-prs.md +72 -74
  37. package/dist/plugin/skills/do/pre-flight.md +21 -14
  38. package/dist/plugin/skills/do/requirements.md +2 -2
  39. package/dist/plugin/skills/do/unit-tests.md +2 -2
  40. package/dist/plugin/skills/m/SKILL.md +8 -0
  41. package/dist/plugin/skills/mdo/SKILL.md +8 -0
  42. package/dist/plugin/skills/mfeedback/SKILL.md +8 -0
  43. package/dist/plugin/skills/mimport/SKILL.md +8 -0
  44. package/dist/plugin/skills/mpr/SKILL.md +8 -0
  45. package/dist/plugin/skills/mprefs/SKILL.md +8 -0
  46. package/dist/plugin/skills/mregen/SKILL.md +8 -0
  47. package/dist/plugin/skills/mrelease/SKILL.md +8 -0
  48. package/dist/plugin/skills/mrepair/SKILL.md +8 -0
  49. package/dist/plugin/skills/mstatus/SKILL.md +8 -0
  50. package/dist/plugin/skills/mtest/SKILL.md +8 -0
  51. package/dist/plugin/skills/mtestlocal/SKILL.md +8 -0
  52. package/dist/plugin/skills/mtestprep/SKILL.md +8 -0
  53. package/dist/plugin/skills/muggle/SKILL.md +9 -6
  54. package/dist/plugin/skills/muggle-do/SKILL.md +36 -61
  55. package/dist/plugin/skills/muggle-do-task/SKILL.md +134 -0
  56. package/dist/plugin/skills/muggle-feedback/SKILL.md +39 -0
  57. package/dist/plugin/skills/muggle-feedback/ops/delete.md +49 -0
  58. package/dist/plugin/skills/muggle-feedback/ops/list.md +65 -0
  59. package/dist/plugin/skills/muggle-feedback/ops/submit.md +130 -0
  60. package/dist/plugin/skills/muggle-pr-followup/SKILL.md +59 -0
  61. package/dist/plugin/skills/muggle-pr-followup/contract.md +191 -0
  62. package/dist/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +68 -43
  63. package/dist/plugin/skills/muggle-pr-visual-walkthrough/e2e-report-assembly.md +86 -0
  64. package/dist/plugin/skills/muggle-preferences/SKILL.md +16 -108
  65. package/dist/plugin/skills/muggle-preferences/ops/change-one.md +10 -0
  66. package/dist/plugin/skills/muggle-preferences/ops/configure.md +49 -0
  67. package/dist/plugin/skills/muggle-preferences/ops/list.md +9 -0
  68. package/dist/plugin/skills/muggle-preferences/ops/reset.md +6 -0
  69. package/dist/plugin/skills/muggle-preferences/ops/set.md +10 -0
  70. package/dist/plugin/skills/muggle-preferences/preference-gates/README.md +1 -1
  71. package/dist/plugin/skills/muggle-preferences/preference-gates/autoCleanup.md +11 -0
  72. package/dist/plugin/skills/muggle-preferences/preference-gates/autoCreatePR.md +13 -0
  73. package/dist/plugin/skills/muggle-preferences/preference-gates/autoDetectChanges.md +3 -3
  74. package/dist/plugin/skills/muggle-preferences/preference-gates/autoE2ETest.md +11 -0
  75. package/dist/plugin/skills/muggle-preferences/preference-gates/autoLogin.md +1 -1
  76. package/dist/plugin/skills/muggle-preferences/preference-gates/autoPublishLocalResults.md +2 -2
  77. package/dist/plugin/skills/muggle-preferences/preference-gates/autoRebase.md +11 -0
  78. package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +19 -0
  79. package/dist/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -2
  80. package/dist/plugin/skills/muggle-preferences/preference-gates/autoUseWorktree.md +11 -0
  81. package/dist/plugin/skills/muggle-preferences/preference-gates/checkForUpdates.md +4 -4
  82. package/dist/plugin/skills/muggle-preferences/preference-gates/defaultExecutionMode.md +1 -1
  83. package/dist/plugin/skills/muggle-preferences/preference-gates/openTestResultsAfterRun.md +1 -1
  84. package/dist/plugin/skills/muggle-preferences/preference-gates/postPRVisualWalkthrough.md +8 -25
  85. package/dist/plugin/skills/muggle-preferences/preference-gates/showElectronBrowser.md +1 -1
  86. package/dist/plugin/skills/muggle-preferences/preference-gates/suggestRelatedTestCases.md +4 -4
  87. package/dist/plugin/skills/muggle-preferences/preference-gates/suggestRelatedUseCases.md +4 -4
  88. package/dist/plugin/skills/muggle-repair/SKILL.md +4 -2
  89. package/dist/plugin/skills/muggle-status/SKILL.md +8 -5
  90. package/dist/plugin/skills/muggle-test/SKILL.md +110 -86
  91. package/dist/plugin/skills/muggle-test-feature-local/SKILL.md +83 -62
  92. package/dist/plugin/skills/muggle-test-import/SKILL.md +28 -23
  93. package/dist/plugin/skills/muggle-test-prepare/SKILL.md +79 -18
  94. package/dist/plugin/skills/muggle-test-regenerate-missing/SKILL.md +34 -18
  95. package/dist/plugin/skills/muggle-test-regenerate-missing/evals/evals.json +9 -9
  96. package/dist/plugin/skills/muggle-upgrade/SKILL.md +4 -2
  97. package/dist/plugin/skills/muggle-works-npm-release/SKILL.md +6 -4
  98. package/dist/plugin/skills/mupgrade/SKILL.md +8 -0
  99. package/dist/release-manifest.json +4 -4
  100. package/dist/{src-GD3YFCCW.js → src-2G7ENRGI.js} +1 -1
  101. package/package.json +9 -7
  102. package/plugin/.claude-plugin/plugin.json +1 -1
  103. package/plugin/.cursor-plugin/plugin.json +1 -1
  104. package/plugin/README.md +4 -2
  105. package/plugin/agents/acceptance-tester.md +24 -10
  106. package/plugin/commands/m.md +7 -0
  107. package/plugin/commands/mdo.md +7 -0
  108. package/plugin/commands/mfeedback.md +7 -0
  109. package/plugin/commands/mimport.md +7 -0
  110. package/plugin/commands/mpr.md +7 -0
  111. package/plugin/commands/mprefs.md +7 -0
  112. package/plugin/commands/mregen.md +7 -0
  113. package/plugin/commands/mrelease.md +7 -0
  114. package/plugin/commands/mrepair.md +7 -0
  115. package/plugin/commands/mstatus.md +7 -0
  116. package/plugin/commands/mtest.md +7 -0
  117. package/plugin/commands/mtestlocal.md +7 -0
  118. package/plugin/commands/mtestprep.md +7 -0
  119. package/plugin/commands/mupgrade.md +7 -0
  120. package/plugin/scripts/ensure-electron-app.sh +35 -8
  121. package/plugin/skills/_aliases.json +18 -0
  122. package/plugin/skills/_shared/dev-server-readiness.md +231 -0
  123. package/plugin/skills/_shared/failure-mode-handling.md +244 -0
  124. package/plugin/skills/_shared/post-merge-cleanup.md +10 -0
  125. package/plugin/skills/_shared/pr-followup-helpers.md +150 -0
  126. package/plugin/skills/_shared/rebase-before-e2e.md +15 -0
  127. package/plugin/skills/_shared/telemetry-emit.md +15 -0
  128. package/plugin/skills/_shared/use-worktrees.md +53 -0
  129. package/plugin/skills/do/build.md +51 -0
  130. package/plugin/skills/do/e2e-acceptance.md +64 -19
  131. package/plugin/skills/do/impact-analysis.md +47 -7
  132. package/plugin/skills/do/open-prs.md +72 -74
  133. package/plugin/skills/do/pre-flight.md +21 -14
  134. package/plugin/skills/do/requirements.md +2 -2
  135. package/plugin/skills/do/unit-tests.md +2 -2
  136. package/plugin/skills/m/SKILL.md +8 -0
  137. package/plugin/skills/mdo/SKILL.md +8 -0
  138. package/plugin/skills/mfeedback/SKILL.md +8 -0
  139. package/plugin/skills/mimport/SKILL.md +8 -0
  140. package/plugin/skills/mpr/SKILL.md +8 -0
  141. package/plugin/skills/mprefs/SKILL.md +8 -0
  142. package/plugin/skills/mregen/SKILL.md +8 -0
  143. package/plugin/skills/mrelease/SKILL.md +8 -0
  144. package/plugin/skills/mrepair/SKILL.md +8 -0
  145. package/plugin/skills/mstatus/SKILL.md +8 -0
  146. package/plugin/skills/mtest/SKILL.md +8 -0
  147. package/plugin/skills/mtestlocal/SKILL.md +8 -0
  148. package/plugin/skills/mtestprep/SKILL.md +8 -0
  149. package/plugin/skills/muggle/SKILL.md +9 -6
  150. package/plugin/skills/muggle-do/SKILL.md +36 -61
  151. package/plugin/skills/muggle-do-task/SKILL.md +134 -0
  152. package/plugin/skills/muggle-feedback/SKILL.md +39 -0
  153. package/plugin/skills/muggle-feedback/ops/delete.md +49 -0
  154. package/plugin/skills/muggle-feedback/ops/list.md +65 -0
  155. package/plugin/skills/muggle-feedback/ops/submit.md +130 -0
  156. package/plugin/skills/muggle-pr-followup/SKILL.md +59 -0
  157. package/plugin/skills/muggle-pr-followup/contract.md +191 -0
  158. package/plugin/skills/muggle-pr-visual-walkthrough/SKILL.md +68 -43
  159. package/plugin/skills/muggle-pr-visual-walkthrough/e2e-report-assembly.md +86 -0
  160. package/plugin/skills/muggle-preferences/SKILL.md +16 -108
  161. package/plugin/skills/muggle-preferences/ops/change-one.md +10 -0
  162. package/plugin/skills/muggle-preferences/ops/configure.md +49 -0
  163. package/plugin/skills/muggle-preferences/ops/list.md +9 -0
  164. package/plugin/skills/muggle-preferences/ops/reset.md +6 -0
  165. package/plugin/skills/muggle-preferences/ops/set.md +10 -0
  166. package/plugin/skills/muggle-preferences/preference-gates/README.md +1 -1
  167. package/plugin/skills/muggle-preferences/preference-gates/autoCleanup.md +11 -0
  168. package/plugin/skills/muggle-preferences/preference-gates/autoCreatePR.md +13 -0
  169. package/plugin/skills/muggle-preferences/preference-gates/autoDetectChanges.md +3 -3
  170. package/plugin/skills/muggle-preferences/preference-gates/autoE2ETest.md +11 -0
  171. package/plugin/skills/muggle-preferences/preference-gates/autoLogin.md +1 -1
  172. package/plugin/skills/muggle-preferences/preference-gates/autoPublishLocalResults.md +2 -2
  173. package/plugin/skills/muggle-preferences/preference-gates/autoRebase.md +11 -0
  174. package/plugin/skills/muggle-preferences/preference-gates/autoSelectLocalHost.md +19 -0
  175. package/plugin/skills/muggle-preferences/preference-gates/autoSelectProject.md +1 -2
  176. package/plugin/skills/muggle-preferences/preference-gates/autoUseWorktree.md +11 -0
  177. package/plugin/skills/muggle-preferences/preference-gates/checkForUpdates.md +4 -4
  178. package/plugin/skills/muggle-preferences/preference-gates/defaultExecutionMode.md +1 -1
  179. package/plugin/skills/muggle-preferences/preference-gates/openTestResultsAfterRun.md +1 -1
  180. package/plugin/skills/muggle-preferences/preference-gates/postPRVisualWalkthrough.md +8 -25
  181. package/plugin/skills/muggle-preferences/preference-gates/showElectronBrowser.md +1 -1
  182. package/plugin/skills/muggle-preferences/preference-gates/suggestRelatedTestCases.md +4 -4
  183. package/plugin/skills/muggle-preferences/preference-gates/suggestRelatedUseCases.md +4 -4
  184. package/plugin/skills/muggle-repair/SKILL.md +4 -2
  185. package/plugin/skills/muggle-status/SKILL.md +8 -5
  186. package/plugin/skills/muggle-test/SKILL.md +110 -86
  187. package/plugin/skills/muggle-test-feature-local/SKILL.md +83 -62
  188. package/plugin/skills/muggle-test-import/SKILL.md +28 -23
  189. package/plugin/skills/muggle-test-prepare/SKILL.md +79 -18
  190. package/plugin/skills/muggle-test-regenerate-missing/SKILL.md +34 -18
  191. package/plugin/skills/muggle-test-regenerate-missing/evals/evals.json +9 -9
  192. package/plugin/skills/muggle-upgrade/SKILL.md +4 -2
  193. package/plugin/skills/muggle-works-npm-release/SKILL.md +6 -4
  194. package/plugin/skills/mupgrade/SKILL.md +8 -0
  195. package/scripts/postinstall.mjs +11 -1
  196. package/dist/plugin/skills/do/validate-code.md +0 -38
  197. package/plugin/skills/do/validate-code.md +0 -38
@@ -11,6 +11,7 @@ import { fileURLToPath } from 'url';
11
11
  import { spawn, exec } from 'child_process';
12
12
  import axios, { AxiosError } from 'axios';
13
13
  import * as fs7 from 'fs/promises';
14
+ import AppInsights from 'applicationinsights';
14
15
  import { z } from 'zod';
15
16
 
16
17
  var __defProp = Object.defineProperty;
@@ -84,6 +85,7 @@ __export(src_exports, {
84
85
  resetConfig: () => resetConfig,
85
86
  resetLogger: () => resetLogger,
86
87
  resetPreference: () => resetPreference,
88
+ resolveElectronAppPathOrNull: () => resolveElectronAppPathOrNull,
87
89
  resolvePreferences: () => resolvePreferences,
88
90
  saveApiKey: () => saveApiKey,
89
91
  saveApiKeyData: () => saveApiKeyData,
@@ -395,7 +397,6 @@ function buildLocalQaConfig() {
395
397
  tempDir: path2.join(dataDir, "temp"),
396
398
  apiKeyFilePath: path2.join(dataDir, API_KEY_FILE),
397
399
  oauthSessionFilePath: path2.join(dataDir, "oauth-session.json"),
398
- electronAppPath: resolveElectronAppPathOrNull(),
399
400
  webServicePath: resolveWebServicePath(),
400
401
  webServicePidFile: path2.join(dataDir, "web-service.pid"),
401
402
  auth0: {
@@ -527,9 +528,9 @@ function resetLogger() {
527
528
  // packages/mcps/src/shared/checksum.ts
528
529
  var logger = getLogger();
529
530
  function getPlatformKey() {
530
- const os4 = platform();
531
+ const os5 = platform();
531
532
  const arch2 = process.arch;
532
- switch (os4) {
533
+ switch (os5) {
533
534
  case "darwin":
534
535
  return arch2 === "arm64" ? "darwin-arm64" : "darwin-x64";
535
536
  case "win32":
@@ -537,7 +538,7 @@ function getPlatformKey() {
537
538
  case "linux":
538
539
  return "linux-x64";
539
540
  default:
540
- throw new Error(`Unsupported platform: ${os4}`);
541
+ throw new Error(`Unsupported platform: ${os5}`);
541
542
  }
542
543
  }
543
544
  async function calculateFileChecksum(filePath) {
@@ -2101,29 +2102,28 @@ async function writeExecutionLogs(params) {
2101
2102
  });
2102
2103
  }
2103
2104
  function getElectronAppPathOrThrow() {
2104
- const config = getConfig();
2105
- const electronAppPath = config.localQa.electronAppPath;
2106
- if (!electronAppPath || electronAppPath.trim() === "") {
2107
- const version = getElectronAppVersion();
2108
- const versionDir = getElectronAppDir(version);
2109
- const envPath = process.env.ELECTRON_APP_PATH;
2110
- const errorLines = [
2111
- "Electron app binary not found.",
2112
- "",
2113
- ` Expected version: ${version}`,
2114
- ` Checked directory: ${versionDir}`
2115
- ];
2116
- if (envPath) {
2117
- errorLines.push(` ELECTRON_APP_PATH: ${envPath} (not found or invalid)`);
2118
- } else {
2119
- errorLines.push(" ELECTRON_APP_PATH: (not set)");
2120
- }
2121
- errorLines.push("");
2122
- errorLines.push("To fix this, run: muggle setup");
2123
- errorLines.push("Or set ELECTRON_APP_PATH to the path of the MuggleAI executable.");
2124
- throw new Error(errorLines.join("\n"));
2105
+ const electronAppPath = resolveElectronAppPathOrNull();
2106
+ if (electronAppPath && electronAppPath.trim() !== "") {
2107
+ return electronAppPath;
2125
2108
  }
2126
- return electronAppPath;
2109
+ const version = getElectronAppVersion();
2110
+ const versionDir = getElectronAppDir(version);
2111
+ const envPath = process.env.ELECTRON_APP_PATH;
2112
+ const errorLines = [
2113
+ "Electron app binary not found.",
2114
+ "",
2115
+ ` Expected version: ${version}`,
2116
+ ` Checked directory: ${versionDir}`
2117
+ ];
2118
+ if (envPath) {
2119
+ errorLines.push(` ELECTRON_APP_PATH: ${envPath} (not found or invalid)`);
2120
+ } else {
2121
+ errorLines.push(" ELECTRON_APP_PATH: (not set)");
2122
+ }
2123
+ errorLines.push("");
2124
+ errorLines.push("To fix this, run: muggle setup");
2125
+ errorLines.push("Or set ELECTRON_APP_PATH to the path of the MuggleAI executable.");
2126
+ throw new Error(errorLines.join("\n"));
2127
2127
  }
2128
2128
  function getRequiredStringField(params) {
2129
2129
  const value = params.source[params.fieldName];
@@ -2223,7 +2223,7 @@ function buildReplayActionScript(params) {
2223
2223
  async function executeElectronAppAsync(params) {
2224
2224
  const mode = params.runType === "generation" ? "explore" : "engine";
2225
2225
  const electronAppPath = getElectronAppPathOrThrow();
2226
- const spawnArgs = [mode, params.scriptFilePath, "", params.authFilePath];
2226
+ const spawnArgs = [mode, params.scriptFilePath, params.mutationsFilePath ?? "", params.authFilePath];
2227
2227
  if (params.showUi) {
2228
2228
  spawnArgs.push("--show-ui");
2229
2229
  }
@@ -2371,11 +2371,16 @@ async function executeTestGeneration(params) {
2371
2371
  filename: `${runId}_auth.json`,
2372
2372
  data: authContent
2373
2373
  });
2374
+ const mutationsFilePath = params.mutations && params.mutations.length > 0 ? await writeTempFile({
2375
+ filename: `${runId}_mutations.json`,
2376
+ data: params.mutations
2377
+ }) : void 0;
2374
2378
  try {
2375
2379
  const executionResult = await executeElectronAppAsync({
2376
2380
  runId,
2377
2381
  runType: "generation",
2378
2382
  scriptFilePath: inputFilePath,
2383
+ mutationsFilePath,
2379
2384
  authFilePath,
2380
2385
  timeoutMs,
2381
2386
  showUi: params.showUi,
@@ -2518,11 +2523,16 @@ async function executeReplay(params) {
2518
2523
  filename: `${runId}_auth.json`,
2519
2524
  data: authContent
2520
2525
  });
2526
+ const mutationsFilePath = params.mutations && params.mutations.length > 0 ? await writeTempFile({
2527
+ filename: `${runId}_mutations.json`,
2528
+ data: params.mutations
2529
+ }) : void 0;
2521
2530
  try {
2522
2531
  const executionResult = await executeElectronAppAsync({
2523
2532
  runId,
2524
2533
  runType: "replay",
2525
2534
  scriptFilePath: inputFilePath,
2535
+ mutationsFilePath,
2526
2536
  authFilePath,
2527
2537
  timeoutMs,
2528
2538
  showUi: params.showUi,
@@ -2991,6 +3001,268 @@ __export(e2e_exports, {
2991
3001
  executeQaTool: () => executeQaTool,
2992
3002
  getQaToolByName: () => getQaToolByName
2993
3003
  });
3004
+
3005
+ var ENV_CONNECTION_STRING = "APPLICATIONINSIGHTS_CONNECTION_STRING";
3006
+ var ENV_VITE_CONNECTION_STRING = "VITE_APPLICATIONINSIGHTS_CONNECTION_STRING";
3007
+ var ENV_TELEMETRY_DISABLED = "MUGGLE_TELEMETRY_DISABLED";
3008
+
3009
+ var DATA_DIR_NAME2 = ".muggle-ai";
3010
+ var INSTALL_ID_FILENAME = "install-id.json";
3011
+ var PREFS_FILENAME = "preferences.json";
3012
+ var OAUTH_SESSION_FILENAME = "oauth-session.json";
3013
+ var RELEASE_MANIFEST_FILENAME = "release-manifest.json";
3014
+ function getDataDir3() {
3015
+ return path2.join(os3.homedir(), DATA_DIR_NAME2);
3016
+ }
3017
+
3018
+ var cachedInstallId;
3019
+ function installIdFilePath() {
3020
+ return join(getDataDir3(), INSTALL_ID_FILENAME);
3021
+ }
3022
+ function generateAndPersist(filePath) {
3023
+ const installId = randomUUID();
3024
+ const contents = { installId };
3025
+ mkdirSync(dirname(filePath), { recursive: true });
3026
+ writeFileSync(filePath, JSON.stringify(contents, null, 2) + "\n", "utf8");
3027
+ return installId;
3028
+ }
3029
+ function isValid(value) {
3030
+ return typeof value === "object" && value !== null && typeof value.installId === "string" && value.installId.length > 0;
3031
+ }
3032
+ function getInstallId() {
3033
+ if (cachedInstallId !== void 0)
3034
+ return cachedInstallId;
3035
+ const filePath = installIdFilePath();
3036
+ if (!existsSync(filePath)) {
3037
+ cachedInstallId = generateAndPersist(filePath);
3038
+ return cachedInstallId;
3039
+ }
3040
+ try {
3041
+ const raw = readFileSync(filePath, "utf8");
3042
+ const parsed = JSON.parse(raw);
3043
+ if (isValid(parsed)) {
3044
+ cachedInstallId = parsed.installId;
3045
+ return cachedInstallId;
3046
+ }
3047
+ cachedInstallId = generateAndPersist(filePath);
3048
+ return cachedInstallId;
3049
+ } catch {
3050
+ cachedInstallId = generateAndPersist(filePath);
3051
+ return cachedInstallId;
3052
+ }
3053
+ }
3054
+
3055
+ var DEV_MANIFEST2 = {
3056
+ release: "dev",
3057
+ buildId: "dev",
3058
+ commitSha: "dev",
3059
+ buildTime: "1970-01-01T00:00:00Z",
3060
+ serviceName: "muggle-ai-telemetry"
3061
+ };
3062
+
3063
+ var MANIFEST_PATH2 = (() => {
3064
+ try {
3065
+ return join(dirname(fileURLToPath(import.meta.url)), RELEASE_MANIFEST_FILENAME);
3066
+ } catch {
3067
+ return RELEASE_MANIFEST_FILENAME;
3068
+ }
3069
+ })();
3070
+ var cachedManifest2;
3071
+ function isValid2(value) {
3072
+ if (typeof value !== "object" || value === null)
3073
+ return false;
3074
+ const m = value;
3075
+ return typeof m.release === "string" && typeof m.buildId === "string" && typeof m.commitSha === "string" && typeof m.buildTime === "string" && typeof m.serviceName === "string";
3076
+ }
3077
+ function readReleaseManifest2() {
3078
+ if (cachedManifest2 !== void 0)
3079
+ return cachedManifest2;
3080
+ const path15 = MANIFEST_PATH2;
3081
+ try {
3082
+ const raw = readFileSync(path15, "utf8");
3083
+ const parsed = JSON.parse(raw);
3084
+ cachedManifest2 = isValid2(parsed) ? parsed : DEV_MANIFEST2;
3085
+ return cachedManifest2;
3086
+ } catch (err) {
3087
+ const code = err.code;
3088
+ if (code !== "ENOENT") {
3089
+ console.warn("release-manifest: unexpected read error, using DEV fallback", {
3090
+ error: err.message
3091
+ });
3092
+ }
3093
+ cachedManifest2 = DEV_MANIFEST2;
3094
+ return cachedManifest2;
3095
+ }
3096
+ }
3097
+ function readUserId() {
3098
+ const filePath = join(getDataDir3(), OAUTH_SESSION_FILENAME);
3099
+ if (!existsSync(filePath))
3100
+ return void 0;
3101
+ try {
3102
+ const raw = readFileSync(filePath, "utf8");
3103
+ const parsed = JSON.parse(raw);
3104
+ if (typeof parsed.userId === "string" && parsed.userId.length > 0) {
3105
+ return parsed.userId;
3106
+ }
3107
+ return void 0;
3108
+ } catch {
3109
+ return void 0;
3110
+ }
3111
+ }
3112
+
3113
+ var state = null;
3114
+ function resolveConnectionString(explicit) {
3115
+ if (explicit && explicit.length > 0)
3116
+ return explicit;
3117
+ return process.env[ENV_CONNECTION_STRING] ?? process.env[ENV_VITE_CONNECTION_STRING] ?? void 0;
3118
+ }
3119
+ function buildCommonProperties(serviceName, surface) {
3120
+ const manifest = readReleaseManifest2();
3121
+ const props = {
3122
+ source: "client",
3123
+ serviceName,
3124
+ surface,
3125
+ release: manifest.release,
3126
+ buildId: manifest.buildId,
3127
+ commitSha: manifest.commitSha,
3128
+ buildTime: manifest.buildTime,
3129
+ installId: getInstallId(),
3130
+ nodeVersion: process.version,
3131
+ platform: process.platform
3132
+ };
3133
+ const userId = readUserId();
3134
+ if (userId !== void 0)
3135
+ props.userId = userId;
3136
+ return props;
3137
+ }
3138
+ function initTelemetry(options) {
3139
+ if (state !== null && state.initialized)
3140
+ return;
3141
+ const connectionString = resolveConnectionString(options.connectionString);
3142
+ if (!connectionString) {
3143
+ state = {
3144
+ client: null,
3145
+ serviceName: options.serviceName,
3146
+ surface: options.surface,
3147
+ initialized: true
3148
+ };
3149
+ return;
3150
+ }
3151
+ try {
3152
+ AppInsights.setup(connectionString).setUseDiskRetryCaching(true).setDistributedTracingMode(AppInsights.DistributedTracingModes.AI_AND_W3C).setAutoCollectRequests(false).setAutoCollectPerformance(false, false).setAutoCollectExceptions(true).setAutoCollectDependencies(false).setAutoCollectConsole(false).setSendLiveMetrics(false);
3153
+ const client = AppInsights.defaultClient;
3154
+ const manifest = readReleaseManifest2();
3155
+ client.context.tags[client.context.keys.cloudRole] = options.serviceName;
3156
+ client.context.tags[client.context.keys.applicationVersion] = manifest.release;
3157
+ client.commonProperties = {
3158
+ ...client.commonProperties,
3159
+ ...buildCommonProperties(options.serviceName, options.surface)
3160
+ };
3161
+ AppInsights.start();
3162
+ state = {
3163
+ client,
3164
+ serviceName: options.serviceName,
3165
+ surface: options.surface,
3166
+ initialized: true
3167
+ };
3168
+ } catch (err) {
3169
+ console.warn("[muggle-telemetry] init failed; events will be dropped", {
3170
+ error: err.message
3171
+ });
3172
+ state = {
3173
+ client: null,
3174
+ serviceName: options.serviceName,
3175
+ surface: options.surface,
3176
+ initialized: true
3177
+ };
3178
+ }
3179
+ }
3180
+ function getClient() {
3181
+ return state?.client ?? null;
3182
+ }
3183
+ function prefsFilePath() {
3184
+ return join(getDataDir3(), PREFS_FILENAME);
3185
+ }
3186
+ function readPreferences() {
3187
+ const filePath = prefsFilePath();
3188
+ if (!existsSync(filePath))
3189
+ return {};
3190
+ try {
3191
+ const raw = readFileSync(filePath, "utf8");
3192
+ const parsed = JSON.parse(raw);
3193
+ if (typeof parsed === "object" && parsed !== null) {
3194
+ return parsed;
3195
+ }
3196
+ return {};
3197
+ } catch {
3198
+ return {};
3199
+ }
3200
+ }
3201
+
3202
+ function isTelemetryEnabled() {
3203
+ if (process.env[ENV_TELEMETRY_DISABLED] === "1")
3204
+ return false;
3205
+ const prefs = readPreferences();
3206
+ if (prefs.telemetryEnabled === false)
3207
+ return false;
3208
+ return true;
3209
+ }
3210
+
3211
+ function track(event) {
3212
+ if (!isTelemetryEnabled())
3213
+ return;
3214
+ const client = getClient();
3215
+ if (!client)
3216
+ return;
3217
+ try {
3218
+ client.trackEvent({
3219
+ name: event.name,
3220
+ properties: event.props
3221
+ });
3222
+ } catch {
3223
+ }
3224
+ }
3225
+
3226
+ var Trigger;
3227
+ (function(Trigger2) {
3228
+ Trigger2["UserSlash"] = "user-slash";
3229
+ Trigger2["ClaudeProactive"] = "claude-proactive";
3230
+ Trigger2["NestedSkill"] = "nested-skill";
3231
+ })(Trigger || (Trigger = {}));
3232
+ var Outcome;
3233
+ (function(Outcome2) {
3234
+ Outcome2["Success"] = "success";
3235
+ Outcome2["Error"] = "error";
3236
+ Outcome2["Cancelled"] = "cancelled";
3237
+ })(Outcome || (Outcome = {}));
3238
+ var ToolSurface;
3239
+ (function(ToolSurface2) {
3240
+ ToolSurface2["Local"] = "local";
3241
+ ToolSurface2["Remote"] = "remote";
3242
+ })(ToolSurface || (ToolSurface = {}));
3243
+ var Surface;
3244
+ (function(Surface2) {
3245
+ Surface2["McpLocal"] = "mcp-local";
3246
+ Surface2["McpRemote"] = "mcp-remote";
3247
+ Surface2["Skill"] = "skill";
3248
+ Surface2["Electron"] = "electron";
3249
+ })(Surface || (Surface = {}));
3250
+ var ServiceName;
3251
+ (function(ServiceName2) {
3252
+ ServiceName2["MuggleMcp"] = "muggle-mcp";
3253
+ ServiceName2["MuggleElectron"] = "muggle-electron";
3254
+ ServiceName2["MuggleSkill"] = "muggle-skill";
3255
+ })(ServiceName || (ServiceName = {}));
3256
+ var EventName;
3257
+ (function(EventName2) {
3258
+ EventName2["SkillInvoked"] = "skill.invoked";
3259
+ EventName2["SkillCompleted"] = "skill.completed";
3260
+ EventName2["McpToolInvoked"] = "mcp.tool.invoked";
3261
+ EventName2["McpToolCompleted"] = "mcp.tool.completed";
3262
+ EventName2["SystemStartup"] = "system.startup";
3263
+ EventName2["SystemOptOutChanged"] = "system.opt_out_changed";
3264
+ EventName2["SystemDisclosureShown"] = "system.disclosure_shown";
3265
+ })(EventName || (EventName = {}));
2994
3266
  var MuggleEntityIdSchema = z.string().uuid();
2995
3267
  var LocalExecutionContextInputSchema = z.object({
2996
3268
  originalUrl: z.string().url().describe("Original local URL used during local execution (typically localhost)"),
@@ -3134,6 +3406,21 @@ var UseCaseUpdateFromPromptInputSchema = z.object({
3134
3406
  useCaseId: IdSchema.describe("Use case ID (UUID) to update"),
3135
3407
  instruction: z.string().min(1).describe("Natural language instruction to regenerate the use case from")
3136
3408
  });
3409
+ var UseCaseUpdateInputSchema = z.object({
3410
+ useCaseId: IdSchema.describe("Use case ID (UUID) to update"),
3411
+ title: z.string().min(1).optional().describe("Updated use case title"),
3412
+ description: z.string().min(1).optional().describe("Updated description"),
3413
+ userStory: z.string().min(1).optional().describe("Updated one-line user story"),
3414
+ url: z.string().url().optional().describe("Updated target URL"),
3415
+ useCaseBreakdown: z.array(z.object({
3416
+ requirement: z.string().min(1).describe("One requirement of the use case"),
3417
+ acceptanceCriteria: z.string().min(1).describe("Concrete, measurable acceptance criteria for the requirement")
3418
+ })).optional().describe("Updated main/alternative/error flows as requirement + acceptance criteria pairs"),
3419
+ status: z.enum(["DRAFT", "IN_REVIEW", "APPROVED", "IMPLEMENTED", "ARCHIVED"]).optional().describe("Updated status"),
3420
+ priority: z.enum(["LOW", "MEDIUM", "HIGH", "CRITICAL"]).optional().describe("Updated priority"),
3421
+ source: z.enum(["PRD_FILE", "SITEMAP", "CRAWLER", "PROMPT", "MANUAL"]).optional().describe("Updated source"),
3422
+ category: z.string().optional().describe("Updated category")
3423
+ });
3137
3424
  var UseCaseCreateInputSchema = z.object({
3138
3425
  projectId: IdSchema.describe("Project ID (UUID) the use case belongs to"),
3139
3426
  title: z.string().min(1).describe("Use case title"),
@@ -3217,6 +3504,20 @@ var TestCaseCreateInputSchema = z.object({
3217
3504
  category: z.string().optional().describe("Test case category"),
3218
3505
  automated: z.boolean().optional().describe("Whether this test case is automated (default: true)")
3219
3506
  });
3507
+ var TestCaseUpdateInputSchema = z.object({
3508
+ testCaseId: IdSchema.describe("Test case ID (UUID) to update"),
3509
+ title: z.string().min(1).optional().describe("Updated test case title"),
3510
+ description: z.string().min(1).optional().describe("Updated description"),
3511
+ goal: z.string().min(1).optional().describe("Updated goal"),
3512
+ precondition: z.string().optional().describe("Updated precondition"),
3513
+ expectedResult: z.string().min(1).optional().describe("Updated expected result"),
3514
+ url: z.string().url().optional().describe("Updated target URL"),
3515
+ status: z.enum(["DRAFT", "ACTIVE", "DEPRECATED", "ARCHIVED"]).optional().describe("Updated status"),
3516
+ priority: z.enum(["HIGH", "MEDIUM", "LOW"]).optional().describe("Updated priority"),
3517
+ tags: z.array(z.string()).optional().describe("Updated tags"),
3518
+ category: z.string().optional().describe("Updated category"),
3519
+ automated: z.boolean().optional().describe("Whether this test case is automated")
3520
+ });
3220
3521
  var TestScriptListInputSchema = z.object({
3221
3522
  projectId: IdSchema.describe("Project ID (UUID) to list test scripts for"),
3222
3523
  testCaseId: IdSchema.optional().describe("Optional test case ID (UUID) to filter scripts by")
@@ -3367,6 +3668,34 @@ var ApiKeyGetInputSchema = z.object({
3367
3668
  var ApiKeyRevokeInputSchema = z.object({
3368
3669
  apiKeyId: ApiKeyRecordIdSchema.describe("ID of the API key record to revoke")
3369
3670
  });
3671
+ var UserFeedbackTargetTypeSchema = z.enum(["actionScript", "step"]);
3672
+ var UserFeedbackTargetSchema = z.object({
3673
+ targetType: UserFeedbackTargetTypeSchema.describe("Target entity kind: 'actionScript' for whole-script feedback, 'step' for a specific step"),
3674
+ targetId: z.string().min(1).describe("Target identifier. For 'actionScript' use the actionScriptId; for 'step' use `${actionScriptId}:${stepIndex}` (0-based stepIndex)")
3675
+ });
3676
+ var UserFeedbackCreateInputSchema = z.object({
3677
+ projectId: IdSchema.describe("Project ID (UUID) the feedback belongs to"),
3678
+ target: UserFeedbackTargetSchema.describe("What entity this feedback is about (action script or a specific step)"),
3679
+ feedbackText: z.string().min(1).describe("Free-form paragraph describing what went wrong and what the expected behavior should be")
3680
+ });
3681
+ var UserFeedbackListInputSchema = z.object({
3682
+ projectId: IdSchema.describe("Project ID (UUID) to list feedback for. Always required \u2014 defines the auth boundary."),
3683
+ actionScriptId: IdSchema.optional().describe("Narrow to feedback whose target resolves to this action script. Mutually exclusive with the other narrowing filters."),
3684
+ testScriptId: IdSchema.optional().describe("Narrow to feedback on the action script of this test script. Mutually exclusive with the other narrowing filters."),
3685
+ testCaseId: IdSchema.optional().describe("Narrow to feedback on any action script ever generated for this test case. Mutually exclusive with the other narrowing filters."),
3686
+ useCaseId: IdSchema.optional().describe("Narrow to feedback on any action script ever generated under this use case. Mutually exclusive with the other narrowing filters."),
3687
+ limit: z.number().int().positive().max(1e3).optional().describe("Max entries to return (default 100)"),
3688
+ offset: z.number().int().min(0).optional().describe("Offset for pagination (default 0)")
3689
+ }).refine(
3690
+ (data) => {
3691
+ const set = [data.actionScriptId, data.testScriptId, data.testCaseId, data.useCaseId].filter(Boolean);
3692
+ return set.length <= 1;
3693
+ },
3694
+ { message: "At most one of actionScriptId / testScriptId / testCaseId / useCaseId may be set" }
3695
+ );
3696
+ var UserFeedbackDeleteInputSchema = z.object({
3697
+ feedbackId: IdSchema.describe("User feedback ID (UUID) to soft-delete")
3698
+ });
3370
3699
  var AuthLoginInputSchema = z.object({
3371
3700
  waitForCompletion: z.boolean().optional().describe("Whether to wait for browser login completion before returning. Default: true"),
3372
3701
  timeoutMs: z.number().int().positive().min(1e3).max(9e5).optional().describe("Maximum time to wait for login completion in milliseconds. Default: 120000"),
@@ -3419,40 +3748,40 @@ function detectMcpHost(env = process.env) {
3419
3748
  }
3420
3749
  return "unknown";
3421
3750
  }
3422
- var INSTALL_ID_FILENAME = "install-id.json";
3423
- var cachedInstallId;
3424
- function installIdFilePath() {
3425
- return join(getDataDir(), INSTALL_ID_FILENAME);
3751
+ var INSTALL_ID_FILENAME2 = "install-id.json";
3752
+ var cachedInstallId2;
3753
+ function installIdFilePath2() {
3754
+ return join(getDataDir(), INSTALL_ID_FILENAME2);
3426
3755
  }
3427
- function generateAndPersist(filePath) {
3756
+ function generateAndPersist2(filePath) {
3428
3757
  const installId = randomUUID();
3429
3758
  const contents = { installId };
3430
3759
  mkdirSync(dirname(filePath), { recursive: true });
3431
3760
  writeFileSync(filePath, JSON.stringify(contents, null, 2) + "\n", "utf8");
3432
3761
  return installId;
3433
3762
  }
3434
- function isValid(value) {
3763
+ function isValid3(value) {
3435
3764
  return typeof value === "object" && value !== null && typeof value.installId === "string" && value.installId.length > 0;
3436
3765
  }
3437
- function getInstallId() {
3438
- if (cachedInstallId !== void 0) return cachedInstallId;
3439
- const filePath = installIdFilePath();
3766
+ function getInstallId2() {
3767
+ if (cachedInstallId2 !== void 0) return cachedInstallId2;
3768
+ const filePath = installIdFilePath2();
3440
3769
  if (!existsSync(filePath)) {
3441
- cachedInstallId = generateAndPersist(filePath);
3442
- return cachedInstallId;
3770
+ cachedInstallId2 = generateAndPersist2(filePath);
3771
+ return cachedInstallId2;
3443
3772
  }
3444
3773
  try {
3445
3774
  const raw = readFileSync(filePath, "utf8");
3446
3775
  const parsed = JSON.parse(raw);
3447
- if (isValid(parsed)) {
3448
- cachedInstallId = parsed.installId;
3449
- return cachedInstallId;
3776
+ if (isValid3(parsed)) {
3777
+ cachedInstallId2 = parsed.installId;
3778
+ return cachedInstallId2;
3450
3779
  }
3451
- cachedInstallId = generateAndPersist(filePath);
3452
- return cachedInstallId;
3780
+ cachedInstallId2 = generateAndPersist2(filePath);
3781
+ return cachedInstallId2;
3453
3782
  } catch {
3454
- cachedInstallId = generateAndPersist(filePath);
3455
- return cachedInstallId;
3783
+ cachedInstallId2 = generateAndPersist2(filePath);
3784
+ return cachedInstallId2;
3456
3785
  }
3457
3786
  }
3458
3787
 
@@ -3482,17 +3811,17 @@ var PromptServiceClient = class {
3482
3811
  * @param path - Path to validate.
3483
3812
  * @throws GatewayError if path is not allowed.
3484
3813
  */
3485
- validatePath(path12) {
3486
- const isAllowed = ALLOWED_UPSTREAM_PREFIXES.some((prefix) => path12.startsWith(prefix));
3814
+ validatePath(path15) {
3815
+ const isAllowed = ALLOWED_UPSTREAM_PREFIXES.some((prefix) => path15.startsWith(prefix));
3487
3816
  if (!isAllowed) {
3488
3817
  const logger6 = getLogger();
3489
3818
  logger6.error("Path not in allowlist", {
3490
- path: path12,
3819
+ path: path15,
3491
3820
  allowedPrefixes: ALLOWED_UPSTREAM_PREFIXES
3492
3821
  });
3493
3822
  throw new GatewayError({
3494
3823
  code: "FORBIDDEN" /* FORBIDDEN */,
3495
- message: `Path '${path12}' is not allowed`
3824
+ message: `Path '${path15}' is not allowed`
3496
3825
  });
3497
3826
  }
3498
3827
  }
@@ -3504,7 +3833,7 @@ var PromptServiceClient = class {
3504
3833
  */
3505
3834
  buildHeaders(credentials, correlationId) {
3506
3835
  const manifest = readReleaseManifest();
3507
- const installId = getInstallId();
3836
+ const installId = getInstallId2();
3508
3837
  const host = detectMcpHost();
3509
3838
  const headers = {
3510
3839
  "X-Correlation-Id": correlationId,
@@ -3890,6 +4219,29 @@ var useCaseTools = [
3890
4219
  };
3891
4220
  }
3892
4221
  },
4222
+ {
4223
+ name: "muggle-remote-use-case-update",
4224
+ description: "Directly update an existing use case's fields without invoking the LLM. Pass only the fields you want to change \u2014 others are left untouched. Use this for cheap, deterministic edits (rename, flip status DRAFT\u2192APPROVED, change priority, edit acceptance criteria). For LLM regeneration from a new instruction, use muggle-remote-use-case-update-from-prompt instead. Note: changing title, description, or url triggers background regeneration of dependent test cases on the server.",
4225
+ inputSchema: UseCaseUpdateInputSchema,
4226
+ mapToUpstream: (input) => {
4227
+ const data = input;
4228
+ const body = { id: data.useCaseId };
4229
+ if (data.title !== void 0) body.title = data.title;
4230
+ if (data.description !== void 0) body.description = data.description;
4231
+ if (data.userStory !== void 0) body.userStory = data.userStory;
4232
+ if (data.url !== void 0) body.url = data.url;
4233
+ if (data.useCaseBreakdown !== void 0) body.useCaseBreakdown = data.useCaseBreakdown;
4234
+ if (data.status !== void 0) body.status = data.status;
4235
+ if (data.priority !== void 0) body.priority = data.priority;
4236
+ if (data.source !== void 0) body.source = data.source;
4237
+ if (data.category !== void 0) body.category = data.category;
4238
+ return {
4239
+ method: "PUT",
4240
+ path: `${MUGGLE_TEST_PREFIX}/use-cases/${data.useCaseId}`,
4241
+ body
4242
+ };
4243
+ }
4244
+ },
3893
4245
  {
3894
4246
  name: "muggle-remote-use-case-create",
3895
4247
  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.",
@@ -4012,6 +4364,31 @@ var testCaseTools = [
4012
4364
  };
4013
4365
  }
4014
4366
  },
4367
+ {
4368
+ name: "muggle-remote-test-case-update",
4369
+ description: "Directly update an existing test case's fields. Pass only the fields you want to change \u2014 others are left untouched. Use this for cheap, deterministic edits (rename, change status/priority, fix expected result, add tags). Does not touch the associated test script \u2014 script regeneration is a separate workflow (muggle-remote-workflow-start-test-script-generation).",
4370
+ inputSchema: TestCaseUpdateInputSchema,
4371
+ mapToUpstream: (input) => {
4372
+ const data = input;
4373
+ const body = { id: data.testCaseId };
4374
+ if (data.title !== void 0) body.title = data.title;
4375
+ if (data.description !== void 0) body.description = data.description;
4376
+ if (data.goal !== void 0) body.goal = data.goal;
4377
+ if (data.precondition !== void 0) body.precondition = data.precondition;
4378
+ if (data.expectedResult !== void 0) body.expectedResult = data.expectedResult;
4379
+ if (data.url !== void 0) body.url = data.url;
4380
+ if (data.status !== void 0) body.status = data.status;
4381
+ if (data.priority !== void 0) body.priority = data.priority;
4382
+ if (data.tags !== void 0) body.tags = data.tags;
4383
+ if (data.category !== void 0) body.category = data.category;
4384
+ if (data.automated !== void 0) body.automated = data.automated;
4385
+ return {
4386
+ method: "PUT",
4387
+ path: `${MUGGLE_TEST_PREFIX}/test-cases/${data.testCaseId}`,
4388
+ body
4389
+ };
4390
+ }
4391
+ },
4015
4392
  {
4016
4393
  name: "muggle-remote-test-case-bulk-preview-submit",
4017
4394
  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.",
@@ -4980,6 +5357,58 @@ var apiKeyTools = [
4980
5357
  }
4981
5358
  }
4982
5359
  ];
5360
+ var userFeedbackTools = [
5361
+ {
5362
+ name: "muggle-remote-user-feedback-create",
5363
+ description: "Submit user feedback on a generated action script \u2014 either the whole script (targetType='actionScript', targetId=actionScriptId) or a specific step (targetType='step', targetId=`${actionScriptId}:${stepIndex}` with 0-based stepIndex). Persists the feedback and triggers an async feedback-analysis workflow that may regenerate the script. The response includes the saved feedback and, when the analysis workflow was started, a feedbackAnalysisWorkflowRuntimeId for polling.",
5364
+ inputSchema: UserFeedbackCreateInputSchema,
5365
+ mapToUpstream: (input) => {
5366
+ const data = input;
5367
+ return {
5368
+ method: "POST",
5369
+ path: `${MUGGLE_TEST_PREFIX}/user-feedback`,
5370
+ body: {
5371
+ projectId: data.projectId,
5372
+ target: data.target,
5373
+ feedbackText: data.feedbackText
5374
+ }
5375
+ };
5376
+ }
5377
+ },
5378
+ {
5379
+ name: "muggle-remote-user-feedback-list",
5380
+ description: "List active user feedback entries for a project. Optionally narrow by exactly one of actionScriptId / testScriptId / testCaseId / useCaseId \u2014 the typical query is 'show me feedback on this test case (or test script, or use case).' Supports limit/offset pagination. Response: { feedback: IUserFeedback[], total: number, hasMore: boolean }.",
5381
+ inputSchema: UserFeedbackListInputSchema,
5382
+ mapToUpstream: (input) => {
5383
+ const data = input;
5384
+ return {
5385
+ method: "GET",
5386
+ path: `${MUGGLE_TEST_PREFIX}/user-feedback`,
5387
+ queryParams: {
5388
+ projectId: data.projectId,
5389
+ actionScriptId: data.actionScriptId,
5390
+ testScriptId: data.testScriptId,
5391
+ testCaseId: data.testCaseId,
5392
+ useCaseId: data.useCaseId,
5393
+ limit: data.limit,
5394
+ offset: data.offset
5395
+ }
5396
+ };
5397
+ }
5398
+ },
5399
+ {
5400
+ name: "muggle-remote-user-feedback-delete",
5401
+ description: "Soft-delete a user feedback entry by ID. Returns 204 on success.",
5402
+ inputSchema: UserFeedbackDeleteInputSchema,
5403
+ mapToUpstream: (input) => {
5404
+ const data = input;
5405
+ return {
5406
+ method: "DELETE",
5407
+ path: `${MUGGLE_TEST_PREFIX}/user-feedback/${data.feedbackId}`
5408
+ };
5409
+ }
5410
+ }
5411
+ ];
4983
5412
  var authTools = [
4984
5413
  {
4985
5414
  name: "muggle-remote-auth-status",
@@ -5116,6 +5545,7 @@ var allQaToolDefinitions = [
5116
5545
  ...walletTools,
5117
5546
  ...recommendationTools,
5118
5547
  ...apiKeyTools,
5548
+ ...userFeedbackTools,
5119
5549
  ...authTools
5120
5550
  ];
5121
5551
  function getQaToolByName(name) {
@@ -5133,6 +5563,13 @@ async function executeQaTool(toolName, input, correlationId) {
5133
5563
  isError: true
5134
5564
  };
5135
5565
  }
5566
+ const startTime = Date.now();
5567
+ safeTrack({
5568
+ name: EventName.McpToolInvoked,
5569
+ props: { toolName, toolSurface: ToolSurface.Remote, correlationId }
5570
+ });
5571
+ let outcome = Outcome.Success;
5572
+ let errorCode;
5136
5573
  try {
5137
5574
  const validatedInput = tool.inputSchema.parse(input);
5138
5575
  if (tool.localHandler) {
@@ -5165,7 +5602,9 @@ async function executeQaTool(toolName, input, correlationId) {
5165
5602
  throw error;
5166
5603
  }
5167
5604
  } catch (error) {
5605
+ outcome = Outcome.Error;
5168
5606
  if (error instanceof GatewayError) {
5607
+ errorCode = error.code;
5169
5608
  logger6.warn("Tool call failed with gateway error", {
5170
5609
  tool: toolName,
5171
5610
  code: error.code,
@@ -5176,12 +5615,31 @@ async function executeQaTool(toolName, input, correlationId) {
5176
5615
  isError: true
5177
5616
  };
5178
5617
  }
5618
+ errorCode = error instanceof Error ? error.name : "UnknownError";
5179
5619
  const errorMessage = error instanceof Error ? error.message : String(error);
5180
5620
  logger6.error("Tool call failed", { tool: toolName, error: errorMessage });
5181
5621
  return {
5182
5622
  content: JSON.stringify({ error: "INTERNAL_ERROR", message: errorMessage }),
5183
5623
  isError: true
5184
5624
  };
5625
+ } finally {
5626
+ safeTrack({
5627
+ name: EventName.McpToolCompleted,
5628
+ props: {
5629
+ toolName,
5630
+ toolSurface: ToolSurface.Remote,
5631
+ correlationId,
5632
+ durationMs: Date.now() - startTime,
5633
+ outcome,
5634
+ ...errorCode !== void 0 ? { errorCode } : {}
5635
+ }
5636
+ });
5637
+ }
5638
+ }
5639
+ function safeTrack(event) {
5640
+ try {
5641
+ track(event);
5642
+ } catch {
5185
5643
  }
5186
5644
  }
5187
5645
 
@@ -5240,7 +5698,9 @@ var ExecuteTestGenerationInputSchema = z.object({
5240
5698
  /** Show the electron-app UI during execution. Default: visible window. Pass false to run headless. */
5241
5699
  showUi: z.boolean().optional().describe("Show the electron-app UI during generation. Defaults to visible; pass false to run headless."),
5242
5700
  /** 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. */
5243
- 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.")
5701
+ 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."),
5702
+ /** Mutation parameters providing variable context for this generation run — a string[] where each element is a plain-English instruction or a local file path for uploads (e.g. "Attach the image at C:\\Users\\user\\photo.jpg"). Matches the electron-app's string[] mutation file format. */
5703
+ mutations: z.array(z.string()).optional().describe(`Mutation parameters: string[] where each element is a plain-English instruction or a local file path (e.g. ["The post content should be 'Hello'", "Attach the image at C:\\\\Users\\\\user\\\\photo.jpg"]).`)
5244
5704
  });
5245
5705
  var ExecuteReplayInputSchema = z.object({
5246
5706
  /** Test script metadata from muggle-remote-test-script-get. */
@@ -5254,7 +5714,9 @@ var ExecuteReplayInputSchema = z.object({
5254
5714
  /** Show the electron-app UI during execution. Default: visible window. Pass false to run headless. */
5255
5715
  showUi: z.boolean().optional().describe("Show the electron-app UI during replay. Defaults to visible; pass false to run headless."),
5256
5716
  /** 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. */
5257
- 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.")
5717
+ 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."),
5718
+ /** Mutation parameters passed to the LLM step predictor — a string[] where each element is an instruction describing what varies this run. Supports plain-English instructions (e.g. "The post content should be 'Hello world'") and local file paths for uploads (e.g. "Attach the image at C:\\Users\\user\\photo.jpg"). Matches the electron-app's string[] mutation file format. When provided, the electron app uses LLM-driven step prediction instead of deterministic replay. */
5719
+ mutations: z.array(z.string()).optional().describe(`Mutation parameters: string[] where each element is a plain-English instruction or a local file path (e.g. ["The post content should be 'Hello'", "Attach the image at C:\\\\Users\\\\user\\\\photo.jpg"]).`)
5258
5720
  });
5259
5721
  var CancelExecutionInputSchema = z.object({
5260
5722
  runId: MuggleEntityIdSchema.describe("Run ID (UUID) to cancel")
@@ -5288,6 +5750,7 @@ var EmptyInputSchema2 = z.object({});
5288
5750
  var PreferenceKey = /* @__PURE__ */ ((PreferenceKey2) => {
5289
5751
  PreferenceKey2["AutoLogin"] = "autoLogin";
5290
5752
  PreferenceKey2["AutoSelectProject"] = "autoSelectProject";
5753
+ PreferenceKey2["AutoSelectLocalHost"] = "autoSelectLocalHost";
5291
5754
  PreferenceKey2["ShowElectronBrowser"] = "showElectronBrowser";
5292
5755
  PreferenceKey2["OpenTestResultsAfterRun"] = "openTestResultsAfterRun";
5293
5756
  PreferenceKey2["DefaultExecutionMode"] = "defaultExecutionMode";
@@ -5296,8 +5759,13 @@ var PreferenceKey = /* @__PURE__ */ ((PreferenceKey2) => {
5296
5759
  PreferenceKey2["SuggestRelatedTestCases"] = "suggestRelatedTestCases";
5297
5760
  PreferenceKey2["AutoDetectChanges"] = "autoDetectChanges";
5298
5761
  PreferenceKey2["PostPRVisualWalkthrough"] = "postPRVisualWalkthrough";
5762
+ PreferenceKey2["AutoCreatePR"] = "autoCreatePR";
5299
5763
  PreferenceKey2["CheckForUpdates"] = "checkForUpdates";
5300
5764
  PreferenceKey2["VerboseOutput"] = "verboseOutput";
5765
+ PreferenceKey2["AutoUseWorktree"] = "autoUseWorktree";
5766
+ PreferenceKey2["AutoRebase"] = "autoRebase";
5767
+ PreferenceKey2["AutoCleanup"] = "autoCleanup";
5768
+ PreferenceKey2["AutoE2ETest"] = "autoE2ETest";
5301
5769
  return PreferenceKey2;
5302
5770
  })(PreferenceKey || {});
5303
5771
  var PreferenceValue = /* @__PURE__ */ ((PreferenceValue2) => {
@@ -5316,6 +5784,7 @@ var PREFERENCES_VERSION = 1;
5316
5784
  var DEFAULT_PREFERENCES = {
5317
5785
  ["autoLogin" /* AutoLogin */]: "ask" /* Ask */,
5318
5786
  ["autoSelectProject" /* AutoSelectProject */]: "ask" /* Ask */,
5787
+ ["autoSelectLocalHost" /* AutoSelectLocalHost */]: "ask" /* Ask */,
5319
5788
  ["showElectronBrowser" /* ShowElectronBrowser */]: "ask" /* Ask */,
5320
5789
  ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: "ask" /* Ask */,
5321
5790
  ["defaultExecutionMode" /* DefaultExecutionMode */]: "ask" /* Ask */,
@@ -5324,14 +5793,23 @@ var DEFAULT_PREFERENCES = {
5324
5793
  ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: "ask" /* Ask */,
5325
5794
  ["autoDetectChanges" /* AutoDetectChanges */]: "ask" /* Ask */,
5326
5795
  ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: "ask" /* Ask */,
5796
+ ["autoCreatePR" /* AutoCreatePR */]: "ask" /* Ask */,
5327
5797
  ["checkForUpdates" /* CheckForUpdates */]: "ask" /* Ask */,
5328
- ["verboseOutput" /* VerboseOutput */]: "ask" /* Ask */
5798
+ ["verboseOutput" /* VerboseOutput */]: "ask" /* Ask */,
5799
+ ["autoUseWorktree" /* AutoUseWorktree */]: "ask" /* Ask */,
5800
+ ["autoRebase" /* AutoRebase */]: "ask" /* Ask */,
5801
+ ["autoCleanup" /* AutoCleanup */]: "ask" /* Ask */,
5802
+ ["autoE2ETest" /* AutoE2ETest */]: "always" /* Always */
5329
5803
  };
5330
5804
  var ALWAYS_ASK_NEVER = [
5331
5805
  "always" /* Always */,
5332
5806
  "ask" /* Ask */,
5333
5807
  "never" /* Never */
5334
5808
  ];
5809
+ var ALWAYS_ASK = [
5810
+ "always" /* Always */,
5811
+ "ask" /* Ask */
5812
+ ];
5335
5813
  var LOCAL_REMOTE_ASK = [
5336
5814
  "local" /* Local */,
5337
5815
  "remote" /* Remote */,
@@ -5340,6 +5818,7 @@ var LOCAL_REMOTE_ASK = [
5340
5818
  var PREFERENCE_ALLOWED_VALUES = {
5341
5819
  ["autoLogin" /* AutoLogin */]: ALWAYS_ASK_NEVER,
5342
5820
  ["autoSelectProject" /* AutoSelectProject */]: ALWAYS_ASK_NEVER,
5821
+ ["autoSelectLocalHost" /* AutoSelectLocalHost */]: ALWAYS_ASK_NEVER,
5343
5822
  ["showElectronBrowser" /* ShowElectronBrowser */]: ALWAYS_ASK_NEVER,
5344
5823
  ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: ALWAYS_ASK_NEVER,
5345
5824
  ["defaultExecutionMode" /* DefaultExecutionMode */]: LOCAL_REMOTE_ASK,
@@ -5348,27 +5827,35 @@ var PREFERENCE_ALLOWED_VALUES = {
5348
5827
  ["suggestRelatedTestCases" /* SuggestRelatedTestCases */]: ALWAYS_ASK_NEVER,
5349
5828
  ["autoDetectChanges" /* AutoDetectChanges */]: ALWAYS_ASK_NEVER,
5350
5829
  ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: ALWAYS_ASK_NEVER,
5830
+ ["autoCreatePR" /* AutoCreatePR */]: ALWAYS_ASK_NEVER,
5351
5831
  ["checkForUpdates" /* CheckForUpdates */]: ALWAYS_ASK_NEVER,
5352
- ["verboseOutput" /* VerboseOutput */]: ALWAYS_ASK_NEVER
5832
+ ["verboseOutput" /* VerboseOutput */]: ALWAYS_ASK_NEVER,
5833
+ ["autoUseWorktree" /* AutoUseWorktree */]: ALWAYS_ASK_NEVER,
5834
+ ["autoRebase" /* AutoRebase */]: ALWAYS_ASK_NEVER,
5835
+ ["autoCleanup" /* AutoCleanup */]: ALWAYS_ASK_NEVER,
5836
+ ["autoE2ETest" /* AutoE2ETest */]: ALWAYS_ASK
5353
5837
  };
5354
5838
  var PREFERENCES_SCHEMA = {
5355
5839
  ["autoLogin" /* AutoLogin */]: {
5356
5840
  description: "When a tool requires auth and saved credentials exist, reuse them without prompting"
5357
5841
  },
5358
5842
  ["autoSelectProject" /* AutoSelectProject */]: {
5359
- description: "When a skill needs a Muggle project and one was used previously in this repo, reuse it without prompting"
5843
+ description: "When a skill needs a Muggle Test project and one was used previously in this repo, reuse it without prompting"
5844
+ },
5845
+ ["autoSelectLocalHost" /* AutoSelectLocalHost */]: {
5846
+ description: "When running local tests, reuse the dev server URL from the previous run in this repo without prompting"
5360
5847
  },
5361
5848
  ["showElectronBrowser" /* ShowElectronBrowser */]: {
5362
5849
  description: "When running local E2E tests, show the Electron browser window"
5363
5850
  },
5364
5851
  ["openTestResultsAfterRun" /* OpenTestResultsAfterRun */]: {
5365
- description: "After a local E2E test run completes, open the per-run results page on Muggle dashboard"
5852
+ description: "After a local E2E test run completes, open the per-run results page on Muggle Test dashboard"
5366
5853
  },
5367
5854
  ["defaultExecutionMode" /* DefaultExecutionMode */]: {
5368
5855
  description: "When a skill supports both local and remote test execution, which to default to"
5369
5856
  },
5370
5857
  ["autoPublishLocalResults" /* AutoPublishLocalResults */]: {
5371
- description: "After a local E2E test run completes, upload results to Muggle cloud for team visibility"
5858
+ description: "After a local E2E test run completes, upload results to Muggle Test cloud for team visibility"
5372
5859
  },
5373
5860
  ["suggestRelatedUseCases" /* SuggestRelatedUseCases */]: {
5374
5861
  description: "After creating or running a use case, suggest related use cases to add"
@@ -5382,11 +5869,26 @@ var PREFERENCES_SCHEMA = {
5382
5869
  ["postPRVisualWalkthrough" /* PostPRVisualWalkthrough */]: {
5383
5870
  description: "After test results are available, post visual walkthrough comment with screenshots to the PR"
5384
5871
  },
5872
+ ["autoCreatePR" /* AutoCreatePR */]: {
5873
+ description: "At the end of the muggle-do dev cycle (or when a test needs an open PR to post results to), push the branch and open a PR without prompting"
5874
+ },
5385
5875
  ["checkForUpdates" /* CheckForUpdates */]: {
5386
- description: "At session start, check if a newer Muggle version is available and notify"
5876
+ description: "At session start, check if a newer Muggle Test version is available and notify"
5387
5877
  },
5388
5878
  ["verboseOutput" /* VerboseOutput */]: {
5389
5879
  description: "Show detailed progress logs during skill and tool execution"
5880
+ },
5881
+ ["autoUseWorktree" /* AutoUseWorktree */]: {
5882
+ description: "When starting non-trivial development work, create a git worktree to isolate the change from the current checkout"
5883
+ },
5884
+ ["autoRebase" /* AutoRebase */]: {
5885
+ description: "Before running dev servers or E2E tests on a branch behind origin, rebase onto the default branch first"
5886
+ },
5887
+ ["autoCleanup" /* AutoCleanup */]: {
5888
+ description: "After a PR is merged, automatically run the cleanup sequence: remove worktree, delete branches, clear local artifacts, prune [gone] branches"
5889
+ },
5890
+ ["autoE2ETest" /* AutoE2ETest */]: {
5891
+ description: "Run Stage 6 (E2E acceptance) at the end of every /muggle-do cycle (default always \u2014 running E2E is the point of muggle-do; never is not an option)"
5390
5892
  }
5391
5893
  };
5392
5894
 
@@ -5413,15 +5915,75 @@ var LastProjectGetInputSchema = z.object({
5413
5915
  });
5414
5916
  var LastProjectSetInputSchema = z.object({
5415
5917
  cwd: z.string().describe("Project root directory to save the cached project to."),
5416
- projectId: z.string().min(1).describe("Muggle project ID."),
5417
- projectUrl: z.string().min(1).describe("Muggle project URL."),
5418
- projectName: z.string().min(1).describe("Muggle project name.")
5918
+ projectId: z.string().min(1).describe("Muggle Test project ID."),
5919
+ projectUrl: z.string().min(1).describe("Muggle Test project URL."),
5920
+ projectName: z.string().min(1).describe("Muggle Test project name.")
5419
5921
  });
5420
5922
  var LastProjectClearInputSchema = z.object({
5421
5923
  cwd: z.string().describe("Project root directory whose last-used project cache to remove.")
5422
5924
  });
5423
- function isFirstRun(dataDirOverride) {
5424
- const filePath = path2.join(dataDirOverride ?? getDataDir(), PREFERENCES_FILE_NAME);
5925
+ var LastHostGetInputSchema = z.object({
5926
+ cwd: z.string().describe("Project root directory whose last-used host to return.")
5927
+ });
5928
+ var LastHostSetInputSchema = z.object({
5929
+ cwd: z.string().describe("Project root directory to save the cached host to."),
5930
+ host: z.string().min(1).describe("Local dev server URL (e.g. http://localhost:3000).")
5931
+ });
5932
+ var LastHostClearInputSchema = z.object({
5933
+ cwd: z.string().describe("Project root directory whose last-used host cache to remove.")
5934
+ });
5935
+ var SkillTelemetryEmitInputSchema = z.object({
5936
+ skillName: z.string().min(1).describe("Name of the skill being invoked, e.g. 'muggle-test'."),
5937
+ trigger: z.nativeEnum(Trigger).default(Trigger.UserSlash).describe("Why this skill ran. Defaults to 'user-slash' (user typed /skill-name).")
5938
+ });
5939
+ var FailureEventType = /* @__PURE__ */ ((FailureEventType2) => {
5940
+ FailureEventType2["PreExecutionClassification"] = "pre-execution-classification";
5941
+ FailureEventType2["ReplayFailureClassified"] = "replay-failure-classified";
5942
+ FailureEventType2["ReplayFailureResolved"] = "replay-failure-resolved";
5943
+ FailureEventType2["RegenFailureClassified"] = "regen-failure-classified";
5944
+ FailureEventType2["RegenFailureResolved"] = "regen-failure-resolved";
5945
+ return FailureEventType2;
5946
+ })(FailureEventType || {});
5947
+ var EventTelemetryEmitInputSchema = z.object({
5948
+ eventType: z.nativeEnum(FailureEventType).describe(
5949
+ "Which decision point this event records. Five values cover the AI's pre-execution replay-vs-regen choice and the four post-failure classify\u2192resolve pairs."
5950
+ ),
5951
+ skillName: z.string().min(1).describe("Skill that emitted this event (e.g. 'muggle-test', 'muggle-test-feature-local')."),
5952
+ aiClassification: z.string().optional().describe(
5953
+ "What the AI classified the situation as. pre-execution: 'replay' or 'regen'. replay-failure: 'infra' | 'stale-script' | 'product-defect'. regen-failure: 'transient' | 'infra' | 'agent-course' | 'product-uxux'."
5954
+ ),
5955
+ aiSuggestion: z.string().optional().describe(
5956
+ "What the AI suggested the user do. Examples: 'regenerate', 'report-bug', 'muggle-feedback', 'retry', 'wait-and-share'."
5957
+ ),
5958
+ userAction: z.string().optional().describe(
5959
+ "What the user actually picked (only set on *-resolved events). Compare against aiSuggestion to measure classification accuracy."
5960
+ ),
5961
+ runId: z.string().optional().describe("Anchor: local run ID for the affected execution."),
5962
+ testCaseId: z.string().optional().describe("Anchor: cloud test case ID."),
5963
+ projectId: z.string().optional().describe("Anchor: cloud project ID."),
5964
+ signals: z.array(z.string()).optional().describe(
5965
+ "Signals that drove the classification, e.g. ['element-not-found', 'selector-timeout', 'electron-exit-26', 'goal-not-achievable']. Used to refine the rules later."
5966
+ ),
5967
+ metadata: z.record(z.string(), z.unknown()).optional().describe("Free-form additional context (e.g. error excerpt, last step name).")
5968
+ });
5969
+ var TELEMETRY_DIR_NAME = "telemetry";
5970
+ var FAILURE_EVENTS_FILE_NAME = "failure-events.jsonl";
5971
+ function appendFailureEvent(record) {
5972
+ try {
5973
+ const dir = path2.join(getDataDir(), TELEMETRY_DIR_NAME);
5974
+ fs5.mkdirSync(dir, { recursive: true });
5975
+ const filePath = path2.join(dir, FAILURE_EVENTS_FILE_NAME);
5976
+ const full = { ts: (/* @__PURE__ */ new Date()).toISOString(), ...record };
5977
+ fs5.appendFileSync(filePath, `${JSON.stringify(full)}
5978
+ `, "utf-8");
5979
+ } catch (error) {
5980
+ getLogger().warn("Failed to append failure event", {
5981
+ error: error instanceof Error ? error.message : String(error)
5982
+ });
5983
+ }
5984
+ }
5985
+ function isFirstRun(dataDirOverride2) {
5986
+ const filePath = path2.join(dataDirOverride2 ?? getDataDir(), PREFERENCES_FILE_NAME);
5425
5987
  return !fs5.existsSync(filePath);
5426
5988
  }
5427
5989
  function readPreferencesFile(filePath) {
@@ -5439,8 +6001,8 @@ function readPreferencesFile(filePath) {
5439
6001
  return {};
5440
6002
  }
5441
6003
  }
5442
- function readGlobalPreferences(dataDirOverride) {
5443
- const filePath = path2.join(dataDirOverride ?? getDataDir(), PREFERENCES_FILE_NAME);
6004
+ function readGlobalPreferences(dataDirOverride2) {
6005
+ const filePath = path2.join(dataDirOverride2 ?? getDataDir(), PREFERENCES_FILE_NAME);
5444
6006
  const partial = readPreferencesFile(filePath);
5445
6007
  return { ...DEFAULT_PREFERENCES, ...partial };
5446
6008
  }
@@ -5448,16 +6010,16 @@ function readProjectPreferences(cwd) {
5448
6010
  const filePath = path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_FILE_NAME);
5449
6011
  return readPreferencesFile(filePath);
5450
6012
  }
5451
- function resolvePreferences(dataDirOverride, cwd) {
5452
- const global = readGlobalPreferences(dataDirOverride);
6013
+ function resolvePreferences(dataDirOverride2, cwd) {
6014
+ const global = readGlobalPreferences(dataDirOverride2);
5453
6015
  if (!cwd) {
5454
6016
  return global;
5455
6017
  }
5456
6018
  const project = readProjectPreferences(cwd);
5457
6019
  return { ...global, ...project };
5458
6020
  }
5459
- function writePreferences(prefs, scope, dataDirOverride, cwd) {
5460
- const dir = scope === "project" && cwd ? path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride ?? getDataDir();
6021
+ function writePreferences(prefs, scope, dataDirOverride2, cwd) {
6022
+ const dir = scope === "project" && cwd ? path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride2 ?? getDataDir();
5461
6023
  fs5.mkdirSync(dir, { recursive: true });
5462
6024
  const filePath = path2.join(dir, PREFERENCES_FILE_NAME);
5463
6025
  const existing = readPreferencesFile(filePath);
@@ -5469,8 +6031,8 @@ function writePreferences(prefs, scope, dataDirOverride, cwd) {
5469
6031
  fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
5470
6032
  `, "utf-8");
5471
6033
  }
5472
- function resetPreference(key, scope, dataDirOverride, cwd) {
5473
- const dir = scope === "project" && cwd ? path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride ?? getDataDir();
6034
+ function resetPreference(key, scope, dataDirOverride2, cwd) {
6035
+ const dir = scope === "project" && cwd ? path2.join(cwd, PREFERENCES_PROJECT_DIR_NAME) : dataDirOverride2 ?? getDataDir();
5474
6036
  const filePath = path2.join(dir, PREFERENCES_FILE_NAME);
5475
6037
  if (!fs5.existsSync(filePath)) {
5476
6038
  return;
@@ -5534,6 +6096,42 @@ function clearLastProject(cwd) {
5534
6096
  fs5.unlinkSync(filePath);
5535
6097
  }
5536
6098
  }
6099
+ var LAST_HOST_FILE_NAME = "last-host.json";
6100
+ var LAST_HOST_DIR_NAME = ".muggle-ai";
6101
+ var LAST_HOST_VERSION = 1;
6102
+ function readLastHost(cwd) {
6103
+ const filePath = path2.join(cwd, LAST_HOST_DIR_NAME, LAST_HOST_FILE_NAME);
6104
+ try {
6105
+ if (!fs5.existsSync(filePath)) {
6106
+ return null;
6107
+ }
6108
+ const raw = JSON.parse(fs5.readFileSync(filePath, "utf-8"));
6109
+ return raw.lastHost ?? null;
6110
+ } catch (error) {
6111
+ getLogger().warn("Failed to read last-host file", {
6112
+ path: filePath,
6113
+ error: error instanceof Error ? error.message : String(error)
6114
+ });
6115
+ return null;
6116
+ }
6117
+ }
6118
+ function writeLastHost(cwd, host) {
6119
+ const dir = path2.join(cwd, LAST_HOST_DIR_NAME);
6120
+ fs5.mkdirSync(dir, { recursive: true });
6121
+ const filePath = path2.join(dir, LAST_HOST_FILE_NAME);
6122
+ const file = {
6123
+ version: LAST_HOST_VERSION,
6124
+ lastHost: { host, savedAt: (/* @__PURE__ */ new Date()).toISOString() }
6125
+ };
6126
+ fs5.writeFileSync(filePath, `${JSON.stringify(file, null, 2)}
6127
+ `, "utf-8");
6128
+ }
6129
+ function clearLastHost(cwd) {
6130
+ const filePath = path2.join(cwd, LAST_HOST_DIR_NAME, LAST_HOST_FILE_NAME);
6131
+ if (fs5.existsSync(filePath)) {
6132
+ fs5.unlinkSync(filePath);
6133
+ }
6134
+ }
5537
6135
 
5538
6136
  // packages/mcps/src/mcp/tools/local/tool-registry.ts
5539
6137
  function createChildLogger2(correlationId) {
@@ -5757,6 +6355,7 @@ var executeTestGenerationTool = {
5757
6355
  const result = await executeTestGeneration({
5758
6356
  testCase: input.testCase,
5759
6357
  localUrl: input.localUrl,
6358
+ mutations: input.mutations,
5760
6359
  timeoutMs: input.timeoutMs,
5761
6360
  showUi,
5762
6361
  freshSession: input.freshSession
@@ -5797,6 +6396,7 @@ var executeReplayTool = {
5797
6396
  testScript: input.testScript,
5798
6397
  actionScript: input.actionScript,
5799
6398
  localUrl: input.localUrl,
6399
+ mutations: input.mutations,
5800
6400
  timeoutMs: input.timeoutMs,
5801
6401
  showUi,
5802
6402
  freshSession: input.freshSession
@@ -5990,22 +6590,15 @@ var preferencesSetTool = {
5990
6590
  void 0,
5991
6591
  input.cwd
5992
6592
  );
5993
- const resolved = resolvePreferences(void 0, input.cwd);
5994
- const oneLiner = formatPreferencesOneLiner(resolved);
5995
- const content = [
5996
- `**${input.key}** set to **${input.value}** (${input.scope}).`,
5997
- "",
5998
- `Preferences file: ~/.muggle-ai/${PREFERENCES_FILE_NAME}`,
5999
- "",
6000
- "Current resolved preferences:",
6001
- `\`${oneLiner}\``
6002
- ].join("\n");
6003
- return { content, isError: false };
6593
+ return {
6594
+ content: `**${input.key}** set to **${input.value}** (${input.scope}).`,
6595
+ isError: false
6596
+ };
6004
6597
  }
6005
6598
  };
6006
6599
  var lastProjectGetTool = {
6007
6600
  name: "muggle-local-last-project-get",
6008
- description: "Get the cached last-used Muggle project for a repo (read from <cwd>/.muggle-ai/last-project.json). Returns the project ID, URL, name, and saved-at timestamp, or null if no cache exists. Skills consult this when 'autoSelectProject = always' to silently reuse the project the user picked previously, instead of presenting the project picker every time.",
6601
+ description: "Get the cached last-used Muggle Test project for a repo (read from <cwd>/.muggle-ai/last-project.json). Returns the project ID, URL, name, and saved-at timestamp, or null if no cache exists. Skills consult this when 'autoSelectProject = always' to silently reuse the project the user picked previously, instead of presenting the project picker every time.",
6009
6602
  inputSchema: LastProjectGetInputSchema,
6010
6603
  execute: async (ctx) => {
6011
6604
  const logger6 = createChildLogger2(ctx.correlationId);
@@ -6035,7 +6628,7 @@ var lastProjectGetTool = {
6035
6628
  };
6036
6629
  var lastProjectSetTool = {
6037
6630
  name: "muggle-local-last-project-set",
6038
- description: "Save the user's selected Muggle project as the cached last-used project for this repo. Writes to <cwd>/.muggle-ai/last-project.json. Subsequent skill invocations honor 'autoSelectProject = always' by silently reusing this entry \u2014 no project picker shown. Always pair this call with 'muggle-local-preferences-set autoSelectProject=always' when the user chose 'Yes, save it'.",
6631
+ description: "Save the user's selected Muggle Test project as the cached last-used project for this repo. Writes to <cwd>/.muggle-ai/last-project.json. Subsequent skill invocations honor 'autoSelectProject = always' by silently reusing this entry \u2014 no project picker shown. Always pair this call with 'muggle-local-preferences-set autoSelectProject=always' when the user chose 'Yes, save it'.",
6039
6632
  inputSchema: LastProjectSetInputSchema,
6040
6633
  execute: async (ctx) => {
6041
6634
  const logger6 = createChildLogger2(ctx.correlationId);
@@ -6073,6 +6666,96 @@ var lastProjectClearTool = {
6073
6666
  return { content, isError: false };
6074
6667
  }
6075
6668
  };
6669
+ var lastHostGetTool = {
6670
+ name: "muggle-local-last-host-get",
6671
+ description: "Get the cached last-used local dev server URL for a repo (read from <cwd>/.muggle-ai/last-host.json). Returns the URL and saved-at timestamp, or null if no cache exists. Skills consult this when 'autoSelectLocalHost = always' to silently reuse the URL the user used previously.",
6672
+ inputSchema: LastHostGetInputSchema,
6673
+ execute: async (ctx) => {
6674
+ const logger6 = createChildLogger2(ctx.correlationId);
6675
+ logger6.info("Executing muggle-local-last-host-get");
6676
+ const input = LastHostGetInputSchema.parse(ctx.input);
6677
+ const cached = readLastHost(input.cwd);
6678
+ if (!cached) {
6679
+ const content2 = [
6680
+ "No cached last-used host for this repo.",
6681
+ "",
6682
+ `Looked at: \`${input.cwd}/.muggle-ai/${LAST_HOST_FILE_NAME}\``
6683
+ ].join("\n");
6684
+ return { content: content2, isError: false };
6685
+ }
6686
+ const content = [
6687
+ "**Cached last-used host:**",
6688
+ "",
6689
+ `- URL: ${cached.host}`,
6690
+ `- Saved at: ${cached.savedAt}`
6691
+ ].join("\n");
6692
+ return { content, isError: false };
6693
+ }
6694
+ };
6695
+ var lastHostSetTool = {
6696
+ name: "muggle-local-last-host-set",
6697
+ description: "Save the user's chosen local dev server URL as the cached last-used host for this repo. Writes to <cwd>/.muggle-ai/last-host.json. Call this on every host pick (independent of 'Remember this URL?' Picker 2) so future runs can offer 'Use {lastHost}' regardless of whether the user opted to set autoSelectLocalHost=always.",
6698
+ inputSchema: LastHostSetInputSchema,
6699
+ execute: async (ctx) => {
6700
+ const logger6 = createChildLogger2(ctx.correlationId);
6701
+ logger6.info("Executing muggle-local-last-host-set");
6702
+ const input = LastHostSetInputSchema.parse(ctx.input);
6703
+ writeLastHost(input.cwd, input.host);
6704
+ return {
6705
+ content: `Cached **${input.host}** as the last-used host for this repo.`,
6706
+ isError: false
6707
+ };
6708
+ }
6709
+ };
6710
+ var lastHostClearTool = {
6711
+ name: "muggle-local-last-host-clear",
6712
+ description: "Remove the cached last-used host for this repo. After this, `autoSelectLocalHost = always` will fall through to ask until the user picks a new URL. No-op if no cache exists.",
6713
+ inputSchema: LastHostClearInputSchema,
6714
+ execute: async (ctx) => {
6715
+ const logger6 = createChildLogger2(ctx.correlationId);
6716
+ logger6.info("Executing muggle-local-last-host-clear");
6717
+ const input = LastHostClearInputSchema.parse(ctx.input);
6718
+ clearLastHost(input.cwd);
6719
+ return {
6720
+ content: `Cleared the cached last-used host for this repo.`,
6721
+ isError: false
6722
+ };
6723
+ }
6724
+ };
6725
+ var telemetrySkillEmitTool = {
6726
+ name: "muggle-local-telemetry-skill-emit",
6727
+ description: "Emit a client telemetry event recording that a Muggle Test skill was invoked. Each muggle-* skill calls this in its first step so we can measure skill usage. Never fails the skill on telemetry errors.",
6728
+ inputSchema: SkillTelemetryEmitInputSchema,
6729
+ execute: async (ctx) => {
6730
+ const input = SkillTelemetryEmitInputSchema.parse(ctx.input);
6731
+ safeTrack2({
6732
+ name: EventName.SkillInvoked,
6733
+ props: { skillName: input.skillName, trigger: input.trigger }
6734
+ });
6735
+ return { content: "ok", isError: false, data: { recorded: true } };
6736
+ }
6737
+ };
6738
+ var telemetryEventEmitTool = {
6739
+ name: "muggle-local-telemetry-event-emit",
6740
+ description: "Emit a structured failure-mode telemetry event recording how a Muggle Test skill classified a situation and what action the user took. Used at five decision points: 'pre-execution-classification' (replay vs regen), 'replay-failure-classified', 'replay-failure-resolved', 'regen-failure-classified', 'regen-failure-resolved'. Records to ~/.muggle-ai/telemetry/failure-events.jsonl. Never fails the skill on telemetry errors.",
6741
+ inputSchema: EventTelemetryEmitInputSchema,
6742
+ execute: async (ctx) => {
6743
+ const input = EventTelemetryEmitInputSchema.parse(ctx.input);
6744
+ appendFailureEvent({
6745
+ eventType: input.eventType,
6746
+ skillName: input.skillName,
6747
+ aiClassification: input.aiClassification,
6748
+ aiSuggestion: input.aiSuggestion,
6749
+ userAction: input.userAction,
6750
+ runId: input.runId,
6751
+ testCaseId: input.testCaseId,
6752
+ projectId: input.projectId,
6753
+ signals: input.signals,
6754
+ metadata: input.metadata
6755
+ });
6756
+ return { content: "ok", isError: false, data: { recorded: true } };
6757
+ }
6758
+ };
6076
6759
  var allLocalQaTools = [
6077
6760
  // Status tools
6078
6761
  checkStatusTool,
@@ -6091,10 +6774,17 @@ var allLocalQaTools = [
6091
6774
  publishTestScriptTool,
6092
6775
  // Preferences tools
6093
6776
  preferencesSetTool,
6094
- // Last-project cache tools (per-repo "last used Muggle project")
6777
+ // Last-project cache tools (per-repo "last used Muggle Test project")
6095
6778
  lastProjectGetTool,
6096
6779
  lastProjectSetTool,
6097
- lastProjectClearTool
6780
+ lastProjectClearTool,
6781
+ // Last-host cache tools (per-repo "last used local dev server URL")
6782
+ lastHostGetTool,
6783
+ lastHostSetTool,
6784
+ lastHostClearTool,
6785
+ // Client telemetry: skills emit invocation events through this tool
6786
+ telemetrySkillEmitTool,
6787
+ telemetryEventEmitTool
6098
6788
  ];
6099
6789
  var toolMap = new Map(
6100
6790
  allLocalQaTools.map((tool) => [tool.name, tool])
@@ -6110,7 +6800,44 @@ async function executeTool(name, input, correlationId) {
6110
6800
  isError: true
6111
6801
  };
6112
6802
  }
6113
- return tool.execute({ input, correlationId });
6803
+ const startTime = Date.now();
6804
+ safeTrack2({
6805
+ name: EventName.McpToolInvoked,
6806
+ props: { toolName: name, toolSurface: ToolSurface.Local, correlationId }
6807
+ });
6808
+ try {
6809
+ const result = await tool.execute({ input, correlationId });
6810
+ safeTrack2({
6811
+ name: EventName.McpToolCompleted,
6812
+ props: {
6813
+ toolName: name,
6814
+ toolSurface: ToolSurface.Local,
6815
+ correlationId,
6816
+ durationMs: Date.now() - startTime,
6817
+ outcome: result.isError ? Outcome.Error : Outcome.Success
6818
+ }
6819
+ });
6820
+ return result;
6821
+ } catch (err) {
6822
+ safeTrack2({
6823
+ name: EventName.McpToolCompleted,
6824
+ props: {
6825
+ toolName: name,
6826
+ toolSurface: ToolSurface.Local,
6827
+ correlationId,
6828
+ durationMs: Date.now() - startTime,
6829
+ outcome: Outcome.Error,
6830
+ errorCode: err instanceof Error ? err.name : "UnknownError"
6831
+ }
6832
+ });
6833
+ throw err;
6834
+ }
6835
+ }
6836
+ function safeTrack2(event) {
6837
+ try {
6838
+ track(event);
6839
+ } catch {
6840
+ }
6114
6841
  }
6115
6842
 
6116
6843
  // packages/mcps/src/mcp/e2e/index.ts
@@ -6172,6 +6899,7 @@ __export(e2e_exports2, {
6172
6899
  TestCaseGetInputSchema: () => TestCaseGetInputSchema,
6173
6900
  TestCaseListByUseCaseInputSchema: () => TestCaseListByUseCaseInputSchema,
6174
6901
  TestCaseListInputSchema: () => TestCaseListInputSchema,
6902
+ TestCaseUpdateInputSchema: () => TestCaseUpdateInputSchema,
6175
6903
  TestScriptGetInputSchema: () => TestScriptGetInputSchema,
6176
6904
  TestScriptListInputSchema: () => TestScriptListInputSchema,
6177
6905
  TokenPackageIdSchema: () => TokenPackageIdSchema,
@@ -6184,6 +6912,12 @@ __export(e2e_exports2, {
6184
6912
  UseCaseListInputSchema: () => UseCaseListInputSchema,
6185
6913
  UseCasePromptPreviewInputSchema: () => UseCasePromptPreviewInputSchema,
6186
6914
  UseCaseUpdateFromPromptInputSchema: () => UseCaseUpdateFromPromptInputSchema,
6915
+ UseCaseUpdateInputSchema: () => UseCaseUpdateInputSchema,
6916
+ UserFeedbackCreateInputSchema: () => UserFeedbackCreateInputSchema,
6917
+ UserFeedbackDeleteInputSchema: () => UserFeedbackDeleteInputSchema,
6918
+ UserFeedbackListInputSchema: () => UserFeedbackListInputSchema,
6919
+ UserFeedbackTargetSchema: () => UserFeedbackTargetSchema,
6920
+ UserFeedbackTargetTypeSchema: () => UserFeedbackTargetTypeSchema,
6187
6921
  WalletAutoTopUpSetPaymentMethodInputSchema: () => WalletAutoTopUpSetPaymentMethodInputSchema,
6188
6922
  WalletAutoTopUpUpdateInputSchema: () => WalletAutoTopUpUpdateInputSchema,
6189
6923
  WalletPaymentMethodCreateSetupSessionInputSchema: () => WalletPaymentMethodCreateSetupSessionInputSchema,
@@ -6230,10 +6964,15 @@ __export(local_exports2, {
6230
6964
  CloudMappingEntityType: () => CloudMappingEntityType,
6231
6965
  DeviceCodePollStatus: () => DeviceCodePollStatus,
6232
6966
  EmptyInputSchema: () => EmptyInputSchema2,
6967
+ EventTelemetryEmitInputSchema: () => EventTelemetryEmitInputSchema,
6233
6968
  ExecuteReplayInputSchema: () => ExecuteReplayInputSchema,
6234
6969
  ExecuteTestGenerationInputSchema: () => ExecuteTestGenerationInputSchema,
6235
6970
  ExecutionStatus: () => ExecutionStatus,
6971
+ FailureEventType: () => FailureEventType,
6236
6972
  HealthStatus: () => HealthStatus,
6973
+ LastHostClearInputSchema: () => LastHostClearInputSchema,
6974
+ LastHostGetInputSchema: () => LastHostGetInputSchema,
6975
+ LastHostSetInputSchema: () => LastHostSetInputSchema,
6237
6976
  LastProjectClearInputSchema: () => LastProjectClearInputSchema,
6238
6977
  LastProjectGetInputSchema: () => LastProjectGetInputSchema,
6239
6978
  LastProjectSetInputSchema: () => LastProjectSetInputSchema,
@@ -6250,6 +6989,7 @@ __export(local_exports2, {
6250
6989
  RunResultListInputSchema: () => RunResultListInputSchema,
6251
6990
  RunResultStorageService: () => RunResultStorageService,
6252
6991
  SessionStatus: () => SessionStatus,
6992
+ SkillTelemetryEmitInputSchema: () => SkillTelemetryEmitInputSchema,
6253
6993
  StorageService: () => StorageService,
6254
6994
  TestCaseDetailsSchema: () => TestCaseDetailsSchema,
6255
6995
  TestResultStatus: () => TestResultStatus,
@@ -6299,7 +7039,10 @@ function isLocalOnlyTool(toolName) {
6299
7039
  "muggle_run_result_list",
6300
7040
  "muggle_run_result_get",
6301
7041
  "muggle_test_script_list",
6302
- "muggle_test_script_get"
7042
+ "muggle_test_script_get",
7043
+ // Client telemetry — purely local, no backend call
7044
+ "muggle-local-telemetry-skill-emit",
7045
+ "muggle-local-telemetry-event-emit"
6303
7046
  ];
6304
7047
  return localOnlyTools.includes(toolName);
6305
7048
  }
@@ -6313,4 +7056,4 @@ var plugins_exports = {};
6313
7056
  // packages/mcps/src/mcp/agents/index.ts
6314
7057
  var agents_exports = {};
6315
7058
 
6316
- export { DEFAULT_PREFERENCES, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, __export, __require, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports2 as e2e_exports, formatPreferencesOneLiner, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir2 as getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports2 as local_exports, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, readGlobalPreferences, readProjectPreferences, resetConfig, resetLogger, resetPreference, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, validatePreference, verifyFileChecksum, writePreferences };
7059
+ export { DEFAULT_PREFERENCES, EventName, PREFERENCES_FILE_NAME, PREFERENCES_PROJECT_DIR_NAME, PREFERENCES_SCHEMA, PREFERENCES_VERSION, PREFERENCE_ALLOWED_VALUES, PreferenceKey, PreferenceValue, ServiceName, Surface, __export, __require, buildElectronAppChecksumsUrl, buildElectronAppReleaseAssetUrl, buildElectronAppReleaseTag, calculateFileChecksum, createApiKeyWithToken, createChildLogger, deleteApiKeyData, deleteCredentials, e2e_exports2 as e2e_exports, formatPreferencesOneLiner, getApiKey, getApiKeyFilePath, getAuthService, getBundledElectronAppVersion, getCallerCredentials, getCallerCredentialsAsync, getChecksumForPlatform, getConfig, getCredentialsFilePath, getDataDir2 as getDataDir, getDownloadBaseUrl, getElectronAppChecksums, getElectronAppDir, getElectronAppVersion, getElectronAppVersionSource, getLocalQaTools, getLogger, getPlatformKey, getQaTools, getValidApiKeyData, getValidCredentials, hasApiKey, initTelemetry, isElectronAppInstalled, isFirstRun, loadApiKeyData, loadCredentials, local_exports2 as local_exports, mcp_exports, openBrowserUrl, performLogin, performLogout, pollDeviceCode, readGlobalPreferences, readProjectPreferences, resetConfig, resetLogger, resetPreference, resolveElectronAppPathOrNull, resolvePreferences, saveApiKey, saveApiKeyData, saveCredentials, src_exports, startDeviceCodeFlow, toolRequiresAuth, track, validatePreference, verifyFileChecksum, writePreferences };